天天看點

@Autowired注解、@Resource注解和@Service注解什麼是注解?注解的效應:

Annotation(注解)是JDK1.5及以後版本引入的。它可以用于建立文檔,跟蹤代碼中的依賴性,甚至執行基本編譯時檢查。注解是以‘@注解名’在代碼中存在的,根據注解參數的個數,我們可以将注解分為:标記注解、單值注解、完整注解三類。它們都不會直接影響到程式的語義,隻是作為注解(辨別)存在,我們可以通過反射機制程式設計實作對這些中繼資料(用來描述資料的資料)的通路。另外,你可以在編譯時選擇代碼裡的注解是否隻存在于源代碼級,或者它也能在class檔案、或者運作時中出現(SOURCE/CLASS/RUNTIME)。

傳統的Spring做法是使用.xml檔案來對bean進行注入或者是配置aop、事物,這麼做有兩個缺點:

1、如果所有的内容都配置在.xml檔案中,會導緻.xml檔案過大;如果按需求分開.xml檔案,又會導緻.xml檔案過多。總之這會使得配置檔案的可讀性與可維護性變得很低。

2、開發中,在.java檔案和.xml檔案之間不斷切換,是一件麻煩的事。同時這種思維上的不連貫也會降低開發的效率

為了解決這兩個問題,Spring引入了注解,通過"@XXX"的方式,讓注解與Java Bean緊密結合,既大大減少了配置檔案的體積,又增加了Java Bean的可讀性與内聚性。

先看一個不使用注解的Spring示例,在這個示例的基礎上,改成注解版本的,這樣也能看出使用與不使用注解之間的差別,先定義一個老師:

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"?>

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns="http://www.springframework.org/schema/beans"

xmlns:context="http://www.springframework.org/schema/context"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-4.2.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-4.2.xsd" default-autowire="byType">

</beans>

這是最初始的.xml配置。

顧名思義,就是自動裝配。其作用是替代Java代碼裡面的getter/setter與bean屬性中的property。如果私有屬性需要對外提供的話,getter應當予以保留。br/>引入@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.zxt" />

11

12 <bean id="school" class="com.zxt.bean.School" />

13 <bean id="teacher" class="com.zxt.uu.Teacher" />

14 <bean id="student" class="com.zxt.uu.Student" />

15

16 </beans>

注意第10行,為了實作bean的自動載入,必須配置spring的掃描器。

在base-package指明一個包:

<context:component-scan base-package="com.zxt"/>br/>表明com.zxt包及其子包中,如果某個類的頭上帶有特定的注解

@Component,@Repository,@Service或@Controller,就會将這個對象作為Bean注入進spring容器。有了<context:component-scan>,另一個<context:annotation-config/>标簽就可以移除掉,因為已經被包含進去了。

<context:component-scan>提供兩個子标簽:<context:include-filter>和<context:exclude-filter>各代表引入和排除的過濾。

看到第12行,原來school裡面應當注入兩個屬性teacher、student,現在不需要注入了。再看下,School.java也很簡練,把getter/setter都可以去掉:

public class School{br/>@Autowired

private Teacher teacher;br/>@Autowired

這裡@Autowired注解的意思就是,當Spring發現@Autowired注解時,将自動在代碼上下文中找到和其比對(預設是類型比對)的Bean,并自動注入到相應的地方去。

值得注意的是,假如.xml檔案bean裡面有property,School.java裡面卻去掉了屬性的getter/setter,并使用@Autowired注解标注這兩個屬性那會怎麼樣?答案是Spring會按照xml優先的原則去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.zxt.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)

at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)

at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:305)

at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)

at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:301)

at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:196)

at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772)

at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:835)

at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:537)

at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)

at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)

at com.xrq.test.MyTest.main(MyTest.java:13)

Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.zxt.uu.Teacher com.xrq.bean.School.teacher; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.zxt.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$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:571)

at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)

at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)

... 13 more

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.zxt.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.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1373)

at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1119)

at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014)

at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:543)

... 15 more

因為,@Autowired注解要去尋找的是一個Bean,Teacher和 Student的Bean定義都給去掉了,自然就不是一個Bean了,Spring容器找不到也很好了解。那麼,如果屬性找不到又不想讓Spring容器抛出異常,而就是顯示null,可以嗎?可以的,其實異常資訊裡面也給出了提示,就是将@Autowired注解的required屬性設定為false 即可:

@Autowired(required = false)

此時,找不到teacher、student兩個屬性,Spring容器不再抛出異常而是認為這兩個屬性為null。

上面的比較簡單,我們隻是簡單注入一個Java類,那麼如果有一個接口,有多個實作,Bean裡引用的是接口名,又該怎麼做呢?比如有一個Person接口:

public interface Person{

public String personName();br/>}

兩個實作類Doctor和Police:

@Service

public class Doctor implements Person{

public String docName(){

return "Doctor person";

public class Police implements Person{

public String personName(){

return "Police person";

寫一個PersonFactory,引用Person:br/>@Service

public class PersonFactory{br/>@Autowired

private Person person;

return person.personName();

Person接口有兩個實作類,Spring并不知道應當引用哪個實作類,代碼報錯。這種情況通常有兩個解決辦法:

1、删除其中一個實作類,Spring會自動去base-package下尋找Person接口的實作類,發現Person接口隻有一個實作類,便會直接引用這個實作類

2、實作類就是有多個該怎麼辦?此時可以使用@Qualifier注解:

@Qualifier("Doctor")

注意@Qualifier注解括号裡面的必須是Person接口實作類的類名,我之前試的時候一直以為是bean的名字,是以寫了"doctor",結果一直報錯。

@Resource注解作用與@Autowired非常相似。先看一下@Resource,直接寫School.java了:br/>@Service

@Resource(name = "teacher")

@Resource(type = Student.class)

return teacher + "\n" + student;br/>}

這是詳細一些的用法,說一下@Resource的裝配順序:

1、@Resource後面沒有任何内容,預設通過name屬性去比對bean,找不到再按type去比對

2、指定了name或者type則根據指定的類型去比對bean

3、指定了name和type則根據指定的name和type去比對bean,任何一個不比對都會報錯。

1、@Autowired預設按照byType方式進行bean比對,@Resource預設按照byName方式進行bean比對br/>2、@Autowired是Spring的注解,@Resource是J2EE的注解,根據導入注解的的包名就可以知曉。

Spring屬于第三方的,J2EE是Java自己的東西。是以,建議使用@Resource注解,以減少代碼和Spring之間的耦合。

使用@Service,可以更加簡化.xml檔案配置。因為spring的配置檔案裡面還有12行~14行三個bean,應用spring配置檔案裡面一個自動掃描的标簽,可以把這三個bean也給去掉,增強Java代碼的内聚性并進一步減少配置檔案。

先看一下配置檔案:

http://www.springframework.org/schema/context/spring-context-4.2.xsd">;

</beans>br/>配置檔案看起來特别清爽。下面以School.java為例,其餘的Teacher.java和Student.java都一樣:

這樣,School.java在Spring容器中存在的形式就是"school",即可以通過ApplicationContext的getBean("school")方法來得到School.java。@Service注解,其實做了兩件事情:

1、聲明School.java是一個bean。這點很重要,因為School.java是一個bean,其他的類才可以使用@Autowired将School作為一個成員變量自動注入。

2、School.java在bean中的id是"school",即類名且首字母小寫。

如果,我不想用這種形式怎麼辦,就想讓School.java在Spring容器中的名字叫做"School",可以的:br/>@Service

@Scope("prototype")

return "TeacherName:" + teacher + "\nStudentName:" + student;

這樣,就可以通過ApplicationContext的getBean("school")方法來得到School.java了。

這裡說下@Scope注解。因為Spring預設産生的bean是單例的,如果不想使用單例,xml檔案裡面可以在bean裡面配置scope屬性。注解也一 樣,配置@Scope即可,預設是"singleton"即單例,"prototype"表示原型即每次都會new一個新的出來。

注意:br/>假如school包下有Teacher、uu包下也有Teacher,它們二者都加了@Service注解,那麼在School.java中即使明确表示我要引用的是uu包下的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.zxt.uu.Teacher] conflicts with existing, non-compatible bean definition of same name and class [com.zxt.school.Teacher]