最近接觸了幾個比較大的Java項目,其中經常存在Bean之間的依賴,例如在某服務啓動前要初始化各種詞典,主要方式有顯式聲明Bean和隱式註解注入2種,本文着重介紹後一種。java
首先咱們定義「依賴」是什麼:若是在Class A中有Class B的實例,則稱A依賴B。
如今咱們來構造一個依賴:數據庫
public class UserDao { DBConnection conn; public UserDao() { // 先初始化數據庫鏈接 conn = new DBConnection(); } }
上述代碼中,UserDao
即對DBConnection
存在依賴。但這樣構造依賴存在以下問題:
(1) 耦合度高。設想在將來咱們爲DBConnection
的構造函數新增字段name
,那麼上述代碼將修改成:conn = new DBConnection();
conn = new DBConnection(String name);
(2) 若是DBConnection
的初始化耗時很長,將影響構造函數的後續初始化工做。express
上述UserDao
在構造函數裏直接初始化DBConnection
的過程,屬於主動初始化依賴對象,兩者耦合度高,不方便測試。若是被依賴對象事先就初始化好了,直接供給本Class調用,則稱之爲依賴注入。例如上述代碼可改寫爲以下:app
public class UserDao { @Autowired //依賴注入 DBConnection conn; public UserDao() { } }
要使注入生效,咱們還須要在DBConnection
中添加Bean標識Component
函數
@Component //將類標識爲Bean public class DBConnection { public DBConnection() { init(); //初始化工做 } }
同時,爲了讓Bean的標識被探測到,咱們還須要在xml配置文件中添加自動掃描:測試
<context:component-scan base-package="org.example.service"/>
到此處相信讀者大體瞭解其注入原理:code
@Component
的標識,使之成爲Bean。@Autowired
對Bean進行依賴注入。 事實上咱們能夠在任何狀況下使用@Component
標識須要被"Bean化"的服務。但不久咱們會發現,其餘更有水平的代碼裏會有更多富於變化的標識,例如:@Repository
、@Service
和@Controller
。這是怎麼回事?設想咱們如今有加載本地詞典的類A,本地詞典監控類B,兩者有明顯區別:前者的功能視數據訪問,後者則是監控服務,咱們更願意對標識作更精細化的區分,具體以下:
@Repository
是爲DAO(數據訪問)特製的聲明,將一個類聲明爲Repository
意味着該類主要功能是數據讀取、DB訪問。
@Service
則聲明該類爲服務性質的Bean,例如上文中的詞典監控服務,還有許多無關數據操做的功能性初始化類,都應該被標識爲Service
。
@Controller
聲明標誌着一個類是SpringWeb的MVC控制器,主要負責MVC之間的操縱。另外一個相似的聲明是@RequestMapping,將URL映射爲一個方法。component
以上3個新的聲明均爲@Component
的子集,所以僅僅使用@Component
也不影響後續的掃描和注入過程。但在現實生活中,其實只有不多的場合咱們須要用到@Component
,例如沒法給類作清晰的定位時。xml
<context:component-scan base-package="org.example.service"/>
注意到上文中的這段自動掃描,它的包名其實能夠根據用戶需求作出更改。例如:如今我有一個判斷query是否爲髒詞的服務A,它僅依賴注入了自動加載髒詞詞典的類B。此外,項目中還存在訪問數據庫的類C,但咱們沒有爲數據庫配置訪問信息。當咱們將註解掃描範圍設置爲整個項目時,啓動服務A將產生問題。對象
解決方案是縮小自動掃描範圍,使得Spring僅掃描B而不掃描C。具體作法是增長掃描過濾器, Spring支持正則Regex和AspectJ兩種表達式的方式進行掃描過濾。
例如:
<context:component-scan base-package="org.example.service"> <context:include-filter type="regex" expression="org\.example\.service\.dictionary\..*"/> <context:exclude-filter type="aspectj" expression="org.example.service.db..*"/> </context:component-scan>
上面的xml配置就成功的掃描名爲com.example.service.dictionary
的package而忽略了以org.example.service.db
爲前綴的package。
@Autowired
:負責自動裝配被成功掃描的Bean。@Qualifier("xxxxx")
: 有時候咱們有參數不一樣的兩個相同類的bean須要裝配(具體爲何會存在這種現象?可想象要鏈接2個數據庫。),那麼如何單獨指定我須要裝配哪個呢?@Qualifier
配合@Autowired
可用於消除這類歧義。
在方法內部,咱們也有一些"註解":@PostConstruct
和@PreDestroy
是Bean內分別執行初始化和銷燬bean的註解。b>注意!它並不屬於Spring,而是屬於J2ee裏的jar包,所以使用時須要在xml配置:
<context:annotation-config />
@PostConstruct
: 初始化bean@preDestroy
: 結束前清理。