spring 启动过程

  1. 首先,对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是ServletContext,其为后面的spring IoC容器提供宿主环境;前端

  2. 其次,在web.xml中会提供有contextLoaderListener。在web容器启动时,会触发容器初始化事件,此时contextLoaderListener会监听到这个事件,其contextInitialized方法会被调用,在这个方法中,spring会初始化一个启动上下文,这个上下文被称为根上下文,即WebApplicationContext,这是一个接口类,确切的说,其实际的实现类是XmlWebApplicationContext。这个就是spring的IoC容器,其对应的Bean定义的配置由web.xml中的context-param标签指定。在这个IoC容器初始化完毕后,spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE为属性Key,将其存储到ServletContext中,便于获取;java

  3. 再次,contextLoaderListener监听器初始化完毕后,开始初始化web.xml中配置的Servlet,这个servlet能够配置多个,以最多见的DispatcherServlet为例,这个servlet其实是一个标准的前端控制器,用以转发、匹配、处理每一个servlet请求。DispatcherServlet上下文在初始化的时候会创建本身的IoC上下文,用以持有spring mvc相关的bean。在创建DispatcherServlet本身的IoC上下文时,会利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE先从ServletContext中获取以前的根上下文(即WebApplicationContext)做为本身上下文的parent上下文。有了这个parent上下文以后,再初始化本身持有的上下文。这个DispatcherServlet初始化本身上下文的工做在其initStrategies方法中能够看到,大概的工做就是初始化处理器映射、视图解析等。这个servlet本身持有的上下文默认实现类也是mlWebApplicationContext。初始化完毕后,spring以与servlet的名字相关(此处不是简单的以servlet名为Key,而是经过一些转换,具体可自行查看源码)的属性为属性Key,也将其存到ServletContext中,以便后续使用。这样每一个servlet就持有本身的上下文,即拥有本身独立的bean空间,同时各个servlet共享相同的bean,即根上下文(第2步中初始化的上下文)定义的那些bean。web

 

 

 

 

1、先说ServletContextspring

  javaee标准规定了,servlet容器须要在应用项目启动时,给应用项目初始化一个ServletContext做为公共环境容器存放公共信息。ServletContext中的信息都是由容器提供的。express

举例:spring-mvc

经过自定义contextListener获取web.xml中配置的参数
1.容器启动时,找到配置文件中的context-param做为键值对放到ServletContext中
2.而后找到listener,容器调用它的contextInitialized(ServletContextEvent event)方法,执行其中的操做
例如:在web.xml中配置
复制代码
<context-param>
   <param-name>key</param-name>
   <param-value>value123</param-value>
</context-param>
<listener> 
   <listener-class>com.brolanda.contextlistener.listener.ContextListenerTest</listener-class>
</listener>
复制代码
配置好以后,在该类中获取对应的参数信息
复制代码
package com.brolanda.contextlistener.listener;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class ContextListenerTest implements ServletContextListener {
    
    public void contextDestroyed(ServletContextEvent event) {
        System.out.println("*************destroy ContextListener*************");
    }
    
    @SuppressWarnings("unused")
    public void contextInitialized(ServletContextEvent event) {
        System.out.println("*************init ContextListener*************");
        ServletContext servletContext = event.getServletContext();
        System.out.println("key:"+servletContext.getInitParameter("key"));
    }
    
}
复制代码

执行流程:缓存

  web.xml在<context-param></context-param>标签中声明应用范围内的初始化参数安全

1.启动一个WEB项目的时候,容器(如:Tomcat)会去读它的配置文件web.xml.读两个节点: <listener></listener> 和 <context-param></context-param>
2.紧接着,容器建立一个ServletContext(上下文)。在该应用内全局共享。

3.容器将<context-param></context-param>转化为键值对,并交给ServletContext.mvc

4.容器建立<listener></listener>中的类实例,即建立监听.该监听器必须实现自ServletContextListener接口

5.在监听中会有contextInitialized(ServletContextEvent event)初始化方法app

              在这个方法中得到ServletContext = ServletContextEvent.getServletContext();
            “context-param的值” = ServletContext.getInitParameter("context-param的键");
6.获得这个context-param的值以后,你就能够作一些操做了.注意,这个时候你的WEB项目尚未彻底启动完成.这个动做会比全部的Servlet都要早.换句话说,这个时候,你对<context-param>中的键值作的操做,将在你的WEB项目彻底启动以前被执行.
 
 
web.xml中能够定义两种参数:
    一个是全局参数(ServletContext),经过<context-param></context-param>
    一个是servlet参数,经过在servlet中声明        <init-param>
                                                                          <param-name>param1</param-name>
                                                                          <param-value>avalible in servlet init()</param-value>   
                                                                    </init-param> 
 
    第一种参数在servlet里面能够经过getServletContext().getInitParameter("context/param")获得
 
    第二种参数只能在servlet的init()方法中经过this.getInitParameter("param1")取得
 

2、spring上下文容器配置

  spring为咱们提供了实现ServletContextListener接口的上下文初始化监听器:org.springframework.web.context.ContextLoaderListener

  spring为咱们提供的IOC容器,须要咱们指定容器的配置文件,而后由该监听器初始化并建立该容器。要求你指定配置文件的地址及文件名称,必定要使用:contextConfigLocation做为参数名称。

复制代码
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml,/WEB-INF/action-servlet.xml,/WEB-INF/jason-servlet.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
复制代码

该监听器,默认读取/WEB-INF/下的applicationContext.xml文件。可是经过context-param指定配置文件路径后,便会去你指定的路径下读取对应的配置文件,并进行初始化。

3、spring上下文容器配置后,初始化了什么?

  既然,ServletContext是由Servlet容器初始化的,那spring的ContextLoaderListener又作了什么初始化呢?

        一、servlet容器启动,为应用建立一个“全局上下文环境”:ServletContext
        二、容器调用web.xml中配置的contextLoaderListener,初始化WebApplicationContext上下文环境(即IOC容器),加载context-param指定的配置文件信息到IOC容器中。WebApplicationContext在ServletContext中以键值对的形式保存
        三、容器初始化web.xml中配置的servlet,为其初始化本身的上下文信息servletContext,并加载其设置的配置信息到该上下文中。将WebApplicationContext设置为它的父容器。
        四、此后的全部servlet的初始化都按照3步中方式建立,初始化本身的上下文环境,将WebApplicationContext设置为本身的父上下文环境。
 

 

       对于做用范围而言,在DispatcherServlet中能够引用由ContextLoaderListener所建立的ApplicationContext中的内容,而反过来不行。
       当Spring在执行ApplicationContext的getBean时,若是在本身context中找不到对应的bean,则会在父ApplicationContext中去找。这也解释了为何咱们能够在DispatcherServlet中获取到由ContextLoaderListener对应的ApplicationContext中的bean。

 

 4、spring配置时:<context:exclude-filter>的使用缘由,为何在applicationContext.xml中排除controller,而在spring-mvc.xml中incloud这个controller

    既然知道了spring的启动流程,那么web容器初始化webApplicationContext时做为公共的上下文环境,只须要将service、dao等的配置信息在这里加载,而servlet本身的上下文环境信息不须要加载。故,在applicationContext.xml中将@Controller注释的组件排除在外,而在dispatcherServlet加载的配置文件中将@Controller注释的组件加载进来,方便dispatcherServlet进行控制和查找。故,配置以下:
 
applicationContext.mxl中:
 <context:component-scan base-package="com.linkage.edumanage">
      <context:exclude-filter expression="org.springframework.stereotype.Controller"    type="annotation" /> 
 </context:component-scan>
 
spring-mvc.xml中:
  <context:component-scan base-package="com.brolanda.cloud"   use-default-filters="false"> 
      <context:include-filter expression="org.springframework.stereotype.Controller"    type="annotation" /> 
 </context:component-scan>

 

 

谈谈springmvc的优化

  上面咱们已经对springmvc的工做原理和源码进行了分析,在这个过程发现了几个优化点:

  1.controller若是能保持单例,尽可能使用单例,这样能够减小建立对象和回收对象的开销.也就是说,若是controller的类变量和实例变量能够以方法形参声明的尽可能以方法的形参声明,不要以类变量和实例变量声明,这样能够避免线程安全问题.

  2.处理request的方法中的形参务必加上@RequestParam注解,这样能够避免springmvc使用asm框架读取class文件获取方法参数名的过程.即使springmvc对读取出的方法参数名进行了缓存,若是不要读取class文件固然是更加好.

  3.阅读源码的过程当中,发现springmvc并无对处理url的方法进行缓存,也就是说每次都要根据请求url去匹配controller中的方法url,若是把url和method的关系缓存起来,会不会带来性能上的提高呢?有点恶心的是,负责解析url和method对应关系的ServletHandlerMethodResolver是一个private的内部类,不能直接继承该类加强代码,必需要该代码后从新编译.固然,若是缓存起来,必需要考虑缓存的线程安全问题.

相关文章
相关标签/搜索