Servlet 3.0
- servlet3.0-簡介&測驗
- 使用前匯入servlet相關的依賴
- @WebServlet : 注冊servlet ,以及servlet 3.0相關的注解說明,鏈接在下面:
- 同樣,要注冊Filter用@WebFilter注解、注冊Listener用@WebListener注解;如果在注冊的時候,需要一些初始化引數,我們就可以用@WebInitParam注解;
- 小細節回顧: 解決tomcat回應中文亂碼問題,通知瀏覽器使用uft-8編碼來對資料進行解碼
- servlet3.0 ==> ServletContainerInitializer
- 第一步:我們寫一個MyServletContainerInitializer 類實作ServletContainerInitializer 介面
- 第二步:在這個路徑下新建一個檔案
- 注意:從運行結果可以分析出,這里傳遞過來的所有感興趣的型別是指定型別下面所有子類,不包括本身
- servlet3.0-在ServletContext中利用編碼的方式注冊三大組件
- 使用編碼方式注冊三大組件的小總結
- servlet3.0-與SpringMVC整合分析
- 首先匯入springMVC相關的依賴
- 原理
- springmvc-整合
- 1.創建web初始化器
- 2.父子配置類,spring是父配置類,要排除掃描controller注解,而springmvc是子配置類,只負責掃描controller注解
- 3.我們再來寫一個Controller和Service進行測驗
- springmvc-定制與接管SpringMVC
- 注解來定制SpringMVC: @EnableWebMvc
- 通過一個配置類來實作介面:extends WebMvcConfigurerAdapter, 這樣就可以來實作定制配置了
- 但是上面直接實作WebMvcConfigurer介面的方式,有很多的方法用不到,我們可以用這個配接器WebMvcConfigurerAdapter來實作,它實作了WebMvcConfigurer介面:
- 我們可以來定義一個視圖決議器:
- 問題: 我們在這個路徑下放一個圖片,然后再寫一個jsp來訪問它,這個時候,圖片是顯示不出來的:
- 現在,我們來配置允許靜態資源的訪問:
- 注意:如果圖片是在WEB-INF下面的,那么獲取圖片是獲取不到的,因為WEB-INF下面的資源不能直接訪問,jsp頁面可以通過controller跳轉方式實作內部資源訪問,但是圖片嘛.....,目前我知識有限,還不知道如何訪問
- 自定義攔截器需要實作HandlerInterceptor介面:
- 在配置類中注冊攔截器
- servlet3.0-異步請求
- 添加異步處理后:
- springmvc-異步請求-回傳Callable
- springmvc-異步請求-回傳DeferredResult
servlet3.0-簡介&測驗
現在,我們來說說注解版的web,我們以前來寫web的三大組件:Servlet、Filter、Listener,包括SpringMVC的前端控制器DispatcherServlet都需要在web.xml檔案中來進行注冊;而在Servlet3.0標準以后,就給我們提供了方便的注解的方式來完成我們這些組件的注冊以及添加,提供了運行時的可插拔的插件能力;
說明:Servlet3.0及以上的標準是需要Tomcat7及以上的支持;
如果使用全注解開發,那么就可以去掉web.xml組態檔了,轉用編碼方式進行替代
使用前匯入servlet相關的依賴
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.2.1</version>
<scope>provided</scope>
</dependency>
@WebServlet : 注冊servlet ,以及servlet 3.0相關的注解說明,鏈接在下面:
@WebServlet("/hello")//指定當前servlet的請求映射路徑
public class MyServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write("大忽悠");
}
}

同樣,要注冊Filter用@WebFilter注解、注冊Listener用@WebListener注解;如果在注冊的時候,需要一些初始化引數,我們就可以用@WebInitParam注解;
Servlet 3.0 注解 @WebServlet @WebFilter @WebListener
@WebFilter 的使用
@WebServlet的使用方法
小細節回顧: 解決tomcat回應中文亂碼問題,通知瀏覽器使用uft-8編碼來對資料進行解碼
resp.setContentType("text/html;charset=utf-8");
servlet3.0 ==> ServletContainerInitializer
Shared libraries(共享庫) / runtimes pluggability(運行時插件能力)
1、Servlet容器啟動會掃描,當前應用里面每一個jar包的ServletContainerInitializer的實作
2、提供ServletContainerInitializer的實作類;
必須系結在,META-INF/services/javax.servlet.ServletContainerInitializer
檔案的內容就是ServletContainerInitializer實作類的全類名;
總結:容器在啟動應用的時候,會掃描當前應用每一個jar包里面
META-INF/services/javax.servlet.ServletContainerInitializer
指定的實作類,啟動并運行這個實作類的方法;傳入感興趣的型別;
ServletContainerInitializer;
@HandlesTypes;
第一步:我們寫一個MyServletContainerInitializer 類實作ServletContainerInitializer 介面
//容器啟動的時候會將@HandlesTypes指定的這個型別下面的子類(實作類或者子介面等)傳遞過來
//傳入感興趣的型別
@HandlesTypes(value = {HelloService.class})
public class MyServletContainerInitializer implements ServletContainerInitializer {
/**
* 在應用啟動的時候,會運行onStartup方法;
* Set<Class<?>> :感興趣的型別的所有子型別;
* ServletContext 代表當前的web應用的ServletContext物件,一個web應用相當于是一個ServletContext
* @throws ServletException
*/
@Override
public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
System.out.println("感興趣的型別:");
set.forEach(System.out::println);
}
}
第二步:在這個路徑下新建一個檔案

檔案的內容就寫我們實作ServletContainerInitializer 這個介面的類MyServletContainerInitializer 的全類名:


最后,我們運行起來,運行結果為:
感興趣的型別:
class com.ldc.service.HelloServiceExt
class com.ldc.service.AbstractHelloService
class com.ldc.service.HelloServiceImpl
注意:從運行結果可以分析出,這里傳遞過來的所有感興趣的型別是指定型別下面所有子類,不包括本身
servlet3.0-在ServletContext中利用編碼的方式注冊三大組件
首先我們寫一個Servlet:
public class MyServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write("大忽悠");
}
}
再來一個Filter:
public class UserFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//過濾請求
System.out.println("放行");
//放行
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
最后,再寫一個Listener:
/**
* 監聽專案的啟動和停止
*/
public class UserListener implements ServletContextListener {
//監聽ServletContextEvent的啟動初始化
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("專案啟動");
}
//監聽ServletContextEvent銷毀
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("專案停止");
}
}
最后,我們來用ServletContext來進行注冊三大組件:
//容器啟動的時候會將@HandlesTypes指定的這個型別下面的子類(實作類或者子介面等)傳遞過來
//傳入感興趣的型別
@HandlesTypes(value = {HelloService.class})
public class MyServletContainerInitializer implements ServletContainerInitializer {
/**
* 在應用啟動的時候,會運行onStartup方法;
* Set<Class<?>> :感興趣的型別的所有子型別;
* ServletContext 代表當前的web應用的ServletContext物件,一個web應用相當于是一個ServletContext
* 1)、使用ServletContext注冊Web組件(Servlet、Filter、Listener)
* 2)、使用編碼的方式,在專案啟動的時候給ServletContext添加組件
* 必須在專案啟動的時候來添加
* (1)ServletContainerInitializer得到ServletContext物件來注冊;
* (2)ServletContextListener的方法的引數里面的ServletContextEvent物件可以獲取ServletContext物件
*/
@Override
public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
System.out.println("感興趣的型別:");
set.forEach(System.out::println);
//注冊組件
ServletRegistration.Dynamic servlet = servletContext.addServlet("userServlet", new MyServlet());
//配置servlet的映射資訊
servlet.addMapping("/hello");
//注冊Listener
servletContext.addListener(UserListener.class);
//注冊Filter
FilterRegistration.Dynamic filter = servletContext.addFilter("userFilter", UserFilter.class);
//攔截什么方式的請求,和要攔截的請求路徑
filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST),true,"/*");
}
}
我們啟動,然后再瀏覽器上進行訪問:


使用編碼方式注冊三大組件的小總結
1.使用ServletContext注冊Web組件(Servlet、Filter、Listener)
2.使用編碼的方式,在專案啟動的時候給ServletContext添加組件, 必須在專案啟動的時候來添加
2.1 ServletContainerInitializer得到ServletContext物件來注冊;
2.2 ServletContextListener的方法的引數里面的ServletContextEvent物件可以獲取ServletContext物件,然后在專案啟動的時候進行注冊,即利用監聽器進行組件注冊

servlet3.0-與SpringMVC整合分析
首先匯入springMVC相關的依賴
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.11.RELEASE</version>
</dependency>
原理
1、web容器在啟動的時候,會掃描每個jar包下的META-INF/services/javax.servlet.ServletContainerInitializer
2、加載這個檔案指定的類SpringServletContainerInitializer
3、spring的應用一啟動會加載感興趣的WebApplicationInitializer介面的下的所有組件;
4、并且為WebApplicationInitializer組件創建物件(組件不是介面,不是抽象類)
1)、AbstractContextLoaderInitializer:創建根容器;createRootApplicationContext();
2)、AbstractDispatcherServletInitializer:
創建一個web的ioc容器;createServletApplicationContext();
創建了DispatcherServlet;createDispatcherServlet();
將創建的DispatcherServlet添加到ServletContext中;
getServletMappings();
3)、AbstractAnnotationConfigDispatcherServletInitializer:注解方式配置的DispatcherServlet初始化器
創建根容器:createRootApplicationContext()
getRootConfigClasses();傳入一個配置類
創建web的ioc容器: createServletApplicationContext();
獲取配置類;getServletConfigClasses();
總結:
以注解方式來啟動SpringMVC;繼承AbstractAnnotationConfigDispatcherServletInitializer;
實作抽象方法指定DispatcherServlet的配置資訊;

下面我們找到了SpringServletContainerInitializer 繼承ServletContainerInitializer 的實作類原始碼如下:
@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {
public SpringServletContainerInitializer() {
}
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList();
Iterator var4;
if (webAppInitializerClasses != null) {
var4 = webAppInitializerClasses.iterator();
while(var4.hasNext()) {
Class<?> waiClass = (Class)var4.next();
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer)waiClass.newInstance());
} catch (Throwable var7) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
} else {
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
var4 = initializers.iterator();
while(var4.hasNext()) {
WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();
initializer.onStartup(servletContext);
}
}
}
}
springmvc-整合
1.創建web初始化器
//Web容器啟動的時候創建物件;呼叫方法來初始化容器以及前端控制器
public class MyWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
//獲取父容器的配置類:(Spring的組態檔) --->作為父容器
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{RootConfig.class};
}
//獲取web容器的配置類(SpringMVC組態檔) --->作為一個子容器
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{AppConfig.class};
}
//獲取DispatcherServlet的映射資訊
// /:攔截所有請求(包括靜態資源(xx.js,xx.png),但是不包括*.jsp)
// /*:攔截所有請求,連*.jsp頁面都攔截;jsp頁面是tomcat引擎決議的
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
2.父子配置類,spring是父配置類,要排除掃描controller注解,而springmvc是子配置類,只負責掃描controller注解
spring是父配置類:
//Spring的容器不掃描Controller,父容器
@ComponentScan(value = {"com.ldc."},excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
})
public class RootConfig
{}
springmvc是子配置類:
//SpringMVC只掃描Controller,子容器
//useDefaultFilters = false 禁用默認的過濾規則
@ComponentScan(value = {"com.ldc"},includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
},useDefaultFilters = false)
public class AppConfig {
}
3.我們再來寫一個Controller和Service進行測驗
@Controller
public class HelloController {
@Autowired
private HelloService helloService;
@ResponseBody
@RequestMapping("hello")
public String hello() {
String hello = helloService.sayHello("tomcat");
return hello;
}
}
@Service
public class HelloService {
public String sayHello(String name) {
return "Hello," + name;
}
}

springmvc-定制與接管SpringMVC
以前進行的xml配置:
將springmvc處理不了的請求交給tomcat的模板引擎決議,這樣靜態資源就可以訪問
<mvc:default-servlet-handler/>
springmvc高級功能開啟
<mvc:annotation-driven/>
配置攔截器
<mvc:interceptors><mvc:interceptors/>
配置視圖映射(發送的請求不想通過controller,只想直接地跳轉到目標頁面)
<mvc:view-controller path="/hello" view-name="hello"></mvc:view-controller>
...
注解來定制SpringMVC: @EnableWebMvc
定制SpringMVC;
1)、@EnableWebMvc:開啟SpringMVC定制配置功能;
<mvc:annotation-driven/>;
2)、配置組件(視圖決議器、視圖映射、靜態資源映射、攔截器,,,)
通過一個配置類來實作介面:extends WebMvcConfigurerAdapter
這樣就可以來實作定制配置了
通過一個配置類來實作介面:extends WebMvcConfigurerAdapter, 這樣就可以來實作定制配置了
我們就可以這樣來寫:
//SpringMVC只掃描Controller,子容器
//useDefaultFilters = false 禁用默認的過濾規則
@ComponentScan(value = {"com.ldc"},includeFilters = {
@Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
},useDefaultFilters = false)
@EnableWebMvc
public class AppConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer pathMatchConfigurer) {
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer contentNegotiationConfigurer) {
}
@Override
public void configureAsyncSupport(AsyncSupportConfigurer asyncSupportConfigurer) {
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer defaultServletHandlerConfigurer) {
//將SpringMVC處理不了的請求交給tomcat,專門針對于靜態資源的,這個時候,靜態資源就是可以訪問的
defaultServletHandlerConfigurer.enable();
}
@Override
public void addFormatters(FormatterRegistry formatterRegistry) {
//添加自定義的型別轉換器
}
@Override
public void addInterceptors(InterceptorRegistry interceptorRegistry) {
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry resourceHandlerRegistry) {
}
@Override
public void addCorsMappings(CorsRegistry corsRegistry) {
}
@Override
public void addViewControllers(ViewControllerRegistry viewControllerRegistry) {
}
@Override
public void configureViewResolvers(ViewResolverRegistry viewResolverRegistry) {
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> list) {
}
@Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> list) {
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> list) {
}
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> list) {
}
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> list) {
}
@Override
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> list) {
}
@Override
public Validator getValidator() {
return null;
}
@Override
public MessageCodesResolver getMessageCodesResolver() {
return null;
}
}
但是上面直接實作WebMvcConfigurer介面的方式,有很多的方法用不到,我們可以用這個配接器WebMvcConfigurerAdapter來實作,它實作了WebMvcConfigurer介面:
我們可以來定義一個視圖決議器:
//SpringMVC只掃描Controller,子容器
//useDefaultFilters = false 禁用默認的過濾規則
@ComponentScan(value = {"com.ldc"},includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
},useDefaultFilters = false)
@EnableWebMvc
public class AppConfig extends WebMvcConfigurerAdapter {
//定制
//視圖決議器
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
//默認所有頁面都是從/WEB-INF/xxx.jsp
/*
* public UrlBasedViewResolverRegistration jsp() {
return this.jsp("/WEB-INF/", ".jsp");
}
* */
//registry.jsp();
//我們也可以自己來寫規則
registry.jsp("/WEB-INF/views/", ".jsp");
}
}
我們在/WEB-INF/views/這個路徑下新建一個success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>success</h1>
</body>
</html>


問題: 我們在這個路徑下放一個圖片,然后再寫一個jsp來訪問它,這個時候,圖片是顯示不出來的:


現在,我們來配置允許靜態資源的訪問:
//SpringMVC只掃描Controller,子容器
//useDefaultFilters = false 禁用默認的過濾規則
@ComponentScan(value = {"com.ldc"},includeFilters = {
@Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
},useDefaultFilters = false)
@EnableWebMvc
public class AppConfig extends WebMvcConfigurerAdapter {
//定制
//視圖決議器
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
//默認所有頁面都是從/WEB-INF/xxx.jsp
//registry.jsp();
//我們也可以自己來寫規則
registry.jsp("/WEB-INF/views/", ".jsp");
}
//靜態資源的訪問
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
這個時候,我們的圖片就是可以訪問了:

注意:如果圖片是在WEB-INF下面的,那么獲取圖片是獲取不到的,因為WEB-INF下面的資源不能直接訪問,jsp頁面可以通過controller跳轉方式實作內部資源訪問,但是圖片嘛…,目前我知識有限,還不知道如何訪問
自定義攔截器需要實作HandlerInterceptor介面:
public class MyFirstInterceptor implements HandlerInterceptor {
//在目標方法執行之前執行
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("preHandle...");
return true;
}
//在目標方法執行之后執行
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle...");
}
//頁面回應以后執行
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System.out.println("afterCompletion...");
}
}
在配置類中注冊攔截器
//SpringMVC只掃描Controller,子容器
//useDefaultFilters = false 禁用默認的過濾規則
@ComponentScan(value = {"com.ldc"},includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
},useDefaultFilters = false)
@EnableWebMvc
public class AppConfig extends WebMvcConfigurerAdapter {
//定制
//視圖決議器
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
//默認所有頁面都是從/WEB-INF/xxx.jsp
/*
* public UrlBasedViewResolverRegistration jsp() {
return this.jsp("/WEB-INF/", ".jsp");
}
* */
//registry.jsp();
//我們也可以自己來寫規則
registry.jsp("/WEB-INF/views/", ".jsp");
}
//靜態資源的訪問
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
//配置攔截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
//攔截任意多層路徑的任意請求
registry.addInterceptor(new MyFirstInterceptor()).addPathPatterns("/**");
}
}
我們重新啟動,并且訪問這個路徑:

我們發現控制臺已經列印了,說明攔截器已經起作用了:
preHandle…
postHandle…
afterCompletion…
servlet3.0-異步請求
servlet3.0異步處理:
在Servlet 3.0之前,Servlet采用Thread-Per-Request的方式處理請求,
即每一次Http請求都由某一個執行緒從頭到尾負責處理,

如果一個請求需要進行IO操作,比如訪問資料庫、呼叫第三方服務介面等,那么其所對應的執行緒將同步地等待IO操作完成, 而IO操作是非常慢的,所以此時的執行緒并不能及時地釋放回執行緒池以供后續使用,在并發量越來越大的情況下,這將帶來嚴重的性能問題,即便是像Spring、Struts這樣的高層框架也脫離不了這樣的桎梏,因為他們都是建立在Servlet之上的,為了解決這樣的問題,Servlet 3.0引入了異步處理,然后在Servlet 3.1中又引入了非阻塞IO來進一步增強異步處理的性能,
我們可以在之前寫的Servlet加上當前的執行緒:
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(Thread.currentThread()+" start...");
try {
sayHello();
} catch (InterruptedException e) {
e.printStackTrace();
}
resp.getWriter().write("hello...");
System.out.println(Thread.currentThread()+" end...");
}
private void sayHello() throws InterruptedException {
System.out.println(Thread.currentThread()+ " processing...");
Thread.sleep(3000);
}
}
啟動服務之后,控制臺列印結果為:我們可以發現從執行緒開始、處理請求到執行結束從始至終都是Thread[http-nio-8081-exec-3,5,main]這個執行緒,主執行緒得不到釋放,當下一個請求進來就得不到處理;
UserFilter…doFilter…
Thread[http-nio-8081-exec-3,5,main] start…
Thread[http-nio-8081-exec-3,5,main] processing…
Thread[http-nio-8081-exec-3,5,main] end…
添加異步處理后:
我們再來加上是哪些執行緒處理的:
@WebServlet(value = "/async",asyncSupported = true)
public class HelloAsyncServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.支持異步處理:asyncSupported = true
//2.開啟異步模式
System.out.println("主執行緒開始..."+Thread.currentThread()+"==>"+ Instant.now().toEpochMilli());
AsyncContext startAsync = req.startAsync();
//3.業務邏輯進行異步處理,開始異步處理
startAsync.start(()-> {
try {
System.out.println("副執行緒開始..."+Thread.currentThread()+"==>"+ Instant.now().toEpochMilli());
sayHello();
startAsync.complete();
//獲取異步背景關系
//AsyncContext asyncContext = req.getAsyncContext();
//4.獲取回應
ServletResponse response = startAsync.getResponse();
response.getWriter().write("hello async...");
System.out.println("副執行緒結束..."+Thread.currentThread()+"==>"+ Instant.now().toEpochMilli());
} catch (Exception e) {
e.printStackTrace();
}
});
System.out.println("主執行緒結束..."+Thread.currentThread()+"==>"+ Instant.now().toEpochMilli());
}
private void sayHello() throws InterruptedException {
System.out.println(Thread.currentThread()+ " processing..."+"==>"+ Instant.now().toEpochMilli());
Thread.sleep(3000);
}
}
執行結果如下:
感興趣的型別:
class com.ldc.service.HelloServiceImpl
class com.ldc.service.HelloServiceExt
class com.ldc.service.AbstractHelloService
UserListener…contextInitialized
[2019-01-18 04:47:06,991] Artifact servlet3.0:war exploded: Artifact is deployed successfully
[2019-01-18 04:47:06,992] Artifact servlet3.0:war exploded: Deploy took 447 milliseconds
UserFilter…doFilter…
UserFilter…doFilter…
UserFilter…doFilter…
主執行緒開始…Thread[http-nio-8081-exec-7,5,main]>1547801232248
主執行緒結束…Thread[http-nio-8081-exec-7,5,main]>1547801232253
副執行緒開始…Thread[http-nio-8081-exec-8,5,main]>1547801232253
Thread[http-nio-8081-exec-8,5,main] processing…>1547801232253
副執行緒結束…Thread[http-nio-8081-exec-8,5,main]==>1547801235253
現在的處理就是可以表示成下圖:

springmvc-異步請求-回傳Callable
@Controller
public class AsyncController {
/**
* 1、控制器回傳Callable
* 2、Spring異步處理,將Callable 提交到 TaskExecutor 使用一個隔離的執行緒進行執行
* 3、DispatcherServlet和所有的Filter退出web容器的執行緒,但是response 保持打開狀態;
* 4、Callable回傳結果,SpringMVC將請求重新派發給容器,恢復之前的處理;
* 5、根據Callable回傳的結果,SpringMVC繼續進行視圖渲染流程等(從收請求-視圖渲染),
*
* preHandle.../springmvc-annotation/async01
主執行緒開始...Thread[http-bio-8081-exec-3,5,main]==>1513932494700
主執行緒結束...Thread[http-bio-8081-exec-3,5,main]==>1513932494700
=========DispatcherServlet及所有的Filter退出執行緒============================
================等待Callable執行==========
副執行緒開始...Thread[MvcAsync1,5,main]==>1513932494707
副執行緒開始...Thread[MvcAsync1,5,main]==>1513932496708
================Callable執行完成==========
================再次收到之前重發過來的請求========
preHandle.../springmvc-annotation/async01
postHandle...(Callable的之前的回傳值就是目標方法的回傳值)
afterCompletion...
異步的攔截器:
1)、原生API的AsyncListener
2)、SpringMVC:實作AsyncHandlerInterceptor;
* @return
*/
@ResponseBody
@RequestMapping("/async01")
public Callable<String> async01() {
System.out.println("主執行緒開始..." + Thread.currentThread() + "==>" + Instant.now().getEpochSecond());
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("副執行緒開始..." + Thread.currentThread() + "==>" + Instant.now().getEpochSecond());
Thread.sleep(2000);
System.out.println("副執行緒結束..." + Thread.currentThread() + "==>" + Instant.now().getEpochSecond());
return "Callable<String> async01()";
}
};
System.out.println("主執行緒結束..." + Thread.currentThread() + "==>" + Instant.now().getEpochSecond());
return callable;
}
}
此時運行起來的測驗結果如下:
preHandle…
主執行緒開始…Thread[http-nio-8081-exec-7,5,main]>1547802269
主執行緒結束…Thread[http-nio-8081-exec-7,5,main]>1547802269
副執行緒開始…Thread[MvcAsync1,5,main]>1547802269
副執行緒結束…Thread[MvcAsync1,5,main]>1547802271
Callable回傳結果,SpringMVC將請求重新派發給容器,恢復之前的處理;
preHandle…
postHandle…
afterCompletion…
springmvc-異步請求-回傳DeferredResult
我們來看一個實際的應用場景:


public class DeferredResultQueue {
private static Queue<DeferredResult<Object>> queue = new ConcurrentLinkedDeque<>();
public static void save(DeferredResult<Object> deferredResult) {
queue.add(deferredResult);
}
public static DeferredResult<Object> get() {
return queue.poll();
}
}
@Controller
public class AsyncController {
@ResponseBody
@RequestMapping("/createOrder")
public DeferredResult<Object> createOrder()
{
//設定當前請必須在三秒內執行完,否則顯示錯誤訊息
DeferredResult<Object> deferredResult = new DeferredResult<>((long)3000, "create fail...");
DeferredResultQueue.save(deferredResult);
return deferredResult;
}
@ResponseBody
@RequestMapping("/create")
public String create(){
//創建訂單
String order = UUID.randomUUID().toString();
DeferredResult<Object> deferredResult = DeferredResultQueue.get();
//只要這邊設定了處理結果,對應的那邊就可以得到回傳
deferredResult.setResult(order);
return "success===>"+order;
}
}
我們先訪問這個創建訂單createOrder介面:

我們再來訪問這個create介面,此時的結果如圖所示:

在來看createOrder的回傳結果:

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/292932.html
標籤:java
上一篇:Python入門實戰系列文章
