前言
在使用maven中,多模塊依賴spring配置文件,出現一些問題,最後的辦法使用context:conmponent-scan來解決,具體我將在正文中詳細描述問題
正文
出現的問題
在使用maven中,我將項目簡單的三層模型分成了多模塊來做,比如,dao層,我設定一個模塊,service層設定一個模塊,controller層我設定爲一個模塊,在dao層中,我配置了spring的基本的數據源,註解掃描,事物,然後在service,我依賴與dao模塊,這樣我service模塊的spring配置文件就不需要配置dao模塊spring已經配置好的信息了。現在的問題是,service使用了dao模塊的spring註解掃描,而我的註解掃描定義如下
<context:component-scan base-package="org.persistent">
這樣出現的問題是,我service中的org.service包下的@service就無法掃描進去。
嗯,解決辦法很簡單,我把base-package屬性值放寬點,就org吧。
真蛋疼,出現下面的異常
Caused by: java.lang.IllegalArgumentException: @EnableAsync annotation metadata was not injected
這異常網上一搜就有解決辦法:你在beans.xml中的瀏覽包不能是org.springframework,你可以變成org..…都行。
可是我不想改包名了。怎麼辦? 我想到了context:include-filter,嗯,我嘗試寫了下面的配置
<context:component-scan base-package="org" use-default-filters="false">
<context:include-filter type="regex" expression="org.service.*"/>
<context:include-filter type="regex" expression="org.persistent.impl*"/>
</context:component-scan>
發現又報錯了,說注入的時候在IOC容器中找不到beanDefine,額,意思也就是說,我使用@Repository 的類,沒有將信息放到IOC中。這導致的原因是我的context:include-filter沒有匹配到這個org.persistent.impl包下的類中
很無語,這樣的錯誤讓我如何查資料? 無奈,自能看源碼了,一步一步debug。在看源碼的時候突然發現自己對context:component-scan認識還真是太少了。
讀源碼的收穫
1、base-package=”org”,spring會項目中所有org包下的類,包括jar
2、use-default-filters=”false” 想用自己的匹配方式,那麼一定不能忽略了這個false,這樣才能執行下面設置好的過濾方式,否則還是使用默認的規則
3、context:include-filter type=”regex” 的匹配方式是這樣子的:第一步找到的所有org下的類,與expression匹配,不管你是不是設置了@Compont 這種註解,而是按照我的匹配規則來,如果按照默認的掃描規則,它是對找到@Compont註解的類的。
說的第三條,我滿臉都是淚,寫一段代碼老是用bug來打擊我,我做了上面的配置以後
在org.persistent.impl包下還存在一個BasticDAO類,裏面有下面的反射處理
public BasticDAO() {
entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
照我第三條描述,上面的配置,會匹配BasticDAO,然後在IOC存放一個beanDefine的對象(這個類的信息),然後通過反射實例化這個Bastic,那麼泛型的具體類型就不會指定,從而導致錯誤,正常的能執行應該是
public class UserDAO extends BasticDAO<User> implements IUserDAO{
具體實例化UserDAO, 在BasticDAO中就指定了泛型參數對象爲User
我的解決辦法是,重寫建一個包,存放BasticDAO,我不和你玩了,我和org.persistent.impl包下的小夥伴一起玩
那麼上面的代碼到底出了什麼問題,調試了以後才發現,我正則寫錯了,這樣寫是不會匹配到的。
org.persistent.impl*
對於org.persistent.impl 星號後面是對多個l,這樣的包名才起作用,而這裏面 . 在正則中表示任意的一個字符串,所以,應該寫成
<context:include-filter type="regex" expression="org.persistent.impl.*"/>
星號表示,多個 . 就是impl 後面有多個任意的字符串組成,這樣才能匹配我期望的包。
呵,最後談談maven的學習吧,說到這個maven,非常不習慣它開發代碼方式,通過自己一點一點寫,也慢慢的理解了一些東西
1、多個模塊中,如果一個模塊依賴了另外一個模塊,你完全可以使用所依賴的那個模塊的配置文件
比如,我service模塊什麼spring配置文件都沒有,我使用單元測試的時候,做了如下配置
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:config/spring-persistent-test.xml")
@TransactionConfiguration(transactionManager="transactionManager",defaultRollback=true)
@Transactional
public class UserServiceTest {
@Resource(name="userService")
private IUserService userService;
@Test
public void exampleForUesrOperation() {
userService.exampleForUesrOperation(new String[] {"username","userid"}, new String[] {"username"}, new String[] {"username", "userage"},
new Object[] {"updateService",1}, new Object[] {"superman"}, new Object[] {"testServiceForSpring", "20"});
}
}
注意一下
@ContextConfiguration("classpath:config/spring-persistent-test.xml")
這裏竟然在classpath中找到了這個測試的xml,我在想應該是service依賴了dao模塊,就會在classpath中找到dao的配置文件
2、pom.xml中dependencyManagement、還有pluginManagement,這種帶Management是在父pom中定義,設定具體版本,在子模塊中引用這些定義好的東西
3、pom.xml中爲什麼要設置plugins? 我之前很不理解,我不設置一樣可以用。後面好像明白了,這樣的作用是,我可以不使用plugins一些默認的配置,我通過設置plugins 增加對該插件的一些自定義的設置,那麼在執行maven這些設置過的插件的時候,就可以往我設置到的方向走
存在多個context:component-scan
如果存在多個context:component-scan,那麼按照匹配的規則讀取指定的掃描路徑的類,封裝成BeanDefinition 存放到IOC中。
如我的項目中使用了springmvc,那麼存在一個掃描路徑
<context:component-scan base-package="org.controller" />
還存在一個spring的基本的掃描路徑
<context:component-scan base-package="org" use-default-filters="false">
<context:include-filter type="regex" expression="org.service.*"/>
<context:include-filter type="regex" expression="org.persistent.impl.*"/>
</context:component-scan>
其實也是一樣,加載到springmvc的配置文件,那麼根據springmvc配置的context:component-scan的包路徑,讀取註解下的class封裝成BeanDefinition,存放到IOC中,加載第二個配置文件的context:component-scan,那麼根據正則讀取匹配的class,存放到IOC中
我試了一下,我在一個配置文件下,用正則匹配掃描路徑
<context:component-scan base-package="org" use-default-filters="false">
<context:include-filter type="regex" expression="org.service.*"/>
<context:include-filter type="regex" expression="org.persistent.impl.*"/>
<context:include-filter type="regex" expression="org.action.controller.*"/>
</context:component-scan>
這樣也是可行的