zoukankan      html  css  js  c++  java
  • 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执行了两次.... 解释如源码中


  • 相关阅读:
    java线程
    windows Server 安装
    nginx正则反向代理
    crontab定时任务启动脚本失败
    数据结构
    异常概念
    shell日期遍历复制文件
    多态和抽象
    图1 列出连通集
    PTA 代码注意事项
  • 原文地址:https://www.cnblogs.com/zhulibin2012/p/11111485.html
Copyright © 2011-2022 走看看