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]