Spring5:@Autowired註解、@Resource註解和@Service註解[轉載]

什麼是註解

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

一、若是全部的內容都配置在.xml文件中,那麼.xml文件將會十分龐大;若是按需求分開.xml文件,那麼.xml文件又會很是多。總之這將致使配置文件的可讀性與可維護性變得很低java

二、在開發中在.java文件和.xml文件之間不斷切換,是一件麻煩的事,同時這種思惟上的不連貫也會下降開發的效率spring

爲了解決這兩個問題,Spring引入了註解,經過"@XXX"的方式,讓註解與Java Bean緊密結合,既大大減小了配置文件的體積,又增長了Java Bean的可讀性與內聚性。dom

本篇文章,講講最重要的三個Spring註解,也就是@Autowired、@Resource和@Service,但願能經過有限的篇幅說清楚這三個註解的用法。post

不使用註解

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

public class Tiger
{
    private String tigerName = "TigerKing";
    
    public String toString()
    {
        return "TigerName:" + tigerName;
    }
}

再定義一個猴子:this

public class Monkey
{
    private String monkeyName = "MonkeyKing";
    
    public String toString()
    {
        return "MonkeyName:" + monkeyName;
    }
}

定義一個動物園:prototype

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的配置文件這麼寫:code

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

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

@Autowired

@Autowired顧名思義,就是自動裝配,其做用是爲了消除代碼Java代碼裏面的getter/setter與bean屬性中的property。固然,getter看我的需求,若是私有屬性須要對外提供的話,應當予以保留。

所以,引入@Autowired註解,先看一下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">
    
    <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裏面應當注入兩個屬性tiger、monkey,如今不須要注入了。再看下,Zoo.java也很方便,把getter/setter均可以去掉:

public class Zoo
{
    @Autowired
    private Tiger tiger;
    
    @Autowired
    private Monkey monkey;
    
    public String toString()
    {
        return tiger + "\n" + monkey;
    }
}

這裏@Autowired註解的意思就是,當Spring發現@Autowired註解時,將自動在代碼上下文中找到和其匹配(默認是類型匹配)的Bean,並自動注入到相應的地方去。

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

OK,假設此時我把.xml文件的13行、14行兩行給去掉,再運行,會拋出異常:

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

由於,@Autowired註解要去尋找的是一個Bean,Tiger和 Monkey的Bean定義都給去掉了,天然就不是一個Bean了,Spring容器找不到也很好理解。那麼,若是屬性找不到我不想讓Spring容器拋 出異常,而就是顯示null,能夠嗎?能夠的,其實異常信息裏面也給出了提示了,就是將@Autowired註解的required屬性設置爲false 便可:

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

@Autowired接口注入

上面的比較簡單,咱們只是簡單注入一個Java類,那麼若是有一個接口,有多個實現,Bean裏引用的是接口名,又該怎麼作呢?好比有一個Car接口:

public interface Car
{
    public String carName();
}

兩個實現類BMW和Benz:

@Service
public class BMW implements Car
{
    public String carName()
    {
        return "BMW car";
    }
}
@Service
public class Benz implements Car
{
    public String carName()
    {
        return "Benz car";
    }
}

寫一個CarFactory,引用Car:

@Service
public class CarFactory
{
    @Autowired
    private Car car;
    
    public String toString()
    {
        return car.carName();
    }
}

不用說,必定是報錯的,Car接口有兩個實現類,Spring並不知道應當引用哪一個實現類。這種狀況一般有兩個解決辦法:

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

二、實現類就是有多個該怎麼辦?此時可使用@Qualifier註解:

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

注意@Qualifier註解括號裏面的應當是Car接口實現類的類名,我以前試的時候一直覺得是bean的名字,因此寫了"bMW",結果一直報錯。

@Resource

把@Resource註解放在@Autowired下面說,是由於它們做用很是類似,這個就簡單說了,例子事後點明一下@Resource和@Autowired的區別。先看一下@Resource,直接寫Zoo.java了:

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

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

一、@Resource後面沒有任何內容,默認經過name屬性去匹配bean,找不到再按type去匹配

二、指定了name或者type則根據指定的類型去匹配bean

三、指定了name和type則根據指定的name和type去匹配bean,任何一個不匹配都將報錯

而後,區分一下@Autowired和@Resource兩個註解的區別:

一、@Autowired默認按照byType方式進行bean匹配,@Resource默認按照byName方式進行bean匹配

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

Spring屬於第三方的,J2EE是Java本身的東西,所以,建議使用@Resource註解,以減小代碼和Spring之間的耦合。

@Service

上面這個例子,還能夠繼續簡化,由於spring的配置文件裏面還有12行~14行三個bean,下一步的簡化是把這三個bean也給去掉,使得spring配置文件裏面只有一個自動掃描的標籤,加強Java代碼的內聚性並進一步減小配置文件。

要繼續簡化,可使用@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>

是否是感受很爽?起碼我以爲是的。OK,下面以Zoo.java爲例,其他的Monkey.java和Tiger.java都同樣:

@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註解,其實作了兩件事情:

一、聲明Zoo.java是一個bean,這點很重要,由於Zoo.java是一個bean,其餘的類纔可使用@Autowired將Zoo做爲一個成員變量自動注入

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

若是,我不想用這種形式怎麼辦,就想讓Zoo.java在Spring容器中的名字叫作"Zoo",能夠的:

@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註解,應該 很好理解。由於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]

轉載於:https://www.cnblogs.com/szlbm...做者:IT·達人

相關文章
相關標籤/搜索