菜鸟学Spring之——父子容器的概念

    技术2022-07-10  139

    Spring父子容器

    我们之前将所有的容器创建过程都放在了一个配置文件中(一个配置文件中整合了SpringMVC、Mybatis等等)这样做太凌乱了。如果有一个过程出错了,整个容器都会出错,这时就引入了父子容器,在启动的时候先启动除了Controller以外的所有的其他的类型对象,而在真正Servlet启动的时候只把Controller和Intercepter加载出来就行了。(所有的Handle都是Spring体系下的东西)

    Controller层属于Spring体系,Service层属于SpringMVC体系

    @Service,@Repository,@Autowired这些注解在没有SpringMVC的体系下都可以单独存在,这种可以单独存在的如果和SpringMVC配置在一个配置文件中就比较凌乱了。SpringMVC应该有他自己的容器,Spring有他自己的容器。Spring用来管理Mybatis,SpringMVC。

    真正启动一个SpringMVC项目的时候第一件事应该是启动一个Listener(监听器,只负责监听,不干遇项目的执行)。这Listener监听全局事务,当某个事务发生时会触发他的回调。Servlet是当某个请求发生时会触发他的回调。Filter是当一个请求过来时,他会在Servlet之前触发回调,并且决定Servlet是否执行

    Listener的配置

    <!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> <display-name>Archetype Created Web Application</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>spring</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-*.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>

    这时项目的启动顺序变为:先启动applicationContext容器,然后再启动spirng-mvc容器(原来是一口大锅,现在是鸳鸯锅了)

    接下来把我们之前创建的那一口大锅整理一下。

    原来的模样

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <context:component-scan base-package="com.lanou"></context:component-scan> <mvc:annotation-driven></mvc:annotation-driven> <mvc:resources mapping="/static/**" location="/static/"></mvc:resources> <!--这个数据源出来TransactionManager用还有SqlSessionFactory用--> <bean id="dataSource" class="org.apache.ibatis.datasource.pooled.PooledDataSource"> <property name="driver" value="com.mysql.cj.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/db_zbmanager?serverTimezone"></property> <property name="username" value="root"></property> <property name="password" value="zaq991221"></property> </bean> <!--这个sqlSessionFactory在创建的时候就一定符合transactionManager的管理规则--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"></property> <property name="mapperLocations" value="classpath:mapper/*.xml"></property> </bean> <bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.lanou.mapper"></property> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property> </bean> <!--配置数据元的事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置文件式编程 <!–对事物管理的时候需要有一个事务管理对象(transactionManager),且这个对象必须要放在Spring中,Spring对事务管理就是通过AOP增强来管理的–> <tx:advice id="interceptor" transaction-manager="transactionManager"> <tx:attributes> <!–针对哪些方法配置哪些事务特征–> <tx:method name="save*" rollback-for="java.lang.Exception" propagation="REQUIRED"/><!–这里配置的方法是为了让我们实现二次过滤和针对不同的方法开启不同的事务隔离级别和事务的传播特性–> <tx:method name="modify*" rollback-for="java.lang.Exception" propagation="REQUIRED"></tx:method> <tx:method name="remove*" rollback-for="java.lang.Exception" propagation="REQUIRED"></tx:method> <tx:method name="query*" rollback-for="java.lang.Exception" propagation="REQUIRES_NEW"></tx:method> <!–一般isolation就选择默认的,因为数据库都是根据大部分用户的需求定制的–> <!–timeout:超时时间–> <!–rollback-for:指当遇到这个异常就回滚–> <!–no-rollback-for:如果出现这个异常那就不回滚–> <!–propagation:传播特性(七种)–> </tx:attributes> </tx:advice> --> <!--注解式编程--> <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven> <!-- <aop:config>--> <!-- <aop:pointcut id="adv" expression="execution(* com.lanou.service..*.*(..))"/>--> <!--<!– <aop:advisor advice-ref="interceptor" pointcut-ref="adv"></aop:advisor> <!–将事务植入到对象的方法前面–>–>--> <!-- </aop:config>--> </beans>

    整理后的模样

    applicationContext.xml

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd "> <!--发现这里都是跟数据库相关的东西--> <context:component-scan base-package="com.lanou"> <!--表示扫描com.lanou包,除了下面的这两个注解的类--> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/> <context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.RestController"/> </context:component-scan> <!--这个数据源出来TransactionManager用还有SqlSessionFactory用--> <bean id="dataSource" class="org.apache.ibatis.datasource.pooled.PooledDataSource"> <property name="driver" value="com.mysql.cj.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/db_zbmanager?serverTimezone"></property> <property name="username" value="root"></property> <property name="password" value="zaq991221"></property> </bean> <!--这个sqlSessionFactory在创建的时候就一定符合transactionManager的管理规则--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"></property> <property name="mapperLocations" value="classpath:mapper/*.xml"></property> </bean> <bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.lanou.mapper"></property> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property> </bean> <!--配置数据元的事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置文件式编程 <!–对事物管理的时候需要有一个事务管理对象(transactionManager),且这个对象必须要放在Spring中,Spring对事务管理就是通过AOP增强来管理的–> <tx:advice id="interceptor" transaction-manager="transactionManager"> <tx:attributes> <!–针对哪些方法配置哪些事务特征–> <tx:method name="save*" rollback-for="java.lang.Exception" propagation="REQUIRED"/><!–这里配置的方法是为了让我们实现二次过滤和针对不同的方法开启不同的事务隔离级别和事务的传播特性–> <tx:method name="modify*" rollback-for="java.lang.Exception" propagation="REQUIRED"></tx:method> <tx:method name="remove*" rollback-for="java.lang.Exception" propagation="REQUIRED"></tx:method> <tx:method name="query*" rollback-for="java.lang.Exception" propagation="REQUIRES_NEW"></tx:method> <!–一般isolation就选择默认的,因为数据库都是根据大部分用户的需求定制的–> <!–timeout:超时时间–> <!–rollback-for:指当遇到这个异常就回滚–> <!–no-rollback-for:如果出现这个异常那就不回滚–> <!–propagation:传播特性(七种)–> </tx:attributes> </tx:advice> --> <!--注解式编程--> <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven> </beans>

    spring-mvc.xml

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd "> <context:component-scan base-package="com.lanou.controller"></context:component-scan> <mvc:annotation-driven></mvc:annotation-driven> <mvc:resources mapping="/static/**" location="/static/"></mvc:resources> </beans>

    这样项目启动的时候就会将大部分类的创建扔到了父容器中,而子容器还没有启动。当一个访问来子容器才启动,子容器的Controller才创建出来(只需要创建Controller就行了,别的东西就不会再创建了)

    这样容器启动的过程会变慢一点,但是运行速度会变快。

    这里需要注意一下,如果子容器扫描一个包,父容器也扫描相同的包,或者子容器创建一套数据库链接,父容器也创建一套数据库链接。这样会出大问题,父容器的事务链接过程会被子容器覆盖,同时覆盖掉原来在父容器中声明好的可以使用的事务管理过程,这时候事务就不会生效了。所以不要把在父容器中声明过的对象在子容器中在声明一遍,父子容器一定要把活分清。子容器就启动SpringMVC体系需要的东西,父容器加载为子容器服务的所有组件。

    日志系统应该放在父容器中,但是刚才我们整理的父容器放的都是和数据库有关的东西,我不想把日志系统加进去,怎么办?稍微改一下之前的配置

    web.xml

    <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext*.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>

    创建一个applicationContext*.xml文件,将日志系统和AOP过程放到这里。

    注意:这里的applicationContext.xml和applicationContext-log.xml是一个容器,他们和spring-mvc之间是两个容器。(listener启动了父容器,servlet启动了子容器)

    Processed: 0.015, SQL: 9