Spring註解@Autowired、@Resource、@Inject
Spring注入的方式有很多,下面介紹一下常用的幾個用於注入的註解
@Autowired
首先來看一下@Autowired註解的定義:
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
/**
* Declares whether the annotated dependency is required.
* <p>Defaults to {@code true}.
*/
boolean required() default true;
}
從原始碼可以看出,@Autowired註解可以用在以下幾個地方
- 構造方法
- 普通方法
- 方法引數
- 欄位
- 其他註解
@Autowired註解只有一個required屬性,預設為true,說明預設情況下是要求一定要有物件注入才行,要不然會報空指標異常。如果允許為null,可以設定required為false。
下面我們來通過一個例子看一下@Autowired在不同地方的注入:
public class Cat {
public String show(){
return "Cat";
}
}
public class Dog {
public String show(){
return "Dog";
}
}
public class Cattle {
public String show(){
return "Cattle";
}
}
@Configuration
public class ServiceConfig {
@Autowired
private Dog dog;
private Cat cat;
private Cattle cattle;
@Autowired
public ServiceConfig(Cattle cattle){
this .cattle = cattle;
}
@Autowired
public void setCat(Cat cat){
this.cat = cat;
}
public String getDesc(){
return dog.show() + "|" + cat.show() + "|" + cattle.show();
}
}
public class ServiceDemoMain {
public static void main(String[] args){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(Config.class, ServiceConfig.class);
context.refresh();
ServiceConfig config = context.getBean(ServiceConfig.class);
System.out.println(config.getDesc());
}
}
@Autowired註解預設是通過型別來注入的,假如同一個型別的物件存在多個,就會出現物件的歧義,Spring不知道該注入哪個物件了,如下:
@Service
public class UserServiceImpl implements UserService {
}
@Service
public class UserServiceImpl2 implements UserService {
}
public class Test{
@Autowired
private UserService userService;
}
如何解決多個物件的歧義性呢? 1.通過名稱注入,配置Qualifier註解通過名稱來注入,通過給不同的物件設定不同的名稱來實現注入
@Service("UserServiceImpl")
public class UserServiceImpl implements UserService {
}
@Service("UserServiceImpl2")
public class UserServiceImpl2 implements UserService {
}
public class Test{
@Autowired
@Qualifier("UserServiceImpl")
private UserService userService;
}
2.使用@Primary註解來標識,假如有同一個型別的多個物件存在,會優先注入@Primary標記的物件。注意:同一個型別的物件,只能有一個用@Primary來標記,如果多個物件都標識了@Primary,還是會存在歧義的問題
@Service
public class UserServiceImpl implements UserService {
}
@Service
@Primary
public class UserServiceImpl2 implements UserService {
}
public class Test{
@Autowired
private UserService userService;
}
@Resource
- @Resource預設是通過name來注入的,有兩個重要屬性可以指定:name和type
- 預設情況下,既不指定name,也不指定type,會預設按照name來查詢匹配的物件,當有唯一的物件則進行注入;若沒有,則嘗試用type來進行匹配,匹配到唯一物件,則進行注入
- 指定name屬性,則用name來進行查詢,沒有則直接拋異常
- 指定type屬性,則用type來進行匹配,沒有則直接拋異常
@Configuration
public class Config {
@Bean(name="userServiceImpl")
public UserService getUserServiceImpl(){
return new UserServiceImpl();
}
@Bean(name="userServiceImpl2")
public UserService getUserServiceImpl2(){
return new UserServiceImpl2();
}
}
/**
* 預設通過名稱來注入
**/
@Resource
private UserService userServiceImpl;
@Resource
private UserService userServiceImpl2;
當名稱沒有匹配時,則用型別來進行匹配
@Configuration
public class Config {
@Bean(name="userServiceImpl")
public UserService getUserServiceImpl(){
return new UserServiceImpl();
}
}
/**
* userServiceImpl2通過名稱沒有匹配上時,則用型別進行匹配,兩個物件都注入userServiceImpl
**/
@Resource
private UserService userServiceImpl;
@Resource
private UserService userServiceImpl2;
@Inject
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Inject {
}
從原始碼可以看出@Inject可以用在普通方法、構造方法、欄位上,@Inject沒有required屬性,物件為空會拋異常
- @Inject是JSR330 (Dependency Injection for Java)中的規範,需要匯入javax.inject.Inject
- @Inject與@Autowired一樣,預設是按照型別來實現注入的,型別注入沒有找到則用名稱去匹配,只是@Autowired是Spring提供的,@Inject是J2EE提供的
- @Inject通過型別匹配到多個物件時,跟@Autowired一樣會產生歧義,不知道該注入哪一個物件,當出現歧義時配合@Named註解按名稱來進行注入
@Inject
@Named("userServiceImpl")
private UserService userService;
@Bean(name="userServiceImpl")
public UserService getUserServiceImpl(){
return new UserServiceImpl();
}
@Bean(name="userServiceImpl2")
public UserService getUserServiceImpl2(){
return new UserServiceImpl2();
}
@Inject通過型別注入沒有找到唯一的物件後,還會通過名稱匹配,匹配上也是可以成功注入的
/**
* 測試發現,這樣也可以注入成功,說明@Inject不光通過型別來注入,也會通過屬性名userServiceImpl2來進行注入
**/
@Inject
private UserService userServiceImpl2;
@Bean(name="userServiceImpl")
public UserService getUserServiceImpl(){
return new UserServiceImpl();
}
@Bean(name="userServiceImpl2")
public UserService getUserServiceImpl2(){
return new UserServiceImpl2();
}
@Autowired、@Resource、@Inject三者間的區別
(1)、@Autowired預設是按照型別裝配注入的,型別沒有注入成功,會用物件名稱來進行注入,預設情況下它要求依賴物件必須存在如果允許為null,可以設定它required屬性為false,如果想按照自定義名稱來進行注入,則需要結合@Qualifier一起使用;@Inject與@Autowired一樣,只是@Inject沒有required屬性,@Inject想按照自定義名稱來進行注入需結合@Named註解使用,當然結合@Qualifier也可以
(2)、@Resource預設按照名稱裝配,當找不到與名稱匹配的bean才會按照型別裝配,可以通過name屬性指定,如果沒有指定name屬 性,當註解標註在欄位上,即預設取欄位的名稱作為bean名稱尋找依賴物件,當註解標註在屬性的setter方法上,即預設取屬性名作為bean名稱尋找 依賴物件.注意:如果沒有指定name屬性,並且按照預設的名稱仍然找不到依賴的物件時候,會按照型別裝配,但一旦指定了name屬性,就只能按照名稱裝配了.
(3)、@Resource、@Inject註解是由J2EE提供,@Resource是JSR-250提供的註解,@Inject是JSR-330提供的註解,而@Autowired是由spring提供;
(4)、@Resource、@Autowired、@Inject都可以書寫標註在欄位或者該欄位的setter方法之上