zoukankan      html  css  js  c++  java
  • [心得体会]SpringMVC源码分析

    1. SpringMVC

    (1) springmvc 是什么?

    前端控制器, 主要控制前端请求分配请求任务到service层获取数据后反馈到springmvc的view层进行包装返回给tomcat, 说白了就类似于请求转发和视图包装的处理器

    img

    对上图说明①:浏览器发送请求到控制器(这里要知道控制器的作用)②:控制器不能处理请求必须交给模型层来处理接着去访问数据库③:模型层将处理好的结果返回给控制层④:控制层将逻辑视图响应给浏览器(浏览器显示的是渲染过的视图)

    img

    对工作原理解释说明:

    1,用户发送请求到springmvc框架提供的DispatcherServlet 这个前端控制器(了解struts2的朋友也都知道其实struts2也有一个前端控制器web.xml中的filter标签就是)

    2,前端控制器会去找处理器映射器(HandlerMapping),处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet 。

    3,根据处理器映射器返回的处理器,DispatcherServlet 会找“合适”的处理器适配器(HandlerAdapter)

    4,处理器适配器HandlerAdpater会去执行处理器(Handler开发的时候会被叫成controller 也叫后端控制器在struts2中action也是一个后端控制器)执行之前会有转换器、数据绑定、校验器等等完成上面这些才会去正在执行Handler

    5,后端控制器Handler执行完成之后返回一个ModelAndView对象

    6,处理器适配器HandlerAdpater会将这个ModelAndView返回前端控制器DispatcherServlet。前端控制器会将ModelAndView对象交给视图解析器ViewResolver。

    7,视图解析器ViewResolver解析ModelAndView对象之后返回逻辑视图。

    8,前端控制器DispatcherServlet对逻辑视图进行渲染(数据填充)之后返回真正的物理View并响应给浏览器。

    对组件说明:

    1,DispatherServlet:前端控制器用户请求到达前端控制器,相当于MVC中的C,而DispatherServlet是整个流程的核心,它来调用其他组件来处理用户的请求,前端控制器的存在降低了其他组件之间的耦合度。

    2,HandlerMapping:处理器映射器它的作用就好比去看电影要拿着电影票根据电影票上面的座位号找到座位其中座位就是Handler,电影票以及上面的座位号就是URLHandlerMapping 负责根据用户请求找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

    3,Handler:处理器Handler是后端控制器,在前端控制器的控制下后端控制器对具体的用户请求进行处理,Handler涉及到具体的用户业务请求,所以一般情况下需要程序员根据业务需求开发.

    4,HandlerAdapter:处理器适配器通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过适配器可以对更多类型的处理器进行执行。播放的电影是3D的你看不清楚,因此电影院跟你说你要想看清电影就必须戴3D眼镜。也就是说Handler满足一定的要求才可以被执行。

    5,ViewResolver:视图解析器ViewResolver负责将处理结果生成View视图,ViewResolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。

    作者:HansonQ链接:http://www.imooc.com/article/3804来源:慕课网

    上面是原链接

    springmvc核心架构

    img

    核心架构的具体流程步骤如下:

    1、 首先用户发送请求——>DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制;

    2、 DispatcherServlet——>HandlerMapping, HandlerMapping将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器)对象,通过这种策略模式,很容易添加新的映射策略;

    3、 DispatcherServlet——>HandlerAdapter,HandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;

    4、 HandlerAdapter——>处理器功能处理方法的调用,HandlerAdapter将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView对象(包含模型数据、逻辑视图名);

    5、 ModelAndView的逻辑视图名——> ViewResolver, ViewResolver将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术;

    6、 View——>渲染,View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构,因此很容易支持其他视图技术;

    7、返回控制权给DispatcherServlet,由DispatcherServlet返回响应给用户,到此一个流程结束。

    img

    工作流程步骤如下:

    1. 用户发送请求到前端控制器(DispatcherServlet)
    2. 前端控制器请求HandlerMapping查找 Handler 【可以根据xml配置、注解进行查找】
    3. 处理器映射器HandlerMapping向前端控制器返回Handler
    4. 前端控制器请求处理器适配器去执行Handler
    5. 处理器适配器去执行Handler
    6. 处理器适配器执行完成后,Controller返回ModelAndView
    7. 处理器适配器向前端控制器返回ModelAndView,ModelAndView是springmvc框架的一个底层对象,包括Model和view
    8. 前端控制器请求视图解析器去进行视图解析【根据逻辑视图名解析成真正的视图(jsp)】
    9. 视图解析器向前端控制器返回View
    10. 前端控制器进行视图渲染【视图渲染即. 模型数据(在ModelAndView对象中)填充到request域】
    11. 前端控制器向用户响应结果、

    DispatcherServlet工作原理

    img

    1. 当DispatcherServlet接到请求时,他先回查找适当的处理程序来处理请求。DispatcherServlet通过一个或者多个处理程序映射,将每个请求映射到处理程序中。处理程序映射配置在web应用程序的上下文中,是实现了HandlerMapping接口的Bean。它负责为请求返回一个适当的处理程序(也就是Controller)。处理程序映射通常根据请求的URL将请求映射到处理程序(Controller)。
    2. 一旦DispatcherServlet选择了适当的控制器,它就会调用这个控制器来处理请求。
    3. 控制器处理完请求后,会将模型和视图名(有时候是视图对象)返回给DispatcherServlet。模型包含了控制器要传递给视图进行显示的属性。如果返回的是视图名称,它会被解析成视图对象再进行呈现。绑定模型和视图的基本类是ModelAndView
    4. 当DispatcherServlet接收到模型和视图名称时,它会将逻辑视图名称解析成视图对象再进行呈现。DispatcherServlet从一个或者多个视图解析器中解析视图。视图解析器配置在Web应用程序上下文中,是实现了ViewResolver接口的Bean。它的任务是根据逻辑视图名称返回试图对象。
    5. 一旦DispatcherServlet将视图名称解析称为试图对象,它就会呈现视图对象,并传递控制器返回的模型。视图的任务是将模型属性展示给用户。

    Spring MVC工作原理总结

    (1 )启动服务器,根据web.xml 的配置加载前端控制器(也称总控制器) DispatcherServlet 。加载(包括加载springmvc-servlet.xml)时会完成一系列的初始化动作。

    (2 )根据servlet 的映射请求(上面的helloWorld 实例中针对.do 请求),并参照“控制器配置文件(即springmvc-servlet.xml 这样的配置文件),把具体的请求分发给特定的后端控制器进行处理(比如上例会分发给HelloWorld 控制器进行处理)

    (3 )后端控制器调用相应的逻辑层代码,完成处理并返回视图对象(ModelAndView )给前端处理器。

    (4 )前端控制器根据后端控制器返回的 ModelAndView 对象,并结合一些配置(后续有说明),返回一个相应的页面给客户端。

    (2) 怎么用? (笔记使用的是注解方式)

    1) tomcat启动springmvc的过程(初始化DispatcherServlet)

    注解方式的使用需要注意从tomcat的启动开始

    这里我们需要关注一个类

    1580217212352

    org.springframework.web.SpringServletContainerInitializer
    @HandlesTypes(WebApplicationInitializer.class)
    public class SpringServletContainerInitializer implements ServletContainerInitializer {
        @Override
        public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
                throws ServletException {
            // ...
        }
    
    }

    上面的那个类就是那个方法注册的, 它最终都会调用onStartup方法

    下面这个类就是上面类的接口

     /**
     * @see javax.servlet.annotation.HandlesTypes
     * @since Servlet 3.0
     */
    public interface ServletContainerInitializer {
        public void onStartup(Set<Class<?>> c, ServletContext ctx)
            throws ServletException; 
    }

    看到上面的 HandlesTypes 注解了

    发现这个注解注释的类将初始化 ServletContainerInitializer 这个类的产生

    看到SpringServletContainerInitializer上面的注解

    @HandlesTypes(WebApplicationInitializer.class)

    就代表则WebApplicationInitializer类初始化了ServletContainerInitializer这个类

    所以现在进入WebApplicationInitializer这个类分析

    下面的代码就是上面用了HandlesTypes注解的类, 这个类的功能主要是用来初始化SpringServletContainerInitializer类内部部分方法而使用的

    /**
     * @see SpringServletContainerInitializer
     * @see org.springframework.web.context.AbstractContextLoaderInitializer
     * @see org.springframework.web.servlet.support.AbstractDispatcherServletInitializer
     * @see org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer
     */
    public interface WebApplicationInitializer {
        void onStartup(ServletContext servletContext) throws ServletException;
    }

    上面就是初始化类的接口

    上面主要的类都贴出来了, 现在看看过程如何

    首先tomcat会调用

    1580456985041

    这个类里面的onStartup方法

    @Override
    public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
        throws ServletException {
        List<WebApplicationInitializer> initializers = new LinkedList<>();
        if (webAppInitializerClasses != null) {
            for (Class<?> waiClass : webAppInitializerClasses) {
                // Be defensive: Some servlet containers provide us with invalid classes,
                // no matter what @HandlesTypes says...
                if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
                    WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                    try {
                        initializers.add((WebApplicationInitializer)
                                         ReflectionUtils.accessibleConstructor(waiClass).newInstance());
                    }
                    catch (Throwable ex) {
                        throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
                    }
                }
            }
        }
    
        if (initializers.isEmpty()) {
            servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
            return;
        }
    
        servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
        AnnotationAwareOrderComparator.sort(initializers);
        for (WebApplicationInitializer initializer : initializers) {
            // 最终都会调用到这个方法, 这个方法需要我们自己实现
            initializer.onStartup(servletContext);
        }
    }

    我们自己实现WebApplicationInitializer这个接口

    因为下面的子类都是抽象类

    1580457201470

    而config就是自己实现的类

    而我们选择这个抽象类实现子类 AbstractDispatcherServletInitializer

    然后最终他会调用这个方法

    public class Config extends AbstractDispatcherServletInitializer {
        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            // 我们主动调用父类的这个方法用于初始化父类需要初始化的出行, 现在分析这个方法
            super.onStartup(servletContext);
            // 这里加入了字符解码过滤器
            CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter("utf-8");
            // 将其加入到springmvc
            FilterRegistration.Dynamic dynamic = servletContext.addFilter("encodingFilter", encodingFilter);
            // 添加这个字符解码过滤器的pattern
            dynamic.addMappingForUrlPatterns(EnumSet.of(DispatcherType.INCLUDE, DispatcherType.REQUEST, DispatcherType.FORWARD), false, "/*");
        }
    }

    进入父类

    public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer {
    
        /**
         * The default servlet name. Can be customized by overriding {@link #getServletName}.
         */
        public static final String DEFAULT_SERVLET_NAME = "dispatcher";
    
    
        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            // 需要分析下面这个方法
            super.onStartup(servletContext);
            // 注册转发器servlet方法
            registerDispatcherServlet(servletContext);
        }
    }

    在进入祖宗类

    public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer {
    
        /** Logger available to subclasses. */
        protected final Log logger = LogFactory.getLog(getClass());
    
    
        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            // 我们现在需要分析这个方法
            registerContextLoaderListener(servletContext);
        }
    }

    现在分析这个方法

    protected void registerContextLoaderListener(ServletContext servletContext) {
        // 这里注入spring根类
        WebApplicationContext rootAppContext = createRootApplicationContext();
        if (rootAppContext != null) {
            // 根据根类生成上下文加载监听器
            ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
            // 这里的方法基本上都是空的, 所以设置了个null根监听器, 其实这个getRootApplicationContextInitializers方法在abstract抽象类中, 我们可以主动实现监听器
            listener.setContextInitializers(getRootApplicationContextInitializers());
            // 将监听器注入到servlet上下文中
            servletContext.addListener(listener);
        }
        else {
            logger.debug("No ContextLoaderListener registered, as " +
                         "createRootApplicationContext() did not return an application context");
        }
    }

    1580457587910

    最终会调用这个方法, 也就是我们自己实现的类中的方法, 这个方法主要的功能就是添加spring的根类

    1580457647260

    这里就不需要分析了, 主要就是注册spring的applicationContext类

    这边祖宗类就初始化完毕了

    下面开始分析父类

    public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer {
        
        public static final String DEFAULT_SERVLET_NAME = "dispatcher";
    
        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            super.onStartup(servletContext);
            registerDispatcherServlet(servletContext);
        }
    }

    public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer {
        // ...
        
        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            super.onStartup(servletContext);
            // 最终会调用下面的这个方法
            registerDispatcherServlet(servletContext);
        }
    
        protected void registerDispatcherServlet(ServletContext servletContext) {
            // dispatcher
            String servletName = getServletName();
            Assert.hasLength(servletName, "getServletName() must not return null or empty");
            // 创建springmvc的应用上下文, 这里的方法需要我们自己实现createServletApplicationContext
            WebApplicationContext servletAppContext = createServletApplicationContext();
            Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");
            // 这个方法的作用主要就是把web应用上下文设置到springmvc的转发器中
            FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
            Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
            // 设置初始化servlet应用上下文初始化器
            dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
            // 向servlet应用上下文设置转发器servlet
    
            ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
            if (registration == null) {
                throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " +
                        "Check if there is another servlet registered under the same name.");
            }
            // 设置servlet被创建的时机
            registration.setLoadOnStartup(1);
            // getServletMappings 这个方法是需要我们自己实现的, 添加servlet被拦截的表达式
            registration.addMapping(getServletMappings());
            // 异步支持
            registration.setAsyncSupported(isAsyncSupported());
            // 获取所有的servlet过滤器
            Filter[] filters = getServletFilters();
            if (!ObjectUtils.isEmpty(filters)) {
                for (Filter filter : filters) {
                    // 注册过滤器
                    registerServletFilter(servletContext, filter);
                }
            }
            // 定制注册器
            customizeRegistration(registration);
        }
    }

    至此父类的分析基本完毕

    现在到当前类的分析了

    public class Config extends AbstractDispatcherServletInitializer {
        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            super.onStartup(servletContext);
            // 这里创建字符集过滤器
            CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter("utf-8");
            // 将字符集过滤器添加到servlet中
            FilterRegistration.Dynamic dynamic = servletContext.addFilter("encodingFilter", encodingFilter);
            // 添加字符集过滤器的patterns
            dynamic.addMappingForUrlPatterns(EnumSet.of(DispatcherType.INCLUDE, DispatcherType.REQUEST, DispatcherType.FORWARD), false, "/*");
        }
    }

    支持当前类的分析也完成了

    然后列出怎么用的剩余部分

    public class Config extends AbstractDispatcherServletInitializer {
        
        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            super.onStartup(servletContext);
            CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter("utf-8");
            FilterRegistration.Dynamic dynamic = servletContext.addFilter("encodingFilter", encodingFilter);
            dynamic.addMappingForUrlPatterns(EnumSet.of(DispatcherType.INCLUDE, DispatcherType.REQUEST, DispatcherType.FORWARD), false, "/*");
        }
        
        @Override
        protected WebApplicationContext createServletApplicationContext() {
            AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
            applicationContext.register(SpringMvcConfiguration.class);
            return applicationContext;
        }
        
        @Override
        protected String[] getServletMappings() {
            return new String[]{"/"};
        }
        
        @Override
        protected WebApplicationContext createRootApplicationContext() {
            AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
            applicationContext.register(SpringConfiguration.class);
            return applicationContext;
        }
    }

    @Configuration
    @ComponentScan(value = {"com.zhazha"}, excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class}))
    public class SpringConfiguration {
    }

    @Configuration
    @EnableWebMvc
    @ComponentScan("com.zhazha.controller")
    public class SpringMvcConfiguration implements WebMvcConfigurer {
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/js/**", "/images/**", "/css/**").addResourceLocations("/js/", "/images/", "/css/").resourceChain(true).addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"));
        }
        
        @Bean
        public ViewResolver viewResolver() {
            InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
            viewResolver.setPrefix("/WEB-INF/pages/");
            viewResolver.setSuffix(".jsp");
            return viewResolver;
        }
    }

    注解EnableWebMvc的作用仅仅只是注入了一个DelegatingWebMvcConfiguration类, 发现内部主要的功能就是提供基础的对象, 给springmvc使用

    (3). RequestMapping注解

    主要配合网页的url进行使用, 匹配相同的HandlerMapping

    (4). RequestParam注解

    主要用于前端名字和后端变量不同的时候使用的

    1580630161169

    (5) initBinder注解

    @InitBinder
    public void initBinder(WebDataBinder webDataBinder) {
        webDataBinder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
    }

    用于处理前端到后端数据之间转化问题

    也可以使用

    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birthday;

    不需要上面的方式, 也同样可以完成那个功能

    (6) RequestHeader和CookieValue注解

    用于获取web中的request和cookie中的对象

    (7) ModelAttribute注解

    ModelAttribute修饰的方法会提前于testModelAttribute方法执行之前运行

    @Controller
    public class ModelAttributeController {
        
        @ModelAttribute
        public void showModel(String username) {
            System.out.println("ModelAttribute username = " + username);
        }
    
        @RequestMapping("testModelAttribute")
        @ResponseBody
        public String testModelAttribute(String username) {
            System.out.println("testModelAttribute username = " + username);
            return "success";
        }
    
    }
    

    (8)SessionAttributes注解和SessionAttribute注解

    SessionAttributes注解的字符串, 将被监控, 如果这个controller中存在这个字符串则会默认将其放入session

    使用SessionAttribute来获取session那个字符串中拿出数据

    @Controller
    // 监控session 的 message
    @SessionAttributes(value = {"message"})
    public class SessionAttributesController {
        
        /**
         * 将值存入model
         *
         * @param model
         *
         * @return
         */
        @RequestMapping("setSessionAttributes")
        public String setSessionAttributes(Model model) {
            model.addAttribute("message", "session的内容");
            return "success";
        }
        
        @RequestMapping("useSessionAttributes")
        public String useSessionAttributes(HttpServletRequest request, @SessionAttribute(value = "message", required = false) String message) {
            System.out.println(message);
            System.out.println("request attribute " + request.getAttribute("message"));
            return "success";
        }
        
    }

    (9) RequestAttribute注解

    这个和上面的SessionAttribute一样, 主要是将request中的值放入到我们需要的RequestAttribute注解修饰的对象中

    (10)ExceptionHandler注解

    @ControllerAdvice
    public class ExceptionHandlerAdvice {
        
        @ExceptionHandler(Exception.class)
        public String handleException(Model model, Exception e) {
            String errorMsg = "";
            //判断Exception的类型是不是CustomerException
            if (e instanceof CustomerException) {
                errorMsg = e.getMessage();
            }
            else {
                //系统异常
                errorMsg = "服务器内部错误,请联系管理员!";
            }
            
            model.addAttribute("errorMsg", errorMsg);
            return "error";
        }
        
    }

    (11) 时间转化

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", locale = "zh", timezone="GMT+8")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date birthday;

    https://794e611d.wiz03.com/wapp/pages/view/share/s/1VjC4t215AfJ2knVCX1yXcay1g3zpJ3oWkvk2CcRRz1_NC9G

    (12) 跨域注解的使用

    @CrossOrigin(origins = "*", maxAge = 3600)

    origins: 允许什么域名访问

    maxAge: 超时时间

    如果不使用注解的话可以使用添加过滤器的方法

    跨域本体过滤器

    public class CrossOriginFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
        
        }
        
        @Override
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
            try {
                HttpServletRequest request = (HttpServletRequest) req;
                HttpServletResponse response = (HttpServletResponse) res;
                System.out.println("解决跨域问题的过滤器执行了");
                //设置response的响应消息头实现跨域问题的解决
                /* 允许跨域的主机地址 */
                response.setHeader("Access-Control-Allow-Origin", "*");
                /* 允许跨域的请求方法GET, POST, HEAD 等 */
                response.setHeader("Access-Control-Allow-Methods", "*");
                /* 重新预检验跨域的缓存时间 (s) */
                response.setHeader("Access-Control-Max-Age", "3600");
                /* 允许跨域的请求头 */
                response.setHeader("Access-Control-Allow-Headers", "*");
                /* 是否携带cookie */
                response.setHeader("Access-Control-Allow-Credentials", "true");
                //放行
                chain.doFilter(request, response);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        @Override
        public void destroy() {
        
        }
    }
    

    将过滤器添加到tomcat容器中使用

    public class Config extends AbstractDispatcherServletInitializer {
        
        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            super.onStartup(servletContext);
            
            /**
             * 可以添加其他过滤器
             */
            
            /**
             * 添加跨域拦截过滤器
             */
            FilterRegistration.Dynamic crossOriginFilter = servletContext.addFilter("crossOriginFilter", new CrossOriginFilter());
            crossOriginFilter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.INCLUDE, DispatcherType.REQUEST, DispatcherType.FORWARD), false, "/*");
        }
        
        //**
    }
    

    1581609309599

    1581609361198

    1581609397402

    2. springmvc源码分析

    下面这个源码就是springmvc初始化的过程

    public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer {
        
        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            super.onStartup(servletContext);
            registerDispatcherServlet(servletContext);
        }
        
        protected void registerDispatcherServlet(ServletContext servletContext) {
            // 获取servlet的名字
            String servletName = getServletName();
            // 这个方法就是我们自定义的, 主要的功能就是注册SpringMvcConfiguration.class类的ApplicationContenxt
            WebApplicationContext servletAppContext = createServletApplicationContext();
            // 这里创建了一个DispatcherServlet, 下面分析DispatcherServlet的时候需要分析这里
            FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
            // 设置上下文初始化器        
            dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
    
            // 添加DispatcherServlet到spring应用上下文并且获取注册器
            ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
            if (registration == null) {
                throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " +
                        "Check if there is another servlet registered under the same name.");
            }
            // 设置tomcat运行
            registration.setLoadOnStartup(1);
            // 设置mapping映射目录
            registration.addMapping(getServletMappings());
            // 判断是否异步
            registration.setAsyncSupported(isAsyncSupported());
            // 获取所有拦截器
            Filter[] filters = getServletFilters();
            if (!ObjectUtils.isEmpty(filters)) {
                for (Filter filter : filters) {
                    // 注册拦截器
                    registerServletFilter(servletContext, filter);
                }
            }
            // 自定义拦截器注册到DispatcherServlet
            customizeRegistration(registration);
        }
        
        // **
    }

    至此DispatcherServlet初始化完毕

    DispatcherServlet也被注入到了spring应用上下文中了

    分析springmvc无外乎分析请求的 接受 - 处理 - 响应 三个步骤, 主要还是对请求的操作

    // 这里创建了一个DispatcherServlet, 下面分析DispatcherServlet的时候需要分析这里
    FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);

    我们分析这里

    protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
        return new DispatcherServlet(servletAppContext);
    }

    1581619842334

    发现这个类, 只不过是一个servlet, 其他的都是Aware用于注入对象使用的

    所以我们分析这个类吧

    public interface Servlet {
        public void init(ServletConfig config) throws ServletException;
        public ServletConfig getServletConfig();
        public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException;
        public String getServletInfo();
        public void destroy();
    }

    学过servlet的都知道, 它存在生命周期的, 它的生命周期是 构造方法 -> init -> service -> destroy

    现在分析DispatcherServlet的生命周期

    (1) DispatcherServlet初始化

    javax.servlet.GenericServlet#init(javax.servlet.ServletConfig)

    GenericServlet:

    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }

    org.springframework.web.servlet.HttpServletBean#init

    接下来就是

    @Override
    public final void init() throws ServletException {
         // 初始化
        PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
        if (!pvs.isEmpty()) {
            try {
                // 设置包装bean
                BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
                bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
                // 初始化包装bean
                initBeanWrapper(bw);
                bw.setPropertyValues(pvs, true);
            }
            catch (BeansException ex) {
                // ...
            }
        }
        // 初始化servletBean, 我们需要分析这个方法
        initServletBean();
    }

    org.springframework.web.servlet.FrameworkServlet#initServletBean

    @Override
        protected final void initServletBean() throws ServletException {
            // ...
            long startTime = System.currentTimeMillis();
    
            try {
                // 初始化web应用上下文, 我们要分析这些下面的两个方法
                this.webApplicationContext = initWebApplicationContext();
                // 初始化servlet
                initFrameworkServlet();
            }
            catch (ServletException | RuntimeException ex) {
                // ...
            }
    
            // ...
        }

    org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext

    protected WebApplicationContext initWebApplicationContext() {
        // 从spring应用上下文中获取webapplicationContext, 下面有它的源码
        WebApplicationContext rootContext =
            WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        WebApplicationContext wac = null;
        // 下面的这个if就是对webapplicationcontext的初始化
        if (this.webApplicationContext != null) {
            wac = this.webApplicationContext;
            if (wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
                if (!cwac.isActive()) {
                    if (cwac.getParent() == null) {
                        cwac.setParent(rootContext);
                    }
                    // 初始化web应用上下文并且刷新启动它(下面有它的源码)
                    configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
        // 下面这段代码, 我们项目没有执行, 估计使用的是xml的方法才会执行他们, 而下面执行的非常重要的方法onRefresh, 我们会在上面的configureAndRefreshWebApplicationContext的wac.refresh();方法里执行
        if (wac == null) {
            // 查找已经绑定的上下文
            wac = findWebApplicationContext();
        }
        if (wac == null) {
            // 如果查找不到上下文则指定父亲为上下文
            wac = createWebApplicationContext(rootContext);
        }
        if (!this.refreshEventReceived) {
            synchronized (this.onRefreshMonitor) {
                // 刷新上下文, 初始化一些数据
                onRefresh(wac);
            }
        }
        if (this.publishContext) {
            String attrName = getServletContextAttributeName();
            getServletContext().setAttribute(attrName, wac);
        }
        return wac;
    }

    下面的参数 attrName 是 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE web应用上下文

    @Nullable
    public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) {
        Object attr = sc.getAttribute(attrName);
        // 忽略中间的异常判断
        return (WebApplicationContext) attr;
    }

    org.springframework.web.servlet.FrameworkServlet#configureAndRefreshWebApplicationContext

    protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
        // 下面的if主要就是判断是否存在id, 不存在则给它一个
        if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
            if (this.contextId != null) {
                wac.setId(this.contextId);
            }
            else {
                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                          ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
            }
        }
        // 初始化web应用上下文
        wac.setServletContext(getServletContext());
        wac.setServletConfig(getServletConfig());
        wac.setNamespace(getNamespace());
        wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
        ConfigurableEnvironment env = wac.getEnvironment();
        if (env instanceof ConfigurableWebEnvironment) {
            ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
        }
        // 后置处理web应用上下文, 这里为空
        postProcessWebApplicationContext(wac);
        // 剩余的初始化
        applyInitializers(wac);
        // 最终和spring的应用上下文一样的刷新, 里面存在十三个步骤
        wac.refresh();
    }

    wac.refresh() 需要分析下

    比如:

    org.springframework.web.servlet.DispatcherServlet#initStrategies

    protected void initStrategies(ApplicationContext context) {
        // 初始化上传文件解析器
        initMultipartResolver(context);
        // 初始化本地解析器
        initLocaleResolver(context);
        // 初始化主题解析器
        initThemeResolver(context);
        // 初始化处理器映射器
        initHandlerMappings(context);
        // 初始化处理器适配器
        initHandlerAdapters(context);
        // 初始化处理器异常解析器,如果执行过程中遇到异常将交给HandlerExceptionResolver来解析
        initHandlerExceptionResolvers(context);
        // 初始化请求到具体视图名称解析器
        initRequestToViewNameTranslator(context);
        // 初始化视图解析器,通过ViewResolver解析逻辑视图名到具体视图实现
        initViewResolvers(context);
        // 初始化flash映射管理
        initFlashMapManager(context);
    }

    (2) DispatcherServlet 处理请求的过程分析

    大概的过程:

    1,DispatherServlet:前端控制器用户请求到达前端控制器,相当于MVC中的C,而DispatherServlet是整个流程的核心,它来调用其他组件来处理用户的请求,前端控制器的存在降低了其他组件之间的耦合度。

    2,HandlerMapping:处理器映射器它的作用就好比去看电影要拿着电影票根据电影票上面的座位号找到座位其中座位就是Handler,电影票以及上面的座位号就是URLHandlerMapping 负责根据用户请求找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

    3,Handler:处理器Handler是后端控制器,在前端控制器的控制下后端控制器对具体的用户请求进行处理,Handler涉及到具体的用户业务请求,所以一般情况下需要程序员根据业务需求开发.

    4,HandlerAdapter:处理器适配器通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过适配器可以对更多类型的处理器进行执行。播放的电影是3D的你看不清楚,因此电影院跟你说你要想看清电影就必须戴3D眼镜。也就是说Handler满足一定的要求才可以被执行。

    5,ViewResolver:视图解析器ViewResolver负责将处理结果生成View视图,ViewResolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。

    img

    我们简单分析下就行

    知道DispatcherServlet 是一个servlet之后可以从 javax.servlet.Servlet#service 这个方法开始分析我们的代码

    1581733492179

    1581733515386

    上图的查找方法方式, 越往最小子类的找越准

    1581733579882

    我的的请求走的是get方法

    1581733626115

    1581733642529

    1581733660299

    1581733685251

    上面这个方法主要分析其内部的

    1581733715461

    的这个方法

    最终进入了最关键的方法中

    现在我们围绕这个方法来分析, springmvc如何处理请求

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        try {
            ModelAndView mv = null;
            Exception dispatchException = null;
            try {
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);
    
                // 确定当前请求的处理链, 内部存在HandlerMapping通过这个mapping获取了处理链, 这里我们有详细的源码分析, 请复制方法名搜索出来
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }
                //确定了handler适配器
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }
                // 借助适配器去调用真实的Controller, 下面有适配器的源码
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                applyDefaultViewName(processedRequest, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {/*...*/}
            // view生成
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Throwable err) {
            triggerAfterCompletion(processedRequest, response, mappedHandler,
                                   new NestedServletException("Handler processing failed", err));
        }
        finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                // Instead of postHandle and afterCompletion
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            }
            else {
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }

    这里我们遇到了前面一直讲的handierMapping类

    @Nullable
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
            for (HandlerMapping mapping : this.handlerMappings) {
                HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }

    这里如果我们要知道handlerMappings这个成员对象是什么时候初始化的, 请下字段监听器断点, 查看前面没有详细剖析的初始化过程

    1582022256920

    1582024974140

    上面的那个红色框框的功能是修改和访问对修改和访问下断点

    运行后你会发现它断在initHandlerMappings方法里, 逆向分析下它(initHandlerMappings很眼熟是不是, 就在前面的初始化过程)

    1582022653435

    我们进入方法下方法首断点, 查看返回值只有一个地方, 所以关注result的变化就行

    public static <T> Map<String, T> beansOfTypeIncludingAncestors(
        ListableBeanFactory lbf, Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
        throws BeansException {
        Map<String, T> result = new LinkedHashMap<>(4);
        // 我们分析这个方法看看
        result.putAll(lbf.getBeansOfType(type, includeNonSingletons, allowEagerInit));
        if (lbf instanceof HierarchicalBeanFactory) {
            HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
            if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
                Map<String, T> parentResult = beansOfTypeIncludingAncestors(
                    (ListableBeanFactory) hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit);
                parentResult.forEach((beanName, beanInstance) -> {
                    if (!result.containsKey(beanName) && !hbf.containsLocalBean(beanName)) {
                        // 把最终的名字和bean都放入到result中返回
                        result.put(beanName, beanInstance);
                    }
                });
            }
        }
        return result;
    }

    详细getBeansOfType()分析后发现

    1582023164099

    发现这个对象 this.beanDefinitionNames (跑题了)

    最终我们发现这个方法被调用了好几次, 但是发现

    1582024749845

    只有在上面事件方法的时候才会发现我们需要的结果

    适配器执行controller的源码

    @Override
    protected ModelAndView handleInternal(HttpServletRequest request,
                                          HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        // model和视图绑定的变量
        ModelAndView mav;
        checkRequest(request);
        if (this.synchronizeOnSession) {
            // .. http session
        }
        else {
            // 最终我们会执行这句话, 获取model和视图绑定的变量, 我们下面分析这个方法
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }
        // 是否缓存
        if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
            if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
                applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
            }
            else {
                // 准备 response, 设置缓存时间
                prepareResponse(response);
            }
        }
        return mav;
    }

    调用controller方法 + 生成modelAndView 并且解析view

    @Nullable
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
                                               HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        // 设置requestattr
        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        try {
            // 获取前端数据绑定, 获取了使用initbinder注解的类
            WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
            ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
            // ...
            // 调用我们的controller方法, 分析这个方法
            invocableMethod.invokeAndHandle(webRequest, mavContainer);
            // ...
            // 生成modelandview, 返回了一个ModelAndView
            return getModelAndView(mavContainer, modelFactory, webRequest);
        }
        finally {
            // 请求处理基本完毕, 现在销毁一些对象和回调参数等等
            webRequest.requestCompleted();
        }
    }

    调用controller方法

    org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle

    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
                Object... providedArgs) throws Exception {
        // 调用这个方法, 我们分析这个方法, 最终解析出了 "sucess" 这几个字符串返回值
        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
        setResponseStatus(webRequest);
        // ...
        mavContainer.setRequestHandled(false);
        Assert.state(this.returnValueHandlers != null, "No return value handlers");
        // 这里面success放入了view中
        // ...
        this.returnValueHandlers.handleReturnValue(
            returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
        // ...
    }

    获取所有参数和调用方法

    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
                                   Object... providedArgs) throws Exception {
        // 获取参数
        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
        if (logger.isTraceEnabled()) {
            logger.trace("Arguments: " + Arrays.toString(args));
        }
        // 调用我们的controller
        return doInvoke(args);
    }

    我们现在需要分析生成和解析view的过程

    // 生成modelandview
    return getModelAndView(mavContainer, modelFactory, webRequest);

    我们现在分析前端参数绑定到后端的过程

    从前面的分析我们知道, 我们controller中方法的调用执行的方法就是invokeForRequest 这个方法进行分发的

    所以围绕着它进行分析

    protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
                                               Object... providedArgs) throws Exception {
        // 获取参数
        MethodParameter[] parameters = getMethodParameters();
        if (ObjectUtils.isEmpty(parameters)) {
            return EMPTY_ARGS;
        }
    
        Object[] args = new Object[parameters.length];
        for (int i = 0; i < parameters.length; i++) {
            MethodParameter parameter = parameters[i];
            parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
            args[i] = findProvidedArgument(parameter, providedArgs);
            if (args[i] != null) {
                continue;
            }
            // 解析是否存在合适的参数解析器, 将参数和解析器放入一个map的缓存中
            if (!this.resolvers.supportsParameter(parameter)) {
                throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
            }
            try {
                // 解析参数, 直接从缓存中获取我们的解析器进行参数解析, 现在我们需要分析这个方法
                args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
            }
            catch (Exception ex) {
                // ...
                throw ex;
            }
        }
        return args;
    }

    public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
                NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
            // 获取requestParam注解的名字, 比如前端名字叫userId, 我们后端需要的是 id, 所以需要这个注解去使用userId然后绑定到我们的 id 变量中, 这里获取的是 userId
            NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
            MethodParameter nestedParameter = parameter.nestedIfOptional();
            // 获取注解上的userId
            Object resolvedName = resolveStringValue(namedValueInfo.name);
            // 解析userId获取userId的值到arg
            Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
            if (arg == null) {
                // ...
            }
            else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
                // ...
            }
            // 判断是否存在initBinder注解的参数绑定方式
            if (binderFactory != null) {
                // 创建binder, 内部存在一个initBinder方法, 初始化我们的initbinder, 最终判断我们存在一个ConverterService, 所以在initBinder里面设置了这个, 如果有其他则也初始化它, 最终会调用Object returnValue = binderMethod.invokeForRequest(request, null, dataBinder);这个方法主动调用我们写的initBinder
                WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
                try {
                    arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
                }
                catch (ConversionNotSupportedException ex) {
                    throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
                            namedValueInfo.name, parameter, ex.getCause());
                }
                catch (TypeMismatchException ex) {
                    throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
                            namedValueInfo.name, parameter, ex.getCause());
                }
            }
    
            handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
    
            return arg;
        }

    protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
        HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
        // ...
        Object arg = null;
        // 判断是否为文件
        MultipartRequest multipartRequest = request.getNativeRequest(MultipartRequest.class);
        if (multipartRequest != null) {
            List<MultipartFile> files = multipartRequest.getFiles(name);
            if (!files.isEmpty()) {
                arg = (files.size() == 1 ? files.get(0) : files);
            }
        }
        // 判断参数是否为空
        if (arg == null) {
            // 获取参数的值, 根据userId
            String[] paramValues = request.getParameterValues(name);
            if (paramValues != null) {
                // 参数放入arg然后返回
                arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
            }
        }
        return arg;
    }

    这里就是我们写的方法, 添加了conversionService的stringToDateConverter, 这个stringToDateConverter 也是我们自定义的

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        ConversionService conversionService = binder.getConversionService();
        if (conversionService instanceof GenericConversionService) {
            GenericConversionService genericConversionService = (GenericConversionService) conversionService;
            genericConversionService.addConverter(stringToDateConverter);
        }
    }

    至此我们解析出了我们想要的参数最终通过

    @Nullable
    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
                                   Object... providedArgs) throws Exception {
        // ...
        return doInvoke(args);
    }

    上的这个方法调用我们的方法

    这是简化版的参数绑定过程

    对了, 还有初始化解析器的过程, 我发现原来它是写死的,

    private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
        List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();
    
        // Annotation-based argument resolution
        resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
        resolvers.add(new RequestParamMapMethodArgumentResolver());
        resolvers.add(new PathVariableMethodArgumentResolver());
        resolvers.add(new PathVariableMapMethodArgumentResolver());
        resolvers.add(new MatrixVariableMethodArgumentResolver());
        resolvers.add(new MatrixVariableMapMethodArgumentResolver());
        resolvers.add(new ServletModelAttributeMethodProcessor(false));
        resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
        resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
        resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
        resolvers.add(new RequestHeaderMapMethodArgumentResolver());
        resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
        resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
        resolvers.add(new SessionAttributeMethodArgumentResolver());
        resolvers.add(new RequestAttributeMethodArgumentResolver());
    
        // Type-based argument resolution
        resolvers.add(new ServletRequestMethodArgumentResolver());
        resolvers.add(new ServletResponseMethodArgumentResolver());
        resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
        resolvers.add(new RedirectAttributesMethodArgumentResolver());
        resolvers.add(new ModelMethodProcessor());
        resolvers.add(new MapMethodProcessor());
        resolvers.add(new ErrorsMethodArgumentResolver());
        resolvers.add(new SessionStatusMethodArgumentResolver());
        resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
    
        // Custom arguments
        if (getCustomArgumentResolvers() != null) {
            resolvers.addAll(getCustomArgumentResolvers());
        }
    
        // Catch-all
        resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
        resolvers.add(new ServletModelAttributeMethodProcessor(true));
    
        return resolvers;
    }

    总共25个解析器

    view渲染的过程

    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
                                       @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
                                       @Nullable Exception exception) throws Exception {
        render(mv, request, response);
        if (mappedHandler != null) {
            // 这里好像是后置拦截器
            mappedHandler.triggerAfterCompletion(request, response, null);
        }
    }

    protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
        View view;
        // ...
        // 这里我们解析view完毕
        view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
        // ...
        try {
            // ...
            view.render(mv.getModelInternal(), request, response);
        }
        // catch ...
    }

    最终在里面调用了这个方法

    view = resolveViewName(viewName, mv.getModelInternal(), locale, request);

    org.springframework.web.servlet.view.AbstractCachingViewResolver#resolveViewName

    public View resolveViewName(String viewName, Locale locale) throws Exception {
        // ...
        // 这里就是解析完成view的过程, 这里完成后就会获得 xxx.jsp
        view = createView(viewName, locale);
        // ...
    }

    我想现在分析这个方法

    org.springframework.web.servlet.view.UrlBasedViewResolver#createView

    这里我们判断是重定向还是转发又或者是返回json, 这样需要使用父类的方法

    protected View createView(String viewName, Locale locale) throws Exception {
        // Check for special "redirect:" prefix.
        if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
            String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
            RedirectView view = new RedirectView(redirectUrl,
                                                 isRedirectContextRelative(), isRedirectHttp10Compatible());
            String[] hosts = getRedirectHosts();
            if (hosts != null) {
                view.setHosts(hosts);
            }
            return applyLifecycleMethods(REDIRECT_URL_PREFIX, view);
        }
        // Check for special "forward:" prefix.
        if (viewName.startsWith(FORWARD_URL_PREFIX)) {
            String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
            InternalResourceView view = new InternalResourceView(forwardUrl);
            return applyLifecycleMethods(FORWARD_URL_PREFIX, view);
        }
    
        // 如果不是转发或者重定向, 则使用父类的方法生成view
        return super.createView(viewName, locale);
    }

    所以它最终调用了这个方法

    @Override
    protected View loadView(String viewName, Locale locale) throws Exception {
        // 这里完成了/WEB-INF/pages/success.jsp的组装
        AbstractUrlBasedView view = buildView(viewName);
        // 这里完成了view的最终包装
        View result = applyLifecycleMethods(viewName, view);
        return (view.checkResource(locale) ? result : null);
    }

    org.springframework.web.servlet.view.UrlBasedViewResolver#buildView

    获取view最终完成view的生成

    protected AbstractUrlBasedView buildView(String viewName) throws Exception {
        // 获取内部资源视图
        Class<?> viewClass = getViewClass();
        AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(viewClass);
        // 这里我们开始组装url, /WEB-INF/pages/success.jsp
        view.setUrl(getPrefix() + viewName + getSuffix());
        view.setAttributesMap(getAttributesMap());
        // ...
        return view;
    }

    3. 转化器的添加方式

    我们可以看到converter的添加顺序

    org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#getMessageConverters

    protected final List<HttpMessageConverter<?>> getMessageConverters() {
        if (this.messageConverters == null) {
            this.messageConverters = new ArrayList<>();
            configureMessageConverters(this.messageConverters);
            if (this.messageConverters.isEmpty()) {
                addDefaultHttpMessageConverters(this.messageConverters);
            }
            extendMessageConverters(this.messageConverters);
        }
        return this.messageConverters;
    }

    我们可以在这里添加WebMvcConfigurer

    /**
         * 增加到我们配置内部
         *
         * @param converters
         */
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        // converters.add(new ProtobufJsonFormatHttpMessageConverter());
    }
    
    /**
         * 钩子, 可以增删改查converters转换器列表的优先级
         *
         * @param converters
         */
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(mappingJackson2HttpMessageConverter());
    }

    4. 拦截器的添加方式

    public class ConverterConfig implements WebMvcConfigurer {
        
        @Bean
        public ViewResolver viewResolver() {
            InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
            viewResolver.setPrefix("/WEB-INF/pages/");
            viewResolver.setSuffix(".jsp");
            return viewResolver;
        }
        
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/js/**", "/images/**", "/css/**").addResourceLocations("/js/", "/images/", "/css/").resourceChain(true).addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"));
        }
        
        public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
            MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
            Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder = new Jackson2ObjectMapperBuilder();
            ObjectMapper objectMapper = jackson2ObjectMapperBuilder.build();
            objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
            objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
            DateFormat dateFormat = objectMapper.getDateFormat();
            objectMapper.setDateFormat(new MyDateFormat(dateFormat));
            mappingJackson2HttpMessageConverter.setObjectMapper(objectMapper);
            List<MediaType> list = new ArrayList<>();
            list.add(MediaType.APPLICATION_JSON);
            mappingJackson2HttpMessageConverter.setSupportedMediaTypes(list);
            return mappingJackson2HttpMessageConverter;
        }
        
        /**
         * 增加到我们配置内部
         *
         * @param converters
         */
        @Override
        public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
            // converters.add(new ProtobufJsonFormatHttpMessageConverter());
            /**
             * 同样可以在这里添加, 这样的话, addDefaultHttpMessageConverters也就不需要再添加mappingJackson2HttpMessageConverter, 它里面添加这个对象前面是有一个if语句判断如果configureMessageConverters这个方法添加了, 它的if语句就不成立了
             *  this.messageConverters = new ArrayList<>();
             *  configureMessageConverters(this.messageConverters);
             *  if (this.messageConverters.isEmpty()) {
             *      addDefaultHttpMessageConverters(this.messageConverters);
             *  }
             *  // 钩子, 用于增删改查我们的messageConverters
             *  extendMessageConverters(this.messageConverters);
             */
            converters.add(mappingJackson2HttpMessageConverter());
        }
        
        /**
         * 钩子, 可以增删改查converters转换器列表的优先级
         *
         * @param converters
         */
        @Override
        public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
            /**
             * 这种方式比较慢, 需要遍历
             */
            // converters.removeIf(httpMessageConverter -> httpMessageConverter instanceof MappingJackson2HttpMessageConverter);
            // converters.add(mappingJackson2HttpMessageConverter());
        }
        /**
         * 添加拦截器的方式
         * @param registry
         */
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new MyInterceptor1());
        }
    }

    public class MyInterceptor1 implements HandlerInterceptor {
        
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            // String requestURI = request.getRequestURI();
            // System.out.println(requestURI); // /user
            // StringBuffer requestURL = request.getRequestURL();
            // System.out.println(requestURL); // http://localhost:8080/user
            if (request.getRequestURL().toString().contains("login")) {
                return true;
            }
            String loginName = (String) request.getSession().getAttribute("loginName");
            return !StringUtils.isBlank(loginName);
        }
        
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            Map<String, Object> map = (Map<String, Object>) request.getAttribute("map");
            if (map == null) {
                return;
            }
            System.out.println("message" + map.get("message"));
            System.out.println("msg" + map.get("msg"));
        }
        
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            // String message = (String) request.getAttribute("message");
            // System.out.println("afterCompletion message = " + message);
        }
    }






  • 相关阅读:
    傻瓜教程:asp.net(c#) 如何配置authentication,完成基于表单的身份验证
    ajax与php交互的get和post两种实现方式
    php 存储过程
    一万小时天才理论
    servlet阅读
    post and get
    合并两个有序数组(重新开始)
    Java参数传递问题
    一万小时(如何实现)阅读
    java IO 流的学习(我们到底能走多远系列1)
  • 原文地址:https://www.cnblogs.com/bangiao/p/12418358.html
Copyright © 2011-2022 走看看