spring注解驱动开发(三):servlet3.0
1.servlet3.0
servlet3.0查看官网: 属于jsr规范
查看tomcat支持servlet的版本 : 发现只有tomcat7以上才能支持servlet3.0
servlet示例:
支持WebServlet,Webfilter,WebListener注解,示例如下:
package com.atguigu.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/hello") public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO Auto-generated method stub //super.doGet(req, resp); System.out.println(Thread.currentThread()+" start..."); try { sayHello(); } catch (Exception e) { e.printStackTrace(); } resp.getWriter().write("hello..."); System.out.println(Thread.currentThread()+" end..."); } public void sayHello() throws Exception{ System.out.println(Thread.currentThread()+" processing..."); Thread.sleep(3000); } }
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;
示例:
1): 在目录(没有则创建此文件)META-INF/services/javax.servlet.ServletContainerInitializer文件添加启动类:
2):创建并实现这个类MyServletContainerInitializer,如下:
备注: @HandlesTypes(value={HelloService.class})这个注解会将接口HelloService的所有子接口包括抽象类及实现类加载进来
package com.atguigu.servlet; import java.util.EnumSet; import java.util.Set; import javax.servlet.DispatcherType; import javax.servlet.FilterRegistration; import javax.servlet.ServletContainerInitializer; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRegistration; import javax.servlet.annotation.HandlesTypes; import com.atguigu.service.HelloService; //容器启动的时候会将@HandlesTypes指定的这个类型下面的子类(实现类,子接口等)传递过来; //传入感兴趣的类型; @HandlesTypes(value={HelloService.class}) public class MyServletContainerInitializer implements ServletContainerInitializer { /** * 应用启动的时候,会运行onStartup方法; * * Set<Class<?>> arg0:感兴趣的类型的所有子类型; * ServletContext arg1:代表当前Web应用的ServletContext;一个Web应用一个ServletContext; * * 1)、使用ServletContext注册Web组件(Servlet、Filter、Listener) * 2)、使用编码的方式,在项目启动的时候给ServletContext里面添加组件; * 必须在项目启动的时候来添加; * 1)、ServletContainerInitializer得到的ServletContext; * 2)、ServletContextListener得到的ServletContext; */ @Override public void onStartup(Set<Class<?>> arg0, ServletContext sc) throws ServletException { // TODO Auto-generated method stub System.out.println("感兴趣的类型:"); for (Class<?> claz : arg0) { System.out.println(claz); } //注册组件 ServletRegistration ServletRegistration.Dynamic servlet = sc.addServlet("userServlet", new UserServlet()); //配置servlet的映射信息 servlet.addMapping("/user"); //注册Listener sc.addListener(UserListener.class); //注册Filter FilterRegistration FilterRegistration.Dynamic filter = sc.addFilter("userFilter", UserFilter.class); //配置Filter的映射信息 filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*"); } }
以上代码注册的servlet,filter,listener三个组件的实现类如下:
servlet:
package com.atguigu.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class UserServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO Auto-generated method stub resp.getWriter().write("tomcat..."); } }
filter:
package com.atguigu.servlet; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class UserFilter implements Filter { @Override public void destroy() { // TODO Auto-generated method stub } @Override public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException { // 过滤请求 System.out.println("UserFilter...doFilter..."); //放行 arg2.doFilter(arg0, arg1); } @Override public void init(FilterConfig arg0) throws ServletException { // TODO Auto-generated method stub } }
listener:
package com.atguigu.servlet; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; /** * 监听项目的启动和停止 * @author lfy * */ public class UserListener implements ServletContextListener { //监听ServletContext销毁 @Override public void contextDestroyed(ServletContextEvent arg0) { // TODO Auto-generated method stub System.out.println("UserListener...contextDestroyed..."); } //监听ServletContext启动初始化 @Override public void contextInitialized(ServletContextEvent arg0) { // TODO Auto-generated method stub ServletContext servletContext = arg0.getServletContext(); System.out.println("UserListener...contextInitialized..."); } }
启动tomcat的结果如下:
servlet3.0与springmvc整合
通过上面的web原理可知:在spring-web包下web-info下同样存在一个启动类,如下截图:
通过查看这个类上,加载进入的类是webApplicationInitializer这个类的子类:
webApplicationInitializer接口有三个子类:
分析过程如下:
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的配置信息;
以注解的方式配置springmvc也可以如果使用配置的方式,在springmvc的官方文档中也可以查到通过继承AbstractAnnotationConfigDispatcherServletInitializer,如下截图:
spring注解方式整合
实现的最终效果是父子ioc容器,图如下:
1)实现AbstractAnnotationConfigDispatcherServletInitializer方式如下:
package com.atguigu; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; import com.atguigu.config.AppConfig; import com.atguigu.config.RootConfig; //web容器启动的时候创建对象;调用方法来初始化容器以前前端控制器 public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { //获取根容器的配置类;(Spring的配置文件) 父容器; @Override protected Class<?>[] getRootConfigClasses() { // TODO Auto-generated method stub return new Class<?>[]{RootConfig.class}; } //获取web容器的配置类(SpringMVC配置文件) 子容器; @Override protected Class<?>[] getServletConfigClasses() { // TODO Auto-generated method stub return new Class<?>[]{AppConfig.class}; } //获取DispatcherServlet的映射信息 // /:拦截所有请求(包括静态资源(xx.js,xx.png)),但是不包括*.jsp; // /*:拦截所有请求;连*.jsp页面都拦截;jsp页面是tomcat的jsp引擎解析的; @Override protected String[] getServletMappings() { // TODO Auto-generated method stub return new String[]{"/"}; } }
2)扫描springmvc的ioc容器配置文件如下:
package com.atguigu.config; import java.util.List;import com.atguigu.controller.MyFirstInterceptor; //SpringMVC只扫描Controller;子容器 //useDefaultFilters=false 禁用默认的过滤规则; @ComponentScan(value="com.atguigu",includeFilters={ @Filter(type=FilterType.ANNOTATION,classes={Controller.class}) },useDefaultFilters=false) @EnableWebMvc public class AppConfig extends WebMvcConfigurerAdapter { }
3)定制SpringMVC
参照springmvc的官方文档:
1)、@EnableWebMvc:开启SpringMVC定制配置功能;<mvc:annotation-driven/>;
2)、配置组件(视图解析器、视图映射、静态资源映射、拦截器。。。)
将步骤2)的类: AppConfig extends WebMvcConfigurerAdapter
package com.atguigu.config; import java.util.List; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.FilterType; import org.springframework.format.FormatterRegistry; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.stereotype.Controller; import org.springframework.validation.MessageCodesResolver; import org.springframework.validation.Validator; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer; import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.ViewResolverRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import com.atguigu.controller.MyFirstInterceptor; //SpringMVC只扫描Controller;子容器 //useDefaultFilters=false 禁用默认的过滤规则; @ComponentScan(value="com.atguigu",includeFilters={ @Filter(type=FilterType.ANNOTATION,classes={Controller.class}) },useDefaultFilters=false) @EnableWebMvc public class AppConfig extends WebMvcConfigurerAdapter { //定制 //视图解析器 @Override public void configureViewResolvers(ViewResolverRegistry registry) { // TODO Auto-generated method stub //默认所有的页面都从 /WEB-INF/ xxx .jsp //registry.jsp(); registry.jsp("/WEB-INF/views/", ".jsp"); } //静态资源访问 @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { // TODO Auto-generated method stub configurer.enable(); } //拦截器 @Override public void addInterceptors(InterceptorRegistry registry) { // TODO Auto-generated method stub //super.addInterceptors(registry); registry.addInterceptor(new MyFirstInterceptor()).addPathPatterns("/**"); } }
3.servlet3.0异步和spring的异步
servlet3.0异步:
@WebServlet(value="/async",asyncSupported=true)
package com.atguigu.servlet; import java.io.IOException; import javax.servlet.AsyncContext; import javax.servlet.ServletException; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @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()+"==>"+System.currentTimeMillis()); AsyncContext startAsync = req.startAsync(); //3、业务逻辑进行异步处理;开始异步处理 startAsync.start(new Runnable() { @Override public void run() { try { System.out.println("副线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis()); sayHello(); startAsync.complete(); //获取到异步上下文 AsyncContext asyncContext = req.getAsyncContext(); //4、获取响应 ServletResponse response = asyncContext.getResponse(); response.getWriter().write("hello async..."); System.out.println("副线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis()); } catch (Exception e) { } } }); System.out.println("主线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis()); } public void sayHello() throws Exception{ System.out.println(Thread.currentThread()+" processing..."); Thread.sleep(3000); } }
执行结果如下:
备注: 如上截图可以看出servlet3.0的异步和servlet用的是同一个线程池
springmvc的异步
源码如下:
package com.atguigu.controller; import java.util.UUID; import java.util.concurrent.Callable; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.context.request.async.DeferredResult; import com.atguigu.service.DeferredResultQueue; @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; } /** * 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()+"==>"+System.currentTimeMillis()); Callable<String> callable = new Callable<String>() { @Override public String call() throws Exception { System.out.println("副线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis()); Thread.sleep(2000); System.out.println("副线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis()); return "Callable<String> async01()"; } }; System.out.println("主线程结束..."+Thread.currentThread()+"==>"+System.currentTimeMillis()); return callable; } }
执行结果如下:
发现拦截器的preHandle执行了两次.... 解释如源码中
posted on 2019-06-30 22:12 zhulibin2012 阅读(317) 评论(0) 编辑 收藏 举报