SpringMVC系列之容器详解【spring容器与springmvc容器的详细介绍】

一、spring容器(root容器、父容器)

Servlet容器(tomcat、jetty)启动时,使用ContextLoaderListener读取 web.xml中的contextConfigLocation全局参数,初始化spring容器,如果没有这 个参数,那么ContextLoaderListener会加载/WEBINF/applicationContext.xml文件; spring容器主要用于整合struts1、Struts2;

二、spring mvc容器(servlet容器、子容器)

使用DispatcherServlet加载;

三、spring容器和spring mvc容器的关系

  • 子容器可以访问父容器中的bean;
  • 父容器不能访问子容器的bean;
  • 两个容器不能使用同一个xml配置文件,一定要做bean的隔离

一张图来表示两者的关系:
SpringMVC系列之容器详解【spring容器与springmvc容器的详细介绍】_第1张图片
我们在配置过程中,通常是将两个容器进行bean的隔离。所谓bean的隔离,其实就是分别存储不同的bean类型对象。也就是在bean的声明时,有目的的将不同的bean配置在两个容器的xml文件中,防止所有的bean都在一个配置文件中,也就是一个容器里。

根据Spring官方给出的建议,我们在隔离两个容器时,一般将与web相关的内容配置到子容器springmvc容器中,也就是Servlet WebApplicationContext,例如:Controllers、ViewResolver、HanlderMapping等;将其他内容配置到父容器root容器中,也就是Root WebApplicationContext,例如:Services、Repositories等。

四、配置文件的具体书写

1. 先来看Root容器:

<?xml version="1.0" encoding="UTF-8"?>
<!--标准的spring的配置文件-->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://www.springframework.org/schema/tx
   http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
       http://www.springframework.org/schema/context
   http://www.springframework.org/schema/context/spring-context-4.3.xsd
       http://www.springframework.org/schema/aop
   http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        ">

    <context:component-scan base-package="com.golden">
        <!-- 扫描@Service和@Repository注解 -->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>

        <!-- 排除@Controller、@RestController、@ControllerAdvice【用于全局异常处理】 -->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
        <context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.RestController" />
        <context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice" />
    </context:component-scan>

    <!-- 1. 数据源 -->
    <!-- 驱动名称、链接地址、用户名、密码 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/test?serverTimezone=GMT" />
        <property name="username" value="root" />
        <property name="password" value="root" />
    </bean>

    <!-- 2. SqlSessionFactoryBean -->
    <!-- 属性: Bean别名、数据源、mapper位置、插件 【config.xml】-->
    <!-- typeAliasesPackage\dataSource\mapperLocations\plugins -->
    <bean name="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="mapperLocations" value="classpath:mappers/*.xml" />
        <property name="typeAliasesPackage" value="com.golden.bean" />
        <property name="plugins">
            <array>
                <!--5.0版本前是PageHelper-->
                <bean class="com.github.pagehelper.PageInterceptor">
                    <!-- 调用setProperties()方法 -->
                    <property name="properties">
                        <props>
                            <prop key="helperDialect">mysql</prop>
                            <prop key="resonable">true</prop>
                        </props>
                    </property>
                </bean>
            </array>
        </property>
    </bean>

    <!-- 3. MapperScannerConfigurer: 扫描mapper接口,创建代理类,并将代理类加载到spring容器中 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.golden.mapper" />
        <!--如果有多个FactoryBean的话,可以使用这种方式指定一下-->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
    </bean>

    <!-- aspectj方式配置声明式事务 -->
    <!-- 事务管理器 -->
    <bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- 事务的通知 -->
    <tx:advice id="txAdvice" transaction-manager="tx">
        <tx:attributes>
            <tx:method name="add*" propagation="REQUIRED" isolation="DEFAULT" rollback-for="Throwable"/>
            <tx:method name="insert*" propagation="REQUIRED" isolation="DEFAULT" rollback-for="Throwable"/>
            <tx:method name="update*" propagation="REQUIRED" isolation="DEFAULT" rollback-for="Throwable"/>
            <tx:method name="delete*" propagation="REQUIRED" isolation="DEFAULT" rollback-for="Throwable"/>
            <tx:method name="del*" propagation="REQUIRED" isolation="DEFAULT" rollback-for="Throwable"/>
        </tx:attributes>
    </tx:advice>

    <aop:config>
        <!-- 切入点 -->
        <aop:pointcut id="tx-point" expression="execution(* com.golden.service..*.*(..))" />
        <!-- 将事务通知与切入点关联起来 -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="tx-point" />
    </aop:config>

    <!--注解版事务-->
    <tx:annotation-driven transaction-manager="tx" />

</beans>


以上Root容器配置文件,里面包含了所有需要扫描的Bean,限定类型为:Service和Repository。像@Controller、@RestController、@ControllerAdvice注解,统统拦截住不去扫描,因为这些注解是接下来Web容器需要扫描的。由此可以看出,这些基础的Bean类型,在Root容器中扫描过,就需要在Web容器中剔除,属于有你没我的状态,以此来实现完全隔离。
初次之外,还配置了持久层的相关内容,例如:spring整合Mybatis的相关配置和事务管理器的配置。这些都不属于web相关内容,而是持久层的内容,所以配置在root容器中。

2. Web容器的配置

<?xml version="1.0" encoding="UTF-8"?>
<!--标准的spring的配置文件-->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://www.springframework.org/schema/mvc
   http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
       http://www.springframework.org/schema/context
   http://www.springframework.org/schema/context/spring-context-4.3.xsd">

    <!-- 容器隔离 -->
    <context:component-scan base-package="com.golden">
        <!--指定 spring mvc 扫描的注解-->
        <!--@Controller @RestController @ControllerAdvice-->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
        <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.RestController" />
        <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice" />

        <!--指定不扫描的注解-->
        <!--@Service  @Respository-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository" />
    </context:component-scan>

    <mvc:annotation-driven />

    <mvc:default-servlet-handler />

    <mvc:resources mapping="/pics/**" location="file:d:/upload/" />
    <mvc:resources mapping="/**" location="/static/" />

    <!--整合Thymeleaf-->
    <!--SpringResourceTemplateResovler-->
    <bean id="templateResolver" class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
        <!--5个属性-->
        <property name="prefix" value="/templates/" />
        <property name="suffix" value=".html" />
        <property name="templateMode" value="HTML" />
        <property name="characterEncoding" value="UTF-8" />
        <!-- 在开发环境中,将cacheable设置为false,进行热加载 -->
        <!--正式环境中,是true-->
        <property name="cacheable" value="false" />
    </bean>

    <bean id="templateEngine" class="org.thymeleaf.spring5.SpringTemplateEngine">
        <property name="templateResolver" ref="templateResolver"/>
    </bean>

    <bean class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
        <property name="templateEngine" ref="templateEngine"/>
        <!--如果不写这个,返回到页面的中文将乱码-->
        <property name="characterEncoding" value="UTF-8" />
    </bean>

    <!-- 文件上传解析器 -->
    <!-- id的值是multipartResolver -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 允许上传的文件大小:2MB -->
        <property name="maxUploadSize" value="2097152" />
    </bean>


    <!-- 配置拦截器 -->
    <!-- 配置登录拦截器 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <mvc:exclude-mapping path="/js/**" />
            <mvc:exclude-mapping path="/user/toLogin" />
            <mvc:exclude-mapping path="/user/login" />
            <bean class="com.golden.interceptor.LoginInterceptor" />
        </mvc:interceptor>
    </mvc:interceptors>

</beans>
  • Web容器中注册的Bean都是直接与web请求密切关联的,由最开始的@Controller @RestController @ControllerAdvice注解扫描器的配置;
  • 使用,Spring会默认帮我们注册处理请求,参数和返回值的类。主要是实现了以下两个接口:HandlerMapping与HandlerAdapter。
  • 使用,会在Spring MVC上下文中定义一个org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler,它会像一个检查员,对进入DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理,如果不是静态资源的请求,才由DispatcherServlet继续处理。一般Web应用服务器默认的Servlet名称是"default",因此DefaultServletHttpRequestHandler可以找到它。如果你所有的Web应用服务器的默认Servlet名称不是"default",则需要通过default-servlet-name属性显示指定:
  • 使用将静态资源的处理经由Spring MVC框架交回Web应用服务器处理。而更进一步,由Spring MVC框架自己处理静态资源,并添加一些有用的附加值功能。
    首先,允许静态资源放在任何地方,如WEB-INF目录下、类路径下等,你甚至可以将JavaScript等静态文件打到JAR包中。通过location属性指定静态资源的位置,由于location属性是Resources类型,因此可以使用诸如"classpath:"等的资源前缀指定资源位置。传统Web容器的静态资源只能放在Web容器的根路径下,完全打破了这个限制。
    其次,依据当前著名的Page Speed、YSlow等浏览器优化原则对静态资源提供优化。你可以通过cacheSeconds属性指定静态资源在浏览器端的缓存时间,一般可将该时间设置为一年,以充分利用浏览器端的缓存。在输出静态资源时,会根据配置设置好响应报文头的Expires 和 Cache-Control值。
    在接收到静态资源的获取请求时,会检查请求头的Last-Modified值,如果静态资源没有发生变化,则直接返回303相应状态码,提示客户端使用浏览器缓存的数据,而非将静态资源的内容输出到客户端,以充分节省带宽,提高程序性能。
  • 整合Thymeleaf,包括:SpringResourceTemplateResolver、SpringTemplateEngine、ThymeleafViewResolver
  • 文件上传解析器,用于前端文件上传时使用。
  • 各种拦截器。例如:登录请求拦截器,用来检测用户是否登录成功,如果没有登录则无权访问其他页面。需要提前将拦截器写好,在这里注册。

3. Web.xml配置文件的书写

上面配置了Root容器和Web容器,但是还有一项非常重要的工作没有做,那就是要去加载这两个容器的配置文件,否则我们只是把配置文件写好,而没有去执行,还是等同于没写。那么如何让springmvc框架去为我们加载配置文件呢,这就用到了springmvc框架的基础配置文件web.xml,这个配置文件是spring在启动时最先加载的配置文件,如果我们在这个配置文件中,由引入了咱们自己写的Root容器和Web容器的配置文件,那么springmvc框架在加载自己的web.xml时,会顺带着把我们定义的两个容器的配置文件也一起加载了,达到我们的目的。
那么这个Springmvc框架自带的,也是每次启动自动读取的Web.xml需要如何写呢?我们来看一下:

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring-root.xml</param-value>
  </context-param>

  <!--post请求的编码过滤器 在前 -->
  <filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
      <param-name>forceEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>
  <!--rest请求过滤器-->
  <filter>
    <filter-name>rest</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/</url-pattern>
  </filter-mapping>
  <filter-mapping>
    <filter-name>rest</filter-name>
    <url-pattern>/</url-pattern>
  </filter-mapping>

  <!-- listener 在后-->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <!-- 前端控制器 dispatcher-->
  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

上面配置文件中,先定义了标签,

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring-root.xml</param-value>
  </context-param>

这是context上下文的参数,也就是springmvc框架底层类中的参数,我们在这里使用标签就可以为底层的类参数传递一个自定义的值。我们这里设置的参数是contextConfigLocation,也就是root容器的配置文件地址,我们使用标签来指定文件的具体地址,值为:classpath:spring-root.xml,也就是去resources资源目录下找名为spring-root.xml文件,也就是我们刚才上面自己写的Root容器的配置,这样,springmvc在启动时,读到这里,就会接着去接在我们指定的Root容器的配置文件,加载完以后回来继续加载下面的内容。

接下来,定义了过滤器和监听器,这里过滤器与我们本篇文章讲述的内容并无关系,只是在这里为了告诉大家,如何在Web.xml中配置过滤器,尤其是上面几个标签的顺序,是固定的,不可以随便的更换。
监听器是必须配置的,因为只有配置了监听器,我们才能在springmvc框架加载的时候,将咱们自己写的Root容器和Web容器作为参数传递给springmvc框架的上下文环境,也就是只有这样,两个容器才能真正被springmvc框架加载到运行环境中,后续才能正常使用。如果没有监听器,是无法监听到合适的时机,将容器作为参数传递给底层对应方法的。关于两个容器底层的加载,我们下面的部分会专门介绍,这里只简单提一嘴。

最后,我们配置了DispatcherServlet,这是Springmvc容器的必备过滤器,这里我们在类方法中使用了标签,也就是给指定的方法传入初始化参数,也就是我们自定义的参数,那么这里,我们传入的参数名称是contextConfigLocation,值是classpath:spring-mvc.xml,瞄向的正是我们刚才自己配置的Web容器,所以当springmvc框架加载到这几行代码时,就会去读取我们指定的Web容器配置文件,将Web容器中的内容进行加载。这里必须强调,要使用标签将web容器的路径进行指定,否则web容器将无法加载,那我们的配置文件就等于没写。

五、springmvc底层如何加载容器?

上面的内容已经介绍完了spring容器和springmvc容器的隔离和装配。接下来的内容,是springmvc框架底层代码的剖析,我们来看一下,究竟两个容器是如何加载的?在什么时间,又是以何种形式?究竟在web.xml中配置了半天是在干啥?

你可能感兴趣的:(SpringMVC系列)