一、使用@Autowired註解,實現自動裝配
在使用註解裝配之前,首先要開啟註解裝配的方式,那就是在配置檔案中加上下面這句話<context:annotation-config></context:annotation-config>
,也可以使用<context:component-scan>
標籤(兩者的區別在Spring整理系列(01)——spring概念簡介、bean掃描與註冊實現方式中有提到)。
使用@Autowired註解,可以用在屬性、setter方法、constructor構造方法上,實現自動裝配。
spring的xml配置檔案:
<!-- 開啟註解配置 -->
<context:annotation-config></context:annotation-config>
<!-- 註冊bean -->
<bean id="userDao" class="com.jsun.test.springDemo.ioc.User.UserDaoImpl"></bean>
<!-- 註冊bean -->
<bean id="userService" class="com.jsun.test.springDemo.ioc.User.UserServiceImpl" ></bean>
UserServiceImpl:
1、註解在setter方法上:
private UserDao userDao;
//添加註解,匹配注入與引數型別有關,即byType,與引數名稱、setter方法名稱、成員變數屬性名稱無關
@Autowired
public void setUserDao(UserDao userDao) {
System.out.println("註解自動裝配setUserDao注入");
this.userDao = userDao;
}
2、constructor構造方法上:
private UserDao userDao;
//添加註解,匹配注入與引數型別有關,即byType,與引數名稱、成員變數屬性名稱無關
@Autowired
public UserServiceImpl(UserDao userDao){
System.out.println("註解自動裝配構造器UserServiceImpl注入");
this.userDao = userDao;
}
3、宣告的屬性上:
//添加註解,先按照屬性名稱匹配注入,如果未找到則按照屬性型別匹配注入,即byName-->byType
@Autowired
private UserDao userDao;
二、使用@Autowired丟擲異常情況及與@Qualifier註解結合使用情況
丟擲異常情況:
@Autowired使用的時候必須只有一個Bean適合裝配,否則spring會丟擲異常;
1、NoSuchBeanDefinitionException異常:如果在應用上下文當中找不到相應的bean去自動裝配,那麼spring也會丟擲異常。
。。。
Caused by: org.springframework.beans.factory.BeanCreationException: 。。。
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type。。。
要避免這種情況發生,而且需要裝配的屬性也不是必須要裝配的話,可以使用@Autowired(required=false)註解:
//在注入bean的時候該屬性不是必須的,即使找不到對應bean注入,也不會報異常
@Autowired(required=false)
private UserDao userDao;
2、NoUniqueBeanDefinitionException異常:如果在同一個spring容器中出現了兩個或多個都必須存在的bean,然而自動裝配只需要裝配期中一個,也會丟擲異常。
父類介面:
public interface TestBeanInterface {
public void sayHello();
}
兩個實現子類:
@Component("testBean1")
public class TestBean1 implements TestBeanInterface{
public void sayHello(){
System.out.println("TestBean1 sayHello...");
}
}
@Component("testBean2")
public class TestBean2 implements TestBeanInterface{
public void sayHello(){
System.out.println("TestBean2 sayHello...");
}
}
測試類中:
//以介面父類宣告的成員變數屬性,在spring容器中有它兩個子類的bean
private TestBeanInterface testBean;
//自動裝配,按照引數型別匹配注入bean,此時spring容器中TestBeanInterface型別有兩個bean
@Autowired
public void setTestBean(TestBeanInterface t){
this.testBean = t;
}
執行測試的異常:
。。。
Caused by: org.springframework.beans.factory.BeanCreationException:。。。
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException:。。。
這個時候@Autowired可以結合使用@Qualifier註解指定一個Bean來裝配:
@Autowired
public void setTestBean(@Qualifier("testBean2")TestBeanInterface t){
this.testBean = t;
}
注:@Qualifier註解其實就是先按照bean的name過濾裝配指定型別bean,把@Autowired放在成員變數屬性上,也是先按照屬性name查詢,再按型別注入,所以把@Autowired放在成員變數屬性上報NoUniqueBeanDefinitionException異常的概率比較低,並且不需要setter或構造器,推薦使用。
三、@Autowired可以裝配的spring內部bean,不可以裝配的bean
1、@Autowired可以註解的spring內部型別:
可以用@Autowired註解那些解析依賴性介面,比如BeanFactory、ApplicationContext、Environment、ResourceLoader、ApplicationEventPublisher、MessageSource等
@Autowired
private ApplicationContext context;
@Test
public void testGetBean(){
TestBeanInterface tb = (TestBeanInterface)context.getBean("testBean1");
System.out.println(tb.getClass().getName());
}
2、@Autowired不可以註解的型別:
因為@Autowired是由Spring BeanPostProcessor處理的,所以不能在自己的BeanPostProcessor或BeanFactoryPostProcessor型別應用@Autowired註解,這些型別必須通過xml或者@Bean註解形式載入。
四、@Autowired與JSR-250提供的@Resource註解的區別
1、先說@Autowired註解:
@Autowired是Spring提供的註解,需匯入Package:org.springframework.beans.factory.annotation.Autowired;
@Autowired用在setter和構造器上只按照byType注入,用在成員變數field上,先按照byName注入,找不到則按照byType注入;
結合@Qualifier註解,可讓setter和構造器實現byName匹配注入。
2、再說@Resource註解:
@Resource由J2EE提供,需匯入javax.annotation.Resource;
@Resource有兩個中重要的屬性:name和type ,Spring將name屬性解析為bean的名字,type屬性解析為bean的型別。所以如果使用name屬性,則按照byName自動注入,使用type屬性則按照byType自動注入。如果既不指定name也不指定type屬性,這時將通過反射機制預設使用byName自動注入。@Resource自動裝配注入順序如下:
(1). 如果同時指定了name和type,則從Spring上下文中找到唯一匹配的bean進行裝配,找不到則丟擲異常;
(2). 如果指定了name,則從上下文中查詢名稱(id)匹配的bean進行裝配,找不到則丟擲異常;
(3). 如果指定了type,則從上下文中找到型別匹配的唯一bean進行裝配,找不到或者找到多個,都會丟擲異常;
(4). 如果既沒有指定name,又沒有指定type,則自動按照byName方式進行裝配;如果沒有匹配,則回退為一個原始型別進行匹配,如果匹配則自動裝配。
@Resource分別可以用在field、setter上,無法應用在constructor構造器上
//用於field欄位
@Resource
private TestBeanInterface testBean;
//用於setter
@Resource(name = "testBean1")
public void setTestBean(TestBeanInterface t){
this.testBean = t;
}
注:@Resource放在setter方法上,setter方法的引數必須是一個引數,如果是多引數,則丟擲如下異常:
...
org.springframework.beans.factory.BeanCreationException: Error creating bean with name ...
...
Caused by: java.lang.IllegalStateException: @Resource annotation requires a single-arg method:...
如果需要多引數setter或者構造器注入,還需要替換為@Autowired結合@Qualifier實現。
3、補充:集合或Map型別bean,無法通過@Autowired註解注入,因為沒有型別匹配到這樣的bean,必須通過@Resource註解注入,通過唯一名稱引用集合或Map型別bean
五、JSR-330註解@Inject、@Name使用介紹
1、spring3.0開始支援JSR330標註註解,使用JSR330需要引入javax.inject包,使用maven引入:
<!-- 引入對JSR330的註解支援包 -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
2、@Inject:
等效於@Autowired,可以用於成員變數屬性、setter方法、構造器,包括匹配注入情況和丟擲異常情況,可以互換使用,程式碼例項參照上面@Autowired註解。
3、@Named:
如果想使用特定名稱進行依賴注入,可以使用@Named,此時與@Quarlifier註解作用相同。
@Inject
private UserDao userDao1;
@Autowired
@Named("userDao2")
private UserDao userDao2;
@Inject
@Named("userDao2")
public void setUserDao2(UserDao ud2){
this.userDao2 = ud2;
}
@Inject
public UserServiceImpl(@Named("userDao1")UserDao ud1){
this.userDao1 = ud1;
}
用在類上,與@Component註解作用是等效的。
@Named("tb")
public class TestBean {
@PostConstruct
public void start(){
System.out.println("TestBean 初始化。。。");
}
@PreDestroy
public void cleanUp(){
System.out.println("TestBean 銷燬。。。");
}
}