前言:因為好久沒有碰ssm組合式開發(fā)的東西了译仗,加上學的時候稍微咸魚了一點觉壶,好多東西其實都忘了髓需,打算抽時間一點一點的把這些內容撿起來玻褪。
這次我們來聊一聊context:component-scan注解肉渴,今天看到一段代碼,在配置文件aplicationContext.xml中看到<context:component-scan>
這個注解带射,代碼如下:
<context:component-scan base-package="com.hrms">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter>
</context:component-scan>
我們先來聊一聊這個配置的作用吧同规。
通常情況下,我們創(chuàng)建一個spring的項目,如果注冊bean對象是通過注解注冊而非配置文件的話券勺,在配置文件當中都會看到<context:component-scan>
這個注解绪钥,這個注解的作用大家想必都能反應過來,掃描包唄关炼,開啟注解掃描程腹。或者說更加準確的說儒拂,是注冊bean對象寸潦,當配置完這個標簽之后,spring就會自動掃描base-package
屬性下面的所有包社痛,如果掃描的包中有被@Service, @Controller, @Repository, @Component注解的類的話见转,根據spring控制反轉的功能,spring會把這些類注冊成bean蒜哀。
打個比方說:我在一個類上寫上@Component注解斩箫,spring就會將這個類注冊成一個bean對象,并放到容器中撵儿,bean對象默認的名字是類名的首字母小寫乘客,如果我寫上@Component(value="abc")
,那么bean對象的名字就是abc统倒,很好理解吧寨典。
啥?還不理解房匆?那我舉個栗子:(已明白的小伙伴請往下翻)
比方說我這里有一個實體類耸成,我想把它注入到spring的ioc容器當中:
public class User{
private String name;
private Inteager age;
}
用配置文件的方式我們應該怎么做呢?當然是在配置文件當中寫入下面的代碼:
<bean name="user" class="xx.xx.User"></bean>
然后要用這個對象的時候浴鸿,我需要先通過配置文件創(chuàng)建一個配置文件對象(例如通過ClassPathXmlApplicationContext
類去new一個配置對象)井氢,然后調用配置對象的getBean方法來獲取這個對象
是不是感覺挺麻煩的,我又不想向這個對象當中注入什么復雜的屬性岳链,甚至不用注入屬性花竞,那我為什么還要在配置文件當中去寫這樣一行代碼呢?于是spring官方提供了比較人性化的操作:
@Component
public class User{
private String name;
private Inteager age;
}
這樣通過一個注解即可完成注冊操作掸哑,后面需要用的時候在加上@Autowired
注解即可约急,便捷不少吧。當然這里的@Component
可以根據情況換成@Service
苗分、@Controller
厌蔽、@Repository
,默認情況下摔癣,只要有這四個屬性注解在類上奴饮,就相當于告訴Spring,這個類是需要它來管理的纬向。
- @Controller: 用于控制器層
- @Service: 用于服務層
- @Repository: 用于DAO
- @Component:用于其他組件
所以如果僅僅是在配置文件中寫<context:component-scan base-package="com.sparta.trans"/>
Use-default-filter
屬性此時為true時,那么會對base-package包或者子包下所有的java類進行掃描,并把匹配的java類注冊成bean戴卜。這種情況下可以發(fā)現掃描的力度還是非常大的
但是逾条,在SSM組合式開發(fā)當中,Controller控制層應當是由SpringMVC單獨管理的投剥,不應該交給spring管理师脂,原因是因為:SSM組合式開發(fā)當中,spring的配置文件與springmvc的配置文件分開加載薇缅,在spring容器初始化的時候危彩,會先加載(web.xml)<context-param>中的配置攒磨,之后再加載<servlet>中的<init-param>去加載前端控制器泳桦。加載springmvc的時候,如果掃描到@service
會重新加載這個service的bean(都是沒有aop配置事務控制的)娩缰,可能會覆蓋之前的service灸撰,導致service的事務失效。因此service層是不應該讓SpringMVC的配置文件加載的時候再加載一遍的拼坎,所以不能只寫上文中提到的單單開啟注解掃描的配置代碼
看到這里浮毯,小伙伴應該都明白了,這種做法可能導致事務失效泰鸡!oh my god债蓝!多么令人害怕的事情,不過不要緊盛龄,因為<context:component-scan>
這個標簽還有兩個子標簽饰迹,可以完美的解決這個問題,我們把目光再次回到本文開頭提到的代碼:
<context:component-scan base-package="com.hrms">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter>
</context:component-scan>
這里有一個<context:exclude-filter>
注解余舶,另外一個注解是<context:include-filter>
注解啊鸭,顧名思義,exclude即排除匿值,就是說赠制,我掃描的時候排除掉你這個注解,排除的注解我在后面通過expression
來指定(這里排除掉的是@Controller注解)挟憔,而include即包含钟些,我掃描的時候只加載被指定的注解注解過的類。相當于手機的黑名單和白名單绊谭,(這里注意如果使用白名單記得把use-default-filters
這個過濾器屬性設為false政恍,否則他會采用默認的過濾器,白名單就失效了)龙誊。這樣就可以完美的避免重復加載的問題抚垃,防止事務失效。
說完了作用,我們來看看這兩個注解的屬性鹤树,這兩個屬性都使用type和expression屬性一起協(xié)作來定義組件掃描策略:
過濾器類型 | 描述 |
---|---|
annotation | 過濾器掃描使用注解所標注的那些類铣焊,通過expression屬性指定要掃描的注解 |
assignable | 過濾器掃描派生于expression屬性所指定類型的那些類 |
aspectj | 過濾器掃描與expression屬性所指定的AspectJ表達式所匹配的那些類 |
custom | 使用自定義的org.springframework.core.type.TypeFliter實現類,該類由expression屬性指定 |
regex | 過濾器掃描類的名稱與expression屬性所指定正則表示式所匹配的那些類 |
- 再次強調一下:若使用
include-filter
去定制掃描內容罕伯,要在use-default-filters="false"的情況下曲伊,不然會“失效”,被默認的過濾機制所覆蓋
這樣我們就可以愉快的開始使用注解開發(fā)啦追他,也不用擔心事務失效的問題坟募。如果你想掃描什么包,或者不想掃描什么包邑狸,完全可以根據表格中的屬性進行定制懈糯,在不需要對bean對象進行復雜的屬性注入的時候,這種開發(fā)方式是簡潔且方便的单雾。(當然需要復雜的屬性注入的時候還是寫配置文件吧赚哗,不一定方便但絕對易讀簡潔)
參考博文: