在將spring與springMVC結合使用時,當我們使用注解的時候,一般都是在spring配置文件中配置注解掃描dao層、service層的包,在springMVC配置文件中配置注解掃描controller,自己在練習spring+SpringMVC+mybatis的項目時對這種做法一知半解,所以在練習項目的時候在實踐中對自己的一些想法進行了驗證。
- 一般的配置
- spring配置文件中
<?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:aop="http://www.springframework.org/schema/aop" 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-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <!-- 啟用注解 --> <context:annotation-config /> <!-- 設置使用注解的類所在的包 --> <context:component-scan base-package="dao,service.*"></context:component-scan> <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:config/jdbc.properties</value> </list> </property> </bean> <!-- c3p0連接池配置 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <!-- 四大參數的name不能換,和連接池的構造函數有關--> <property name="driverClass" value="${jdbc.driverClassName}"/> <property name="jdbcUrl" value="${jdbc.url}" /> <property name="user" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <!--連接池中保留的最大連接數。默認值: 15 --> <property name="maxPoolSize" value="20"/> <!-- 連接池中保留的最小連接數,默認為:3--> <property name="minPoolSize" value="2"/> <!-- 初始化連接池中的連接數,取值應在minPoolSize與maxPoolSize之間,默認為3--> <property name="initialPoolSize" value="2"/> <!--最大空閑時間,60秒內未使用則連接被丟棄。若為0則永不丟棄。默認值: 0 --> <property name="maxIdleTime" value="60"/> <!-- 當連接池連接耗盡時,客戶端調用getConnection()后等待獲取新連接的時間,超時后將拋出SQLException,如設為0則無限期等待。單位毫秒。默認: 0 --> <property name="checkoutTimeout" value="3000"/> <!--當連接池中的連接耗盡的時候c3p0一次同時獲取的連接數。默認值: 3 --> <property name="acquireIncrement" value="2"/> <!--定義在從數據庫獲取新連接失敗后重復嘗試的次數。默認值: 30 ;小於等於0表示無限次--> <property name="acquireRetryAttempts" value="0"/> <!--重新嘗試的時間間隔,默認為:1000毫秒--> <property name="acquireRetryDelay" value="1000" /> <!--關閉連接時,是否提交未提交的事務,默認為false,即關閉連接,回滾未提交的事務 --> <property name="autoCommitOnClose" value="false"/> <!--c3p0全局的PreparedStatements緩存的大小。如果maxStatements與maxStatementsPerConnection均為0,則緩存不生效,只要有一個不為0,則語句的緩存就能生效。如果默認值: 0--> <property name="maxStatements" value="100"/> <!--maxStatementsPerConnection定義了連接池內單個連接所擁有的最大緩存statements數。默認值: 0 --> <property name="maxStatementsPerConnection" value="0"/> </bean> <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <!-- 配置mybatis配置文件 --> <property name="configLocation"> <value>classpath:config/sqlMapConfig.xml</value> </property> <!-- 實體類映射文件路徑,可以在這里直接配置,也可以在configLocation配置的文件中配置 ibatis2不支持 --> <!-- <property name="mapperLocations" value="classpath:config/sqlmap/*.xml" /> --> </bean> <!-- Dao --> <bean id="ibatisDao" class="dao.IbatisDao"> <property name="dataSource" ref="dataSource"></property> <!-- 這里的dao繼承SqlMapClientDaoSupport,所以要顯示的配置注入 sqlMapClient--> <property name="sqlMapClient"> <ref bean="sqlMapClient"/> </property> </bean> <!-- 開啟事務注解 --> <tx:annotation-driven transaction-manager="transactionManager"/> <!-- 定義事務管理器(聲明式的事務) --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> </beans>
這里我們配置了事務管理器和基本的dao。service層采用的是注解注入
- springMVC配置文件中:
<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <!-- 啟用spring mvc 注解 --> <context:annotation-config /> <!-- 設置使用注解的類所在的包 --> <context:component-scan base-package="controller"></context:component-scan> <!-- 完成請求和注解POJO的映射 3.1后已經過時 與DefaultAnnotationHandlerMapping配套使用 <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />--> <!-- 新的注解映射--> <!-- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/> --> <!-- 使用新的注解映射必須指定 RequestMappingHandlerMapping去替代過時的DefaultAnnotationHandlerMapping--> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/> <!-- 使用ajax向前台返回json需要在注解映射中添加轉換器 --> <!-- 依賴包為jackson-core 、 jackson-annotation、 jackson-databind --> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="messageConverters"> <list> <ref bean="jsonHttpMessageConverter"/> </list> </property> </bean> <bean id="jsonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" /><!-- 用於jackson2 --> <!-- class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/> --> <!-- 對轉向頁面的路徑解析。prefix:前綴, suffix:后綴 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/page/" p:suffix=".jsp" /> </beans>
- spring配置文件中
springMVC配置文件中配置了指定的HandlerAdapter和HandlerMapping以及Resolver。
在這種配置下,項目能正常運行,配置了@Transactional的service中的事務也能被我們的事務管理器管理。
- 只在spring配置文件中啟用注解掃描(關閉springmvc掃描),掃描dao、service、controller。
這種情況下,項目啟動正常,但是無法處理我們的請求,鑒於springmvc容器是spring容器的子容器,我猜想由於controller中的所有請求處理方法我都是采用@requestMapping來進行配置的,requestMapping都是由我們的springMVC配置文件中配置的HandlerAdapter和HandlerMapping以及Resolver的Bean來進行處理(即使我們不在這里面配置,spingmvc也有默認的),但是父容器又不能訪問子容器的bean,所以即使掃描了controller包也無法將controller的處理方法與我們的請求正確mapping,所以這里如果僅僅在spring中掃描所有包,項目是無法實現正常運轉的。 - 只在spring配置文件中啟用注解掃描,掃描dao、service、controller,並且在spring配置文件中配置springmvc的HandlerAdapter和HandlerMapping以及Resolver。
這種情況項目與我們一般配置的效果一樣,這種情況就相當於我們在spring中也完成了spingmvc的配置,將原本應該在springmvc容器中的bean直接在spring中配置,雖然效果是一樣的,但是不建議這種做。這種配置的結果也代表我們上一種情況出現的原因的猜想是對的,spring父容器無法訪問springmvc中的bean,無法將掃描到的controller與請求url正確適配。 - 在springMVC配置文件中啟用注解掃描(關閉spring掃描),掃描dao、service、controller。
這種情況下項目能正常跳轉,但是我們通過@Transactional配置的service事務失效,這里是因為spring容器配置了事務管理器后,裝配的service都是經過cglib代理過的實例,具有事務功能,而springmvc容器裝配的service是沒有進行代理處理的,所以是沒有事務功能的,這一點可以參見這篇博客http://blog.csdn.net/haoyifen/article/details/51172647。這里我試着在springMVC配置文件中也配置了一個事務管理器過后,發現一切正常了,由springmvc裝配的service同樣也具備事務功能,這里就很奇怪,為什么父容器配置的事務管理器子容器無法使用呢?父容器中的bean子容器都是可以訪問的,為什么沒有使用呢?
到這里差不多可以回答開始的問題了,為什么要在spring中掃描dao、service,因為通過spring容器掃描dao以及service可以進行事務的增強,如果你僅僅在一個子容器中進行事務的增強那么其他的serviceBean是不會被事務增強的(比如第四種情況中,如果我們在spring容器中手動配置一個bean,那么這個bean不是由springmvc裝配的,而我們的事務管理器在springmvc中,這個bean就不具備事務的功能)。而controller之所以要在springmvc中配置,因為spring容器無法訪問到springmvc容器的bean(HandlerAdapter和HandlerMapping以及Resolver等),無法完成請求與hander的正確適配,如果要強行實現,在spring中配置HandlerAdapter和HandlerMapping以及Resolver則顯得太過牽強。在書上看到的一種說法是這樣也體現了分層的概念,dao、service等web層都需要使用的bean應該直接在spring容器中裝配,而controller這種則放在專門處理請求跳轉的springmvc容器中,相對於將所有bean配置在spring容器中,也體現了權限控制的思想吧。