關於spring的context:component-scan

前言

在使用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>

這樣也是可行的

發佈了88 篇原創文章 · 獲贊 29 · 訪問量 29萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章