天天看點

spring中@Autowired、@Resource和@Service

在學習spring注解新知識的時候會遇到很多問題,這篇文章不錯,轉載過來。

什麼是注解

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

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

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

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

本篇文章,講講最重要的三個Spring注解,也就是@Autowired、@Resource和@Service,希望能通過有限的篇幅說清楚這三個注解的用法。

不使用注解

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

spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service
public class Tiger
{
    private String tigerName = "TigerKing";

    public String toString() { return "TigerName:" + tigerName; } }      
spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service

再定義一個猴子:

spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service
public class Monkey
{
    private String monkeyName = "MonkeyKing";

    public String toString() { return "MonkeyName:" + monkeyName; } }      
spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service

定義一個動物園:

spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service
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中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service

spring的配置檔案這麼寫:

spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service
<?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>      
spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service

都很熟悉,權當複習一遍了。

@Autowired

@Autowired顧名思義,就是自動裝配,其作用是為了消除代碼Java代碼裡面的getter/setter與bean屬性中的property。當然,getter看個人需求,如果私有屬性需要對外提供的話,應當予以保留。

是以,引入@Autowired注解,先看一下spring配置檔案怎麼寫:

spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service
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.xrq" /> 11 12 <bean id="zoo" class="com.xrq.bean.Zoo" /> 13 <bean id="tiger" class="com.xrq.domain.Tiger" /> 14 <bean id="monkey" class="com.xrq.domain.Monkey" /> 15 16 </beans>      
spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service

注意第10行,使用必須告訴spring一下我要使用注解了,告訴的方式有很多,<context:component-scan base-package=”xxx” />是一種最簡單的,spring會自動掃描xxx路徑下的注解。

看到第12行,原來zoo裡面應當注入兩個屬性tiger、monkey,現在不需要注入了。再看下,Zoo.java也很友善,把getter/setter都可以去掉:

spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service
public class Zoo
{
    @Autowired
    private Tiger tiger;

    @Autowired
    private Monkey monkey; public String toString() { return tiger + "\n" + monkey; } }      
spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service

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

有一個細節性的問題是,假如bean裡面有兩個property,Zoo.java裡面又去掉了屬性的getter/setter并使用@Autowired注解标注這兩個屬性那會怎麼樣?答案是Spring會按照xml優先的原則去Zoo.java中尋找這兩個屬性的getter/setter,導緻的結果就是初始化bean報錯。

OK,假設此時我把.xml檔案的13行、14行兩行給去掉,再運作,會抛出異常:

spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service
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      
spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service

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

spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service
public class Zoo
{
    @Autowired(required = false)
    private Tiger tiger; @Autowired(required = false) private Monkey monkey; public String toString() { return tiger + "\n" + monkey; } }      
spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service

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

@Autowired接口注入

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

public interface Car
{
    public String carName();
}      

兩個實作類BMW和Benz:

spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service
@Service
public class BMW implements Car
{
    public String carName() { return "BMW car"; } }      
spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service
@Service
public class Benz implements Car
{
    public String carName() { return "Benz car"; } }      
spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service

寫一個CarFactory,引用Car:

spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service
@Service
public class CarFactory
{
    @Autowired
    private Car car; public String toString() { return car.carName(); } }      
spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service

不用說,一定是報錯的,Car接口有兩個實作類,Spring并不知道應當引用哪個實作類。這種情況通常有兩個解決辦法:

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

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

spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service
@Service
public class CarFactory
{
    @Autowired
    @Qualifier("BMW")
    private Car car; public String toString() { return car.carName(); } }      
spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service

注意@Qualifier注解括号裡面的應當是Car接口實作類的類名,我之前試的時候一直以為是bean的名字,是以寫了”bMW”,結果一直報錯。

@Resource

把@Resource注解放在@Autowired下面說,是因為它們作用非常相似,這個就簡單說了,例子過後點明一下@Resource和@Autowired的差別。先看一下@Resource,直接寫Zoo.java了:

spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service
@Service
public class Zoo
{
    @Resource(name = "tiger")
    private Tiger tiger; @Resource(type = Monkey.class) private Monkey monkey; public String toString() { return tiger + "\n" + monkey; } }      
spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service

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

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

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

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

然後,區分一下@Autowired和@Resource兩個注解的差別:

1、@Autowired預設按照byType方式進行bean比對,@Resource預設按照byName方式進行bean比對

2、@Autowired是Spring的注解,@Resource是J2EE的注解,這個看一下導入注解的時候這兩個注解的包名就一清二楚了

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

@Service

上面這個例子,還可以繼續簡化,因為spring的配置檔案裡面還有12行~14行三個bean,下一步的簡化是把這三個bean也給去掉,使得spring配置檔案裡面隻有一個自動掃描的标簽,增強Java代碼的内聚性并進一步減少配置檔案。

要繼續簡化,可以使用@Service。先看一下配置檔案,當然是全部删除了:

spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service
<?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>      
spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service

是不是感覺很爽?起碼我覺得是的。OK,下面以Zoo.java為例,其餘的Monkey.java和Tiger.java都一樣:

spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service
@Service
public class Zoo
{
    @Autowired
    private Tiger ttiger; @Autowired private Monkey mmonkey; public String toString() { return ttiger + "\n" + mmonkey; } }      
spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service

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

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

2、Zoo.java在bean中的id是”zoo”,即類名且首字母小寫

如果,我不想用這種形式怎麼辦,就想讓Zoo.java在Spring容器中的名字叫做”Zoo”,可以的:

spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service
@Service
@Scope("prototype")
public class Zoo
{
    @Autowired
    private Monkey monkey; @Autowired private Tiger tiger; public String toString() { return "MonkeyName:" + monkey + "\nTigerName:" + tiger; } }      
spring中@Autowired、@Resource和@Service
spring中@Autowired、@Resource和@Service

這樣,就可以通過ApplicationContext的getBean(“zoo”)方法來得到Zoo.java了。

這裡我還多加了一個@Scope注解,應該 很好了解。因為Spring預設産生的bean是單例的,假如我不想使用單例怎麼辦,xml檔案裡面可以在bean裡面配置scope屬性。注解也是一 樣,配置@Scope即可,預設是”singleton”即單例,”prototype”表示原型即每次都會new一個新的出來。

補充細節

最後再補充一個我發現的細節。假如animal包下有Tiger、domain包下也有Tiger,它們二者都加了@Service注解,那麼在Zoo.java中即使明确表示我要引用的是domain包下的Tiger,程式運作的時候依然會報錯。

細想,其實這很好了解,兩個Tiger都使 用@Service注解标注,意味着兩個Bean的名字都是”tiger”,那麼我在Zoo.java中自動裝配的是哪個Tiger呢?不明确,因 此,Spring容器會抛出BeanDefinitionStoreException異常,Caused by:

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]      

轉載位址: http://www.cnblogs.com/szlbm/p/5512931.html

繼續閱讀