• SpringMVC


    文章一直更新中......

    众所周知,Spring mvc 将所有请求都在 DispatcherServlet 中处理,并且配置有一个监听器

    在源码了解之前,我们需要知道的是服务器启动时候,listener filter servlet 执行顺序:---.大家可以自己去测试

                            1.listener 2.filter 3.servlet 

    (一).所以我们第一步先看一看监听器listener 做了什么事情?

    1.实现了 ServletContextListener 接口 ,用监听servletcontext的动作
    2.继承了ContextLoader ,执行根应用程序的上下文实际的初始化工作被 ContextLoaderListener(我们的监听器)所调用

     所以在服务器启动时候,率先进行了 invoke initWebApplicationContext()  method;

    我们点进去看一看它的实现

    因为是初始化--先进行了判断保证我们的servletcontext 是一个干净的servletcontext 

    执行流程:
    1.private
    WebApplicationContext context;
    初始化是null;

    2.在createWebApplicationCotext()方法中 成员变量 context 被实例化为 class org.springframework.web.context.support.XmlWebApplicationContext 类型,这个类型实现了 ConfigurableWebApplicationContext 接口,也是必须要实现的;

    ConfigurableWebApplicationContext 接口的描述:

     *Interface to be implemented by configurable web application contexts.

     * Supported by {@link ContextLoader} and

     * {@link org.springframework.web.servlet.FrameworkServlet}.

     *该接口由可配置的web 上下文所实现,对contextloader(监听器) frameworkservlet(servlet 容器)有支持,可以得知该接口专门为web 打造的;

    3.isActive() 方法: 翻译看源码,我们并没有进行刷新的操作,所以返回默认值false

    /**
         * Determine whether this application context is active, that is,
         * whether it has been refreshed at least once and has not been closed yet.
         * @return whether the context is still active
         * @see #refresh()
         * @see #close()
         * @see #getBeanFactory()
         */
      确定是否这个应用上下文处于活动状态,也就是说它至少被刷新过一次,并尚未关闭
    boolean isActive();

    4.configureAndRefreshWebApplicationContext():完成bean 的解析,加载,初始化,很重要的操作;

    只一步主要就是 refresh() 操作:
      源码对此方法对解释
    /**
    
    * Load or refresh the persistent representation of the configuration,
    
    * which might an XML file, properties file, or relational database schema.
    
    * <p>As this is a startup method, it should destroy already created singletons
    
    * if it fails, to avoid dangling resources. In other words, after invocation
    
    * of that method, either all or no singletons at all should be instantiated.
    
    * @throws BeansException if the bean factory could not be initialized
    
    * @throws IllegalStateException if already initialized and multiple refresh
    
    * attempts are not supported
    
    */
    可以持久化的加载或者刷新配置文件,可能是XML 文件,也可能是properties 文件,或者是关系型数据库的模式
    作为启动的方法,如果启动失败,他应该销毁已经创建的单例,去避免挂起资源; 换句话说,在调用这个方法后,所有实例化的和不实例化的
    都应该被实例化
    public void refresh() throws BeansException, IllegalStateException {
            synchronized (this.startupShutdownMonitor) {
                // Prepare this context for refreshing.
                prepareRefresh();
    
                // Tell the subclass to refresh the internal bean factory.
                ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
                // Prepare the bean factory for use in this context.
                prepareBeanFactory(beanFactory);
    
                try {
                    // Allows post-processing of the bean factory in context subclasses.
                    postProcessBeanFactory(beanFactory);
    
                    // Invoke factory processors registered as beans in the context.
                    invokeBeanFactoryPostProcessors(beanFactory);
    
                    // Register bean processors that intercept bean creation.
                    registerBeanPostProcessors(beanFactory);
    
                    // Initialize message source for this context.
                    initMessageSource();
    
                    // Initialize event multicaster for this context.
                    initApplicationEventMulticaster();
    
                    // Initialize other special beans in specific context subclasses.
                    onRefresh();
    
                    // Check for listener beans and register them.
                    registerListeners();
    
                    // Instantiate all remaining (non-lazy-init) singletons.
                    finishBeanFactoryInitialization(beanFactory);
    
                    // Last step: publish corresponding event.
                    finishRefresh();
                }
    
                catch (BeansException ex) {
                    // Destroy already created singletons to avoid dangling resources.
                    destroyBeans();
    
                    // Reset 'active' flag.
                    cancelRefresh(ex);
    
                    // Propagate exception to caller.
                    throw ex;
                }
            }
        }

     

    其中主要就是第2部和第11部:

    obtainFreshBeanFactory():进行解析我们的xml 以及我们自定义的xml 标签,每一个<bean> 都是 BeanDefinitions对象,里面维护了bean的一些详细信息,方便实例化

    注意,这一步并没有实例化bean ,只是解析XML ,将所有的eanDefinitions对象都放入了 beanDefinitionMap中;我们看它的实现

        (-)使用默认的bean工厂DefaultListableBeanFactory, loadBeanDefinitions(beanFactory)

       (二).使用XmlBean解析器去解析xml bean definition 

       (三). 注册bean

      (四).这一步很重要了,开始干活了,解析xml  

      (五).循环遍历查找使用哪一种解析器,像一些默认的标签bean,alias,import,beans 会进入第一个方法;

        像context ,aspect,dubbo 这种标签是属于自定义标签,只要符合Spring 规则就可以加载进行加载;下面我会说;

      我们先看parseDefaultElement(ele,delegate)方法:

      是不是提供了默认的标签bean,alias,import,beans的解析:

    我们看它解析bean 的实现;源码多,具体需要自己去看,就是拿到,ID class 等信息:

    bdholder 是一个装饰类,里面拿到了bean的所有信息

    注册bean 

    注册bean: 

    点进去可以看到:其实就是将bean 的信息对象注册到我们DefaultListableBeanFactory 工厂维护的beanDefinitionMap中;名字放入了List<String> beanDefinitionNames 中

     

    这里bean 的注册就完成了; 

    (六)关于自定义的XML 标签解析,也是Spring很有魅力的一个地方,像一些aop 标签,dubbo 标签都是通过自定义解析器去实现的,当然要遵循Spring 解析配置规则

    才会被Spring扫描解析到,那么如何让才能让Spring解析我们自定义的标签呢?

          以Spring-aop 包为例: 1.定义spring.handlers :键值对,里面存放的是我们组册解析我们自定义XML的类,有我们自己实现,会被反射实例化执行里面的方法,

                     2.spring.schemas :我们XML 所遵循的schemas,如果schemas地址是无效的,请META-INF目录下放入自己的xsd文件

        看Spring 解析方法:handler 就是在Spring.handlers 对应的被反射实例化的对象,parse 方法就是对自定义标签的解析

    实例化并调用init ,init 方法是将标签的属性进行注册存放到map 中,由parse 方法去获取并对应解析

     自定义解析请看link:https://www.cnblogs.com/iscys/p/9905608.html


    1.spring 加载的时候,使用的bean 工厂 是默认的 DefaultListableBeanFactory 对象
    2.工厂内部维护着 Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);beanmap
    3.解析XML 时候,会分别识别bean 标签,import标签,alias 标签,beans 标签并做不同的判断 注册bean时候调用了registerBeanDefinition()将bean 注册到 beanDefinitionMap中
        beanDefinitionMap.put(beanName, beanDefinition);

     关于第11步,是很重要的,会完成bean 的实例化,以及自定义InitializingBean aware 的接口实现的调用,

       我单独写了文章;link:https://www.cnblogs.com/iscys/p/9903766.html

    5.将 this.context 即XmlWebApplicationContext 放入servletContext上下文中,servlet 可以进行共享此applicattioncontext,以此达到共享bean 的目的

    servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

    扩展:我们可以通过得知监听器给我们创建的applicationcontext 的类型
      WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());
      
      System.out.println(rootContext.getClass());

    二).Servlet 的实现 

    我们看一看它的类层次结构:DispatcherServlet--->  FrameworkServlet-->  HttpServletBean ---> HttpServlet 

    可以得知底层其实实现了HttpServlet,其实就是一个servlet
    给大家翻译一下 DispatcherServlet 文档:
    /**
     * Central dispatcher for HTTP request handlers/controllers, e.g. for web UI controllers or HTTP-based remote service
     * exporters. Dispatches to registered handlers for processing a web request, providing convenient mapping and exception
     * handling facilities.
     
    处理http 请求处理器和控制器的中央调度器,例如,用于web ui 控制器或者基于http的远程;调度注册的处理程序为web 请求
    提供便利的映射和异常处理;
    * <p>This servlet is very flexible: It can be used with just about any workflow, with the installation of the * appropriate adapter classes. It offers the following functionality that distinguishes it from other request-driven * web MVC frameworks:
    这个servlet 是非常精巧的:它几乎可以被任何的工作流所使用,只要安装了合适的适配器类,它提供了如下功能使之区别其他请求驱动的web mvc 框架

    * <ul> <li>It is based around a JavaBeans configuration mechanism.
    它是基于java bean 的配置机制的 * * <li>It can use any {@link HandlerMapping} implementation - pre-built or provided as part of an application - to * control the routing of requests to handler objects. Default is {@link org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping} * and {@link org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping}. HandlerMapping objects * can be defined as beans in the servlet's application context, implementing the HandlerMapping interface, overriding * the default HandlerMapping if present. HandlerMappings can be given any bean name (they are tested by type).
    它可以使用基于handlermapping 的实现,提前构建或者提供作为应用程序的一部分,来控制请求到处理对象之间的路由;默认提供了beannameurlhandlermapping
    以及default annotationmapping ;处理器映射器对象可以被定义作为servlet 上下问对象的bean ;实现handlermapping 接口,覆盖默认的handlempping
    handlermapping 会被赋予一个bean 名字;
    * <li>It can use any {@link HandlerAdapter}; this allows for using any handler interface. Default adapters are {@link * org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter}, {@link org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter}, * for Spring's {@link org.springframework.web.HttpRequestHandler} and {@link org.springframework.web.servlet.mvc.Controller} * interfaces, respectively. A default {@link org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter} * will be registered as well. HandlerAdapter objects can be added as beans in the application context, overriding the * default HandlerAdapters. Like HandlerMappings, HandlerAdapters can be given any bean name (they are tested by type). 它可以使用一些处理器适配器;它允许使用任何处理接口
    默认的适配器是 HttpRequestHandlerAdapter,simplecontrollerhandleradapter分别对应着 HttpRequestHandler,Controller handler接口
    默认的annotationhandler也会被注册 处理器适配器对象也会在application 上下文加载进来为bean

     * <li>The dispatcher's exception resolution strategy can be specified via a {@link HandlerExceptionResolver}, for
     * example mapping certain exceptions to error pages. Default are
     * {@link org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver},
     * {@link org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver}, and
     * {@link org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver}. These HandlerExceptionResolvers can be overridden
     * through the application context. HandlerExceptionResolver can be given any bean name (they are tested by type).
     *
     * <li>Its view resolution strategy can be specified via a {@link ViewResolver} implementation, resolving symbolic view
     * names into View objects. Default is {@link org.springframework.web.servlet.view.InternalResourceViewResolver}.
     * ViewResolver objects can be added as beans in the application context, overriding the default ViewResolver.
     * ViewResolvers can be given any bean name (they are tested by type).
     *
     * <li>If a {@link View} or view name is not supplied by the user, then the configured {@link
     * RequestToViewNameTranslator} will translate the current request into a view name. The corresponding bean name is
     * "viewNameTranslator"; the default is {@link org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator}.
     *
     * <li>The dispatcher's strategy for resolving multipart requests is determined by a {@link
     * org.springframework.web.multipart.MultipartResolver} implementation. Implementations for Jakarta Commons FileUpload
     * and Jason Hunter's COS are included; the typical choise is {@link org.springframework.web.multipart.commons.CommonsMultipartResolver}.
     * The MultipartResolver bean name is "multipartResolver"; default is none.
     *
     * <li>Its locale resolution strategy is determined by a {@link LocaleResolver}. Out-of-the-box implementations work via
     * HTTP accept header, cookie, or session. The LocaleResolver bean name is "localeResolver"; default is {@link
     * org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver}.
     *
     * <li>Its theme resolution strategy is determined by a {@link ThemeResolver}. Implementations for a fixed theme and for
     * cookie and session storage are included. The ThemeResolver bean name is "themeResolver"; default is {@link
     * org.springframework.web.servlet.theme.FixedThemeResolver}. </ul>
     *
     * <p><b>NOTE: The {@code @RequestMapping} annotation will only be processed if a corresponding
     * {@code HandlerMapping} (for type level annotations) and/or {@code HandlerAdapter} (for method level
     * annotations) is present in the dispatcher.</b> This is the case by default. However, if you are defining custom
     * {@code HandlerMappings} or {@code HandlerAdapters}, then you need to make sure that a corresponding custom
     * {@code DefaultAnnotationHandlerMapping} and/or {@code AnnotationMethodHandlerAdapter} is defined as well -
     * provided that you intend to use {@code @RequestMapping}.
     *
     * <p><b>A web application can define any number of DispatcherServlets.</b> Each servlet will operate in its own
     * namespace, loading its own application context with mappings, handlers, etc. Only the root application context as
     * loaded by {@link org.springframework.web.context.ContextLoaderListener}, if any, will be shared.
     *
     * <p>As of Spring 3.1, {@code DispatcherServlet} may now be injected with a web
     * application context, rather than creating its own internally. This is useful in Servlet
     * 3.0+ environments, which support programmatic registration of servlet instances. See
     * {@link #DispatcherServlet(WebApplicationContext)} Javadoc for details.
     *

    其实根据上一篇对servlet 详解,我们知道对于servlet 的初始化动作一定放在init 方法中,设置start 等级,这样就可以服务器启动,初始化servlet

    /**
         * Map config parameters onto bean properties of this servlet, and
         * invoke subclass initialization.
         * @throws ServletException if bean properties are invalid (or required
         * properties are missing), or if subclass initialization fails.
         */
    将文件配置参数映射到servlet 的bean 属性上,并调用子类初始化;
    @Override public final void init() throws ServletException {

     关于我们的请求是经过什么的过程呢?下面这张图就是它的执行流程,网上一堆,那么我一个一个的给大家解析跟随源代码;你就不会迷茫了;

    1.发送请求,这个就不用说了,哈哈,地址栏回车;接下来就是我们的DispatcherServlet操作了

    2.映射处理器:熟悉servlet 都知道,请求第一步进入doService(request,response) 方法 ,就是这个方法。

      里面会有一个doDispatch() ;这个我们可以理解为方法入口,进行分发 ;

    所有的方法执行都在doDispatch()中;我们看一看获取HandlerMapping,就是这个方法,通过request 请求得到mappedHandler,是个 HandlerExecutionChain

    执行链: 

     确定一个handler通过我们的request请求; 

    我们看一看getHandler()方法:会遍历我们注册过的handlermapping(handlerMapping是在初始化注册的) ;

    执行getHandler(request);往后面点,可以看到它会根据地址去我们的urlMap中去找HandlerMethod ,就是执行这个url

    所匹配的类名和方法;后期会被反射调用;

    去看有没有拦截器之类的,并返回执行链: 

    其实可以得出,这个执行链就是一些需要执行方法的链路,就是一些类方法组成的;如下:

    HandlerExecutionChain with handler [public void com.thirdparty.controller.gzh.WxConnectionController.wxConnection(javax.servlet.http.HttpServletResponse,java.lang.String,java.lang.String,java.lang.String,java.lang.String) throws java.lang.Exception] and 1 interceptor

    到这里handlerMapping 就结束了并返回执行链; 

    3.开始获得处理器适配器; 得到执行链中的需要执行的methodhandler来匹配HandlerAdapter;

    得到HandlerAdapter ,我们就可以通过这个适配器 去执行里面的方法;看第四步;

    4.关键的方法调用就来了,通过反射调用执行的方法,返回ModelAndView 

    方法调用: 

    Java 反射:

      

    这样,其实方法就执行完了;

    5.视图解析 

    原创打造,多多指教
  • 相关阅读:
    SQL语句集(转)
    Oracle 数据库 for update / for update nowait 的区别
    XML 关键字
    JAVA 操作 DBF 文件数据库
    Hibernate 懒加载和 Json 序列化冲突
    MYSQL 语法大全自己总结的
    php-laravel中间件使用
    php-表单验证
    php-laravel安装与使用
    iOS 关于权限设置的问题
  • 原文地址:https://www.cnblogs.com/iscys/p/9756458.html
Copyright © 2020-2023  润新知