一、區(qū)別總結
1??
- @Autowired 是 Spring 自帶的删性,通過AutowiredAnnotationBeanPostProcessor類實現(xiàn)的依賴注入亏娜,Spring 屬于第三方的。
- @Resource 是 JSR250 規(guī)范的實現(xiàn)蹬挺,J2EE 的注解维贺,在javax.annotation包下。J2EE 是 Java 自己的巴帮,建議使用 @Resource 注解溯泣,以減少代碼和 Spring 之間的耦合。
- @Inject 是 JSR330 規(guī)范實現(xiàn)的榕茧,需要導入javax.inject.Inject jar包垃沦,才能實現(xiàn)注入。
2??
- @Autowired 可以作用在 constructor用押、method肢簿、parameter、annotation_type蜻拨、field 上池充。
- @Resource 可以作用 method、field缎讼、type 上收夸。
- @Inject 可以作用 constructor、method血崭、field 上卧惜。
3??
@Autowired 默認按照(byType)進行 bean 匹配。如果有多個類型一樣的 bean 候選者夹纫,需要配合 @Qualifier 按照(byName)進行裝配咽瓷。指定名稱后,如果 Spring IOC 容器中沒有對應的組件 bean 拋出 NoSuchBeanDefinitionException舰讹。也可以將該注解屬性 required 配置為 false忱详,找不到相應 bean 的時候,系統(tǒng)不再拋異常跺涤。
①@Resource 默認按照(byName)方式進行自動裝配匈睁,找不到再按 type 去匹配监透。如果有多個類型一樣的 bean 候選者,則可以通過 name 屬性指定進行注入航唆。
②指定了 name 或者 type 則根據(jù)指定的類型去匹配 bean胀蛮。
③指定了 name 和 type 則根據(jù)指定的 name 和 type 去匹配 bean,任何一個不匹配都會報錯糯钙。@Inject 是根據(jù)類型進行自動裝配的粪狼,如果需要按名稱進行裝配,則需要配合 @Named任岸。
4??@Autowired再榄、@Inject 用法基本一樣。不同的是 @Autowired 有一個 request 屬性享潜。
二困鸥、什么是注解
Annotation(注解)是 JDK1.5 及以后版本引入的。它可以用于創(chuàng)建文檔剑按,跟蹤代碼中的依賴性疾就,甚至執(zhí)行基本編譯時檢查。注解是以@注解名
在代碼中存在的艺蝴。根據(jù)注解參數(shù)的個數(shù)猬腰,可以將注解分為:標記注解、單值注解猜敢、完整注解三類姑荷。它們都不會直接影響到程序的語義,只是作為注解(標識)存在缩擂,可以通過反射機制編程實現(xiàn)對這些元數(shù)據(jù)(用來描述數(shù)據(jù)的數(shù)據(jù))的訪問鼠冕。另外,可以在編譯時選擇代碼里的注解是否只存在于源代碼級撇叁,或者它也能在 class 文件、或者運行時中出現(xiàn)(SOURCE/CLASS/RUNTIME)畦贸。
- @Component:通用的注解陨闹,可標注任意類為 Spring 組件。如果一個 bean 不知道屬于哪個層薄坏,可以使用 @Component 注解標注趋厉。
- @Repository:對應持久層即 Dao 層,主要用于數(shù)據(jù)庫相關操作胶坠。
- @Service:對應服務層君账,主要涉及一些復雜的邏輯,需要用到 Dao 層沈善。
- @Controller:對應 Spring MVC 控制層乡数,主要用于接受用戶請求并調用 Service 層返回數(shù)據(jù)給前端頁面椭蹄。
三、注解的效應
傳統(tǒng)的 Spring 做法是使用 .xml 文件來對 bean 進行注入或者是配置 AOP净赴、事務绳矩,這么做有兩個缺點:
- 如果所有的內容都配置在 .xml 文件中,會導致 .xml 文件過大玖翅;如果按需求分開 .xml 文件翼馆,又會導致 .xml 文件過多〗鸲龋總之這會使得配置文件的可讀性與可維護性變得很低应媚。
- 開發(fā)中,在 .java 文件和 .xml 文件之間不斷切換猜极,是一件麻煩的事中姜。同時這種思維上的不連貫也會降低開發(fā)的效率。
為了解決這兩個問題魔吐,Spring 引入了注解扎筒,通過@注解名
的方式,讓注解與 Java Bean 緊密結合酬姆,既大大減少了配置文件的體積嗜桌,又增加了 Java Bean 的可讀性與內聚性。
四辞色、@Autowired骨宠、@Resource、@Inject 和 @Service
1??看一個不使用注解的 Spring 示例相满,在這個示例的基礎上层亿,改成注解版本的,這樣也能看出使用與不使用注解之間的區(qū)別立美,先定義一個老師:
public class Teacher{
private String teacherName = "TW";
public String toString() {
return "TeacherName:" + teacherName;
}
}
再定義一個學生:
public class Student{
private String studentName = "SL";
public String toString() {
return "StudentName:" + studentName;
}
}
然后定義一個學校:
public class School{
private Teacher teacher;
private Student student;
public void setTeacher(Teacher teacher){
this.teacher = teacher;
}
public void setStudent(Student student){
this.student = student;
}
public Teacher getTeacher(){
return teacher;
}
public Student getStudent(){
return student;
}
public String toString(){
return teacher + "\n" + student;
}
}
Spring的配置文件這么寫:
<?xml version="1.0" encoding="UTF-8"?>
<bean id="school" class="com.wg.bean.School" >
<property name="teacher" ref="teacher" />
<property name="student" ref="student" />
</bean>
<bean id="teacher" class="com.wg.uu.Teacher" />
<bean id="student" class="com.wg.uu.Student" />
這是最初始的 .xml 配置匿又。
2??@Autowired
自動裝配。其作用是替代 Java 代碼里面的 getter/setter 與 bean 屬性中的 property建蹄。如果私有屬性需要對外提供的話碌更,getter 應當予以保留。引入 @Autowired 注解洞慎,先看一下 Spring 配置文件怎么寫:
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xmlns="http://www.springframework.org/schema/beans"
4 xmlns:context="http://www.springframework.org/schema/context"
5 xsi:schemaLocation="http://www.springframework.org/schema/beans
6 http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
7 http://www.springframework.org/schema/context
8 http://www.springframework.org/schema/context/spring-context-4.2.xsd">
9
10 <context:component-scan base-package="com.wg" />
11
12 <bean id="school" class="com.wg.bean.School" />
13 <bean id="teacher" class="com.wg.uu.Teacher" />
14 <bean id="student" class="com.wg.uu.Student" />
15
16 </beans>
注意第10行痛单,為了實現(xiàn) bean 的自動載入,必須配置 Spring 的掃描器劲腿。在 base-package 指明一個包:
<context:component-scan base-package=“com.wg”/>
表明com.wg包及其子包中旭绒,如果某個類的頭上帶有特定的注解@Component
、@Repository
、@Service
或@Controller
挥吵,就會將這個對象作為 Bean 注入進 Spring 容器重父。有了context:component-scan
,另一個context:annotation-config
標簽就可以移除掉蔫劣,因為已經(jīng)被包含進去了坪郭。
context:component-scan
提供兩個子標簽:context:include-filter
和context:exclude-filter
。各代表引入和排除的過濾脉幢。
看到第12行歪沃,原來 school 里面應當注入兩個屬性 teacher、student嫌松,現(xiàn)在不需要注入了沪曙。再看下,School.java 也很簡練萎羔,getter/setter 都可以去掉:
public class School{
@Autowired
private Teacher teacher;
@Autowired
private Student student;
public String toString(){
return teacher + "\n" + student;
}
}
這里@Autowired
注解的意思就是液走,當 Spring 發(fā)現(xiàn)@Autowired
注解時,將自動在代碼上下文中找到與其匹配(默認是類型匹配)的Bean贾陷,并自動注入到相應的地方去。
xml優(yōu)先原則
值得注意的是髓废,假如 .xml 文件 bean 里面有 property巷懈,而 School.java 里面卻去掉了屬性的 getter/setter,并使用@Autowired
注解標注這兩個屬性會怎么樣慌洪?答案是 Spring 會按照 xml 優(yōu)先的原則去 School.java 中尋找這兩個屬性的 getter/setter顶燕,導致的結果就是初始化 bean 報錯。
再假如此時把 .xml 文件的13行冈爹、14行兩行再給去掉涌攻,運行會拋出異常:
Exception in thread “main” org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘School’: Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.zxt.uu.Teacher com.zxt.bean.School.teacher; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.wg.uu.Teacher] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543) …
因為,@Autowired
注解要去尋找的是一個 Bean频伤,Teacher 和 Student 的 Bean 定義都給去掉了恳谎,自然就不是一個 Bean 了,Spring 容器找不到也很好理解憋肖。那么因痛,如果屬性找不到又不想讓 Spring 容器拋出異常,而就是顯示 null瞬哼,可以嗎婚肆?可以租副,其實異常信息里面也給出了提示坐慰,就是將@Autowired
注解的 required 屬性設置為 false 即可:
public class School{
@Autowired(required = false)
private Teacher teacher;
@Autowired(required = false)
private Student student;
public String toString(){
return teacher + "\n" + student;
}
}
此時,找不到 teacher、student 兩個屬性结胀,Spring 容器不再拋出異常而是認為這兩個屬性為 null赞咙。
@Autowired接口注入----->@Qualifier
上面僅僅只是注入一個 Java 類,比較簡單糟港。那么如果有一個接口攀操,有多個實現(xiàn),Bean 里引用的是接口名秸抚,又該如何速和?比如有一個 Person 接口:
public interface Person{
public String personName();
}
兩個實現(xiàn)類 Doctor 和 Police:
@Service
public class Doctor implements Person{
public String docName(){
return "Doctor person";
}
}
@Service
public class Police implements Person{
public String personName(){
return "Police person";
}
}
寫一個 PersonFactory,引用 Person:
@Service
public class PersonFactory{
@Autowired
private Person person;
public String toString(){
return person.personName();
}
}
Person 接口有兩個實現(xiàn)類剥汤,Spring 并不知道應當引用哪個實現(xiàn)類颠放,代碼報錯。這種情況通常有兩個解決辦法:
- 刪除其中一個實現(xiàn)類吭敢,Spring 會自動去 base-package 下尋找 Person 接口的實現(xiàn)類碰凶,發(fā)現(xiàn) Person 接口只有一個實現(xiàn)類,便會直接引用這個實現(xiàn)類鹿驼。
- 實現(xiàn)類就是有多個該怎么辦欲低?此時可以使用 @Qualifier 注解:
@Service
public class PersonFactory{
@Autowired
@Qualifier("Doctor")
private Person person;
public String toString(){
return person.personName();
}
}
注意 @Qualifier 注解括號里面的必須是 bean 的名字,即 Person 接口實現(xiàn)類的類名首字母小寫如“doctor”畜晰,否則會報錯砾莱。
3??@Resource
@Resource 注解作用與 @Autowired 非常相似。先看一下 @Resource:
@Service
public class School{
@Resource(name = "teacher")
private Teacher teacher;
@Resource(type = Student.class)
private Student student;
public String toString(){
return teacher + "\n" + student;
}
}
4??@Inject
- @Inject 是JSR330 (Dependency Injection for Java)中的規(guī)范舷蟀,需要導入javax.inject.Inject jar包恤磷,才能實現(xiàn)注入。
- @Inject 可以作用 constructor野宜、method扫步、field 上。
- @Inject 是根據(jù)類型進行自動裝配的匈子。如果需要按名稱進行裝配河胎,則需要配合 @Named;
簡單示例:
@Inject
private Car car;
指定加入BMW組件:
@Inject
@Named("BMW")
private Car car;
@Named 的作用類似 @Qualifier
5??@Service
使用@Service虎敦,可以更加簡化.xml文件配置游岳。因為Spring的配置文件里面還有12行~14行三個bean,應用Spring配置文件里面一個自動掃描的標簽其徙,可以把這三個bean也給去掉胚迫,增強Java代碼的內聚性并進一步減少配置文件。先看一下配置文件:
<context:component-scan base-package="com.wg" />
配置文件看起來特別清爽唾那。下面以School.java為例访锻,Teacher.java和Student.java同理:
@Service
public class School{
@Autowired
private Teacher teacher;
@Autowired
private Student student;
public String toString(){
return teacher + "\n" + student;
}
}
這樣,School.java在Spring容器中存在的形式就是"school",即可以通過ApplicationContext的getBean("school")
方法來得到School.java期犬。@Service注解河哑,其實做了兩件事情:
- 聲明School.java是一個bean。這點很重要龟虎,因為School.java是一個bean璃谨,其他的類才可以使用@Autowired將School作為一個成員變量自動注入。
- School.java在bean中的id是"school"鲤妥,即類名且首字母小寫佳吞。
如果不想用這種形式怎么辦,就想讓School.java在Spring容器中的名字叫做"School"棉安,可以的:
@Service
@Scope("prototype")
public class School{
@Autowired
private Teacher teacher;
@Autowired
private Student student;
public String toString(){
return "TeacherName:" + teacher + "\nStudentName:" + student;
}
}
這樣容达,就可以通過ApplicationContext的getBean(“School”)方法來得到 School.java 了。
因為 Spring 默認產(chǎn)生的 bean 是單例的垂券,如果不想使用單例花盐,xml 文件里面可以在 bean 里面配置 @Scope 屬性。注解也一 樣菇爪,配置 @Scope 即可算芯,默認是“singleton”即單例,“prototype”表示原型即每次都會 new 一個新的出來凳宙。
注意:
假如 school 包下有 Teacher熙揍、wg 包下也有 Teacher,它們二者都加了 @Service 注解氏涩,那么在 School.java 中即使明確表示要引用的是 wg 包下的 Teacher届囚,程序運行的時候依然會報錯。這是因為是尖,兩個 Teacher 都使用 @Service 注解標注意系,意味著兩個 bean 的名字都是“teacher”,那么在 School.java 中自動裝配的是哪個 Teacher 呢饺汹?不明確蛔添,因此,Spring容器會拋出BeanDefinitionStoreException兜辞,Caused by:
org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name ‘teacher’ for bean class [com.wg.uu.Teacher] conflicts with existing, non-compatible bean definition of same name and class [com.wg.school.Teacher]