什么是注解
傳統(tǒng)的Spring做法是使用.xml文件來對bean進(jìn)行注入或者是配置aop、事物,這么做有兩個缺點(diǎn):
1、如果所有的內(nèi)容都配置在.xml文件中,那么.xml文件將會十分龐大;如果按需求分開.xml文件,那么.xml文件又會非常多。總之這將導(dǎo)致配置文件的可讀性與可維護(hù)性變得很低
2、在開發(fā)中在.java文件和.xml文件之間不斷切換,是一件麻煩的事,同時這種思維上的不連貫也會降低開發(fā)的效率
為了解決這兩個問題,Spring引入了注解,通過"@XXX"的方式,讓注解與Java Bean緊密結(jié)合,既大大減少了配置文件的體積,又增加了Java Bean的可讀性與內(nèi)聚性。
本篇文章,講講最重要的三個Spring注解,也就是@Autowired、@Resource和@Service,希望能通過有限的篇幅說清楚這三個注解的用法。
不使用注解
先看一個不使用注解的Spring示例,在這個示例的基礎(chǔ)上,改成注解版本的,這樣也能看出使用與不使用注解之間的區(qū)別,先定義一個老虎:
1
2
3
4
5
6
7
8
9
|
public class Tiger { private String tigerName = "TigerKing" ; public String toString() { return "TigerName:" + tigerName; } } |
再定義一個猴子:
1
2
3
4
5
6
7
8
9
|
public class Monkey { private String monkeyName = "MonkeyKing" ; public String toString() { return "MonkeyName:" + monkeyName; } } |
定義一個動物園:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
public class Zoo { private Tiger tiger; private Monkey monkey; public void setTiger(Tiger tiger) { this .tiger = tiger; } public void setMonkey(Monkey monkey) { this .monkey = monkey; } public Tiger getTiger() { return tiger; } public Monkey getMonkey() { return monkey; } public String toString() { return tiger + "\n" + monkey; } } |
spring的配置文件這么寫:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<? 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" > < bean id = "zoo" class = "com.xrq.bean.Zoo" > < property name = "tiger" ref = "tiger" /> < property name = "monkey" ref = "monkey" /> </ bean > < bean id = "tiger" class = "com.xrq.domain.Tiger" /> < bean id = "monkey" class = "com.xrq.domain.Monkey" /> </ beans > |
都很熟悉,權(quán)當(dāng)復(fù)習(xí)一遍了。
@Autowired
@Autowired顧名思義,就是自動裝配,其作用是為了消除代碼Java代碼里面的getter/setter與bean屬性中的property。當(dāng)然,getter看個人需求,如果私有屬性需要對外提供的話,應(yīng)當(dāng)予以保留。
因此,引入@Autowired注解,先看一下spring配置文件怎么寫:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<? 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"> < context:component-scan base-package = "com.xrq" /> < bean id = "zoo" class = "com.xrq.bean.Zoo" /> < bean id = "tiger" class = "com.xrq.domain.Tiger" /> < bean id = "monkey" class = "com.xrq.domain.Monkey" /> </ beans > |
注意第10行,使用必須告訴spring一下我要使用注解了,告訴的方式有很多,<context:component-scan base-package="xxx" />是一種最簡單的,spring會自動掃描xxx路徑下的注解。
看到第12行,原來zoo里面應(yīng)當(dāng)注入兩個屬性tiger、monkey,現(xiàn)在不需要注入了。再看下,Zoo.java也很方便,把getter/setter都可以去掉:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class Zoo { @Autowired private Tiger tiger; @Autowired private Monkey monkey; public String toString() { return tiger + "\n" + monkey; } } |
這里@Autowired注解的意思就是,當(dāng)Spring發(fā)現(xiàn)@Autowired注解時,將自動在代碼上下文中找到和其匹配(默認(rèn)是類型匹配)的Bean,并自動注入到相應(yīng)的地方去。
有一個細(xì)節(jié)性的問題是,假如bean里面有兩個property,Zoo.java里面又去掉了屬性的getter/setter并使用@Autowired注解標(biāo)注這兩個屬性那會怎么樣?答案是Spring會按照xml優(yōu)先的原則去Zoo.java中尋找這兩個屬性的getter/setter,導(dǎo)致的結(jié)果就是初始化bean報錯。
OK,假設(shè)此時我把.xml文件的13行、14行兩行給去掉,再運(yùn)行,會拋出異常:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'Zoo': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.xrq.domain.Tiger com.xrq.bean.Zoo.ttiger; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.xrq.domain.Tiger] 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.xrq.domain.Tiger com.xrq.bean.Zoo.ttiger; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.xrq.domain.Tiger] 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.xrq.domain.Tiger] 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 |
因?yàn)椋珸Autowired注解要去尋找的是一個Bean,Tiger和Monkey的Bean定義都給去掉了,自然就不是一個Bean了,Spring容器找不到也很好理解。那么,如果屬性找不到我不想讓Spring容器拋出異常,而就是顯示null,可以嗎?可以的,其實(shí)異常信息里面也給出了提示了,就是將@Autowired注解的required屬性設(shè)置為false即可:
1
2
3
4
5
6
7
8
9
10
11
12
|
public class Zoo { @Autowired (required = false ) private Tiger tiger; @Autowired (required = false ) private Monkey monkey; public String toString() { return tiger + "\n" + monkey; } } |
此時,找不到tiger、monkey兩個屬性,Spring容器不再拋出異而是認(rèn)為這兩個屬性為null。
@Autowired接口注入
上面的比較簡單,我們只是簡單注入一個Java類,那么如果有一個接口,有多個實(shí)現(xiàn),Bean里引用的是接口名,又該怎么做呢?比如有一個Car接口:
1
2
3
4
|
public interface Car { public String carName(); } |
兩個實(shí)現(xiàn)類BMW和Benz:
1
2
3
4
5
6
7
8
|
@Service public class BMW implements Car { public String carName() { return "BMW car" ; } } |
1
2
3
4
5
6
7
8
|
@Service public class Benz implements Car { public String carName() { return "Benz car" ; } } |
寫一個CarFactory,引用Car:
1
2
3
4
5
6
7
8
9
10
|
@Service public class CarFactory { @Autowired private Car car; public String toString() { return car.carName(); } } |
不用說,一定是報錯的,Car接口有兩個實(shí)現(xiàn)類,Spring并不知道應(yīng)當(dāng)引用哪個實(shí)現(xiàn)類。這種情況通常有兩個解決辦法:
1、刪除其中一個實(shí)現(xiàn)類,Spring會自動去base-package下尋找Car接口的實(shí)現(xiàn)類,發(fā)現(xiàn)Car接口只有一個實(shí)現(xiàn)類,便會直接引用這個實(shí)現(xiàn)類
2、實(shí)現(xiàn)類就是有多個該怎么辦?此時可以使用@Qualifier注解:
1
2
3
4
5
6
7
8
9
10
11
12
|
@Service public class CarFactory { @Autowired @Qualifier ( "BMW" ) private Car car; public String toString() { return car.carName(); } } |
注意@Qualifier注解括號里面的應(yīng)當(dāng)是Car接口實(shí)現(xiàn)類的類名,我之前試的時候一直以為是bean的名字,所以寫了"bMW",結(jié)果一直報錯。
@Resource
把@Resource注解放在@Autowired下面說,是因?yàn)樗鼈冏饔梅浅O嗨疲@個就簡單說了,例子過后點(diǎn)明一下@Resource和@Autowired的區(qū)別。先看一下@Resource,直接寫Zoo.java了:
1
2
3
4
5
6
7
8
9
10
11
12
|
@Service public class Zoo { @Resource (name = "tiger" ) private Tiger tiger; @Resource (type = Monkey. class ) private Monkey monkey; public String toString() { return tiger + "\n" + monkey; } } |
這是詳細(xì)一些的用法,說一下@Resource的裝配順序:
1、@Resource后面沒有任何內(nèi)容,默認(rèn)通過name屬性去匹配bean,找不到再按type去匹配
2、指定了name或者type則根據(jù)指定的類型去匹配bean
3、指定了name和type則根據(jù)指定的name和type去匹配bean,任何一個不匹配都將報錯
然后,區(qū)分一下@Autowired和@Resource兩個注解的區(qū)別:
1、@Autowired默認(rèn)按照byType方式進(jìn)行bean匹配,@Resource默認(rèn)按照byName方式進(jìn)行bean匹配
2、@Autowired是Spring的注解,@Resource是J2EE的注解,這個看一下導(dǎo)入注解的時候這兩個注解的包名就一清二楚了
Spring屬于第三方的,J2EE是Java自己的東西,因此,建議使用@Resource注解,以減少代碼和Spring之間的耦合。
@Service
上面這個例子,還可以繼續(xù)簡化,因?yàn)閟pring的配置文件里面還有12行~14行三個bean,下一步的簡化是把這三個bean也給去掉,使得spring配置文件里面只有一個自動掃描的標(biāo)簽,增強(qiáng)Java代碼的內(nèi)聚性并進(jìn)一步減少配置文件。
要繼續(xù)簡化,可以使用@Service。先看一下配置文件,當(dāng)然是全部刪除了:
1
2
3
4
5
6
7
8
9
10
|
<? 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"> < context:component-scan base-package = "com.xrq" /> </ beans > |
是不是感覺很爽?起碼我覺得是的。OK,下面以Zoo.java為例,其余的Monkey.java和Tiger.java都一樣:
1
2
3
4
5
6
7
8
9
10
11
12
|
@Service public class Zoo { @Autowired private Tiger ttiger; @Autowired private Monkey mmonkey; public String toString() { return ttiger + "\n" + mmonkey; } } |
這樣,Zoo.java在Spring容器中存在的形式就是"zoo",即可以通過ApplicationContext的getBean("zoo")方法來得到Zoo.java。@Service注解,其實(shí)做了兩件事情:
1、聲明Zoo.java是一個bean,這點(diǎn)很重要,因?yàn)閆oo.java是一個bean,其他的類才可以使用@Autowired將Zoo作為一個成員變量自動注入
2、Zoo.java在bean中的id是"zoo",即類名且首字母小寫
如果,我不想用這種形式怎么辦,就想讓Zoo.java在Spring容器中的名字叫做"Zoo",可以的:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@Service @Scope ( "prototype" ) public class Zoo { @Autowired private Monkey monkey; @Autowired private Tiger tiger; public String toString() { return "MonkeyName:" + monkey + "\nTigerName:" + tiger; } } |
這樣,就可以通過ApplicationContext的getBean("zoo")方法來得到Zoo.java了。
這里我還多加了一個@Scope注解,應(yīng)該很好理解。因?yàn)镾pring默認(rèn)產(chǎn)生的bean是單例的,假如我不想使用單例怎么辦,xml文件里面可以在bean里面配置scope屬性。注解也是一樣,配置@Scope即可,默認(rèn)是"singleton"即單例,"prototype"表示原型即每次都會new一個新的出來。
補(bǔ)充細(xì)節(jié)
最后再補(bǔ)充一個我發(fā)現(xiàn)的細(xì)節(jié)。假如animal包下有Tiger、domain包下也有Tiger,它們二者都加了@Service注解,那么在Zoo.java中即使明確表示我要引用的是domain包下的Tiger,程序運(yùn)行的時候依然會報錯。
細(xì)想,其實(shí)這很好理解,兩個Tiger都使用@Service注解標(biāo)注,意味著兩個Bean的名字都是"tiger",那么我在Zoo.java中自動裝配的是哪個Tiger呢?不明確,因此,Spring容器會拋出BeanDefinitionStoreException異常,Caused by:
1
|
Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'monkey' for bean class [com.xrq.domain.Monkey] conflicts with existing, non-compatible bean definition of same name and class [com.xrq.animal.Monkey] |
以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,同時也希望多多支持服務(wù)器之家!
原文鏈接:http://www.cnblogs.com/xrq730/p/5313412.html