Spring各種依存注入注解の違い


SpringはBeanに対する依存注入であり、複数の注釈方式をサポートする.
@Resource
javax.annotation
JSR250 (Common Annotations for Java)

@Inject
javax.inject
JSR330 (Dependency Injection for Java)

@Autowired
org.springframework.bean.factory
Spring
直観的に見ると、@AutowiredはSpringから提供された注釈であり、他のいくつかはJDK自身が構築した注釈であり、Springはこれらの注釈をサポートしている.しかし、この三つの違いは何ですか?方法のテストを通して、面白い特性を見つけました.
違いは以下の通りです.
一、@Autowiredにはrequired属性があり、falseに設定できます.この場合、対応するbeanが見つからないと投げられません.@Injectと@Resourceは対応する配置を提供していませんので、見つけなければなりません.
二、@Autowiredと@Injectは基本的に同じです.両方ともAutowiredAnnotationBeanPostProcessorを使って依存注入を処理します.しかし@Resourceは例外です.これはCommon AnnotationBeanPostProcessorを使って依存注入を処理します.もちろん、両方ともBeanPostProcessorです.
@Autowired @Inject
-    autowired by type
-      @Qualifier      autowired by qualifier name。
-    autowired by type   (           ),    autowired by field name

@Resource
-    autowired by field name
-    autowired by field name  ,     autowired by type
-      @Qualifier      autowired by qualifier name
-    autowired by qualifier name  ,     autowired by field name。        autowired by field name  ,       autowired by type 。
TIPS Qualfied name VS Bean name
Spring設計では、Qualfied nameはBean nameと同じではなく、後者は唯一でなければならないが、前者は「またはgroup」のような役割をして、特定のbeanを分類する.getByTagの効果が得られます.XML設定のbeanについては、id属性でbean nameを指定できます.
<bean id="lamborghini" class="me.arganzheng.study.spring.autowired.Lamborghini">
    <qualifier value="luxury"/>
    <!-- inject any dependencies required by this bean -->
</bean>
注釈方式であれば、@Qualifer nameを@Qualifer注解で指定し、@Namedまたは@Component(@Service、@Repositoryなど)のvalue値でbean nameを指定できます.
@Component("lamborghini")
@Qualifier("luxury")
public class Lamborghini implements Car {

}
または
@Component
@Named("lamborghini")
@Qualifier("luxury")
public class Lamborghini implements Car {

}   
同様に、bean nameが指定されていない場合、Springはデフォルトではクラス名の頭文字で小文字で書かれます.
三、Anotationによる注入依存方式はXML注入方式の前に行う.同一のbeanに対する依存性が同時に二つの注入方式を使用している場合、XMLの優先度が高くなります.しかし、アンタレーションによる依存はXMLに配置されたbeanに注入できず、注入に依存するのはbeanの登録後です.
四、現在のautwired by type方式(筆者は3.3.2.RELEASEバージョンを使用しています.)、SpringのAutowiredAnnotationBeanPostProcessorの実現には「bug」があります.つまり、@Autowiredと@Injectはピットがあります.これはオンラインのバグから来たもので、こちらの文章を書く原因でもあります.現場は以下の通りです
アプリアプリ-context.xmlには以下のような定義があります.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">

    <context:annotation-config />

    <context:component-scan base-package="me.arganzheng.study" />

    <util:constant id="en" static-field="me.arganzheng.study.spring.autowired.Constants.Language.EN" />
    <util:constant id="ja" static-field="me.arganzheng.study.spring.autowired.Constants.Language.JP" />
    <util:constant id="ind" static-field="me.arganzheng.study.spring.autowired.Constants.Language.IND" />
    <util:constant id="pt" static-field="me.arganzheng.study.spring.autowired.Constants.Language.PT" />
    <util:constant id="th" static-field="me.arganzheng.study.spring.autowired.Constants.Language.TH" />
    <util:constant id="ar" static-field="me.arganzheng.study.spring.autowired.Constants.Language.AR" />
    <util:constant id="en-rIn" static-field="me.arganzheng.study.spring.autowired.Constants.Language.EN_RIN" />

    <util:map id="languageChangesMap" key-type="java.lang.String" value-type="java.lang.String">
        <entry key="pt" value="pt" />
        <entry key="br" value="pt" />
        <entry key="jp" value="ja" />
        <entry key="ja" value="ja" />
        <entry key="ind" value="ind" />
        <entry key="id" value="ind" />
        <entry key="en-rin" value="en-rIn" />
        <entry key="in" value="en-rIn" />
        <entry key="en" value="en" />
        <entry key="gb" value="en" />
        <entry key="th" value="th" />
        <entry key="ar" value="ar" />
        <entry key="eg" value="ar" />
    </util:map>

</beans>    
ここで、static-fieldアプリケーションの定数定義は、次のクラスにある.
package me.arganzheng.study.spring.autowired;

public interface Constants {

    public interface Language {
        public static final String EN = "CommonConstants.LANG_ENGLISH";
        public static final String JP = "CommonConstants.LANG_JAPANESE";
        public static final String IND = "CommonConstants.LANG_INDONESIAN";
        public static final String PT = "CommonConstants.LANG_PORTUGUESE";
        public static final String TH = "CommonConstants.LANG_THAI";
        public static final String EN_RIN = "CommonConstants.LANG_ENGLISH_INDIA";
        public static final String AR = "CommonConstants.LANG_Arabic";
    }
}
そして、コードの中で次のように依存を宣言したら、
public class AutowiredTest extends BaseSpringTestCase {

    @Autowired
    private Map<String, String> languageChangesMap;

    @Test
    public void testAutowired() {
        notNull(languageChangesMap);
        System.out.println(languageChangesMap.getClass().getSimpleName());
        System.out.println(languageChangesMap);
    }
}
Gress what、怪しいことが発生しました!
運転結果は以下の通りです.
LinkedHashMap
{en=CommonConstants.LANG_ENGLISH, ja=CommonConstants.LANG_JAPANESE, ind=CommonConstants.LANG_INDONESIAN, pt=CommonConstants.LANG_PORTUGUESE, th=CommonConstants.LANG_THAI, ar=CommonConstants.LANG_Arabic, en-rIn=CommonConstants.LANG_ENGLISH_INDIA}
つまりMap
  : Caught exception while allowing TestExecutionListener [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@5c51ee0a] to prepare test instance [me.arganzheng.study.spring.autowired.AutowiredTest@6e301e0]
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'me.arganzheng.study.spring.autowired.AutowiredTest': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private java.util.Map me.arganzheng.study.spring.autowired.AutowiredTest.languageChangesMap; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency [map with value type java.lang.String]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    ...
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency [map with value type java.lang.String]: 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:986)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:843)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:768)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:486)
    ... 28 more
debugを見たら、確かにSpringのbugです.DefaultListable BenFactoryでのこの方法に問題が発生しました.
protected Object doResolveDependency(DependencyDescriptor descriptor, Class<?> type, String beanName,
            Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
        ...     

        else if (Map.class.isAssignableFrom(type) && type.isInterface()) {
            Class<?> keyType = descriptor.getMapKeyType();
            if (keyType == null || !String.class.isAssignableFrom(keyType)) {
                if (descriptor.isRequired()) {
                    throw new FatalBeanException("Key type [" + keyType + "] of map [" + type.getName() +
                            "] must be assignable to [java.lang.String]");
                }
                return null;
            }
            Class<?> valueType = descriptor.getMapValueType();
            if (valueType == null) {
                if (descriptor.isRequired()) {
                    throw new FatalBeanException("No value type declared for map [" + type.getName() + "]");
                }
                return null;
            }
            Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType, descriptor);
            if (matchingBeans.isEmpty()) {
                if (descriptor.isRequired()) {
                    raiseNoSuchBeanDefinitionException(valueType, "map with value type " + valueType.getName(), descriptor);
                }
                return null;
            }
            if (autowiredBeanNames != null) {
                autowiredBeanNames.addAll(matchingBeans.keySet());
            }
            return matchingBeans;
        }
        ...
    }
ポイントはこの文です.
  : Caught exception while allowing TestExecutionListener [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@9476189] to prepare test instance [me.arganzheng.study.spring.autowired.AutowiredTest@2d546e21]
...
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency [map with value type java.lang.String]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=languageChangesMap)}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:986)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:843)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:768)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:486)
    ... 28 more 
debugを確認しましたが、指定されていないqualifee nameと同じ実行経路です.bean nameを指定したのではないですか?どうしてautwired by typeなのですか?よく調べてみたら、やっと見つけました.Default Listable BenFactoryのdoResoveDependency方法はまずタイプに対して区別しました.
protected Object doResolveDependency(DependencyDescriptor descriptor, Class<?> type, String beanName,
            Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {

        Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
        if (value != null) {
            if (value instanceof String) {
                String strVal = resolveEmbeddedValue((String) value);
                BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
                value = evaluateBeanDefinitionString(strVal, bd);
            }
            TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
            return (descriptor.getField() != null ?
                    converter.convertIfNecessary(value, type, descriptor.getField()) :
                    converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
        }

        if (type.isArray()) {
            Class<?> componentType = type.getComponentType();
            Map<String, Object> matchingBeans = findAutowireCandidates(beanName, componentType, descriptor);
            if (matchingBeans.isEmpty()) {
                if (descriptor.isRequired()) {
                    raiseNoSuchBeanDefinitionException(componentType, "array of " + componentType.getName(), descriptor);
                }
                return null;
            }
            if (autowiredBeanNames != null) {
                autowiredBeanNames.addAll(matchingBeans.keySet());
            }
            TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
            return converter.convertIfNecessary(matchingBeans.values(), type);
        }
        else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
            Class<?> elementType = descriptor.getCollectionType();
            if (elementType == null) {
                if (descriptor.isRequired()) {
                    throw new FatalBeanException("No element type declared for collection [" + type.getName() + "]");
                }
                return null;
            }
            Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType, descriptor);
            if (matchingBeans.isEmpty()) {
                if (descriptor.isRequired()) {
                    raiseNoSuchBeanDefinitionException(elementType, "collection of " + elementType.getName(), descriptor);
                }
                return null;
            }
            if (autowiredBeanNames != null) {
                autowiredBeanNames.addAll(matchingBeans.keySet());
            }
            TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
            return converter.convertIfNecessary(matchingBeans.values(), type);
        }
        else if (Map.class.isAssignableFrom(type) && type.isInterface()) {
            Class<?> keyType = descriptor.getMapKeyType();
            if (keyType == null || !String.class.isAssignableFrom(keyType)) {
                if (descriptor.isRequired()) {
                    throw new FatalBeanException("Key type [" + keyType + "] of map [" + type.getName() +
                            "] must be assignable to [java.lang.String]");
                }
                return null;
            }
            Class<?> valueType = descriptor.getMapValueType();
            if (valueType == null) {
                if (descriptor.isRequired()) {
                    throw new FatalBeanException("No value type declared for map [" + type.getName() + "]");
                }
                return null;
            }
            Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType, descriptor);
            if (matchingBeans.isEmpty()) {
                if (descriptor.isRequired()) {
                    raiseNoSuchBeanDefinitionException(valueType, "map with value type " + valueType.getName(), descriptor);
                }
                return null;
            }
            if (autowiredBeanNames != null) {
                autowiredBeanNames.addAll(matchingBeans.keySet());
            }
            return matchingBeans;
        }
        else {
            Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
            if (matchingBeans.isEmpty()) {
                if (descriptor.isRequired()) {
                    raiseNoSuchBeanDefinitionException(type, "", descriptor);
                }
                return null;
            }
            if (matchingBeans.size() > 1) {
                String primaryBeanName = determinePrimaryCandidate(matchingBeans, descriptor);
                if (primaryBeanName == null) {
                    throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet());
                }
                if (autowiredBeanNames != null) {
                    autowiredBeanNames.add(primaryBeanName);
                }
                return matchingBeans.get(primaryBeanName);
            }
            // We have exactly one match.
            Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
            if (autowiredBeanNames != null) {
                autowiredBeanNames.add(entry.getKey());
            }
            return entry.getValue();
        }
    }
Arayであれば、CollectionまたはMapは、クラスタ内の要素のタイプに応じてautwired by type(Mapはvalueを使用するタイプ)を行う.なぜこのように特別に処理しますか?元々、Springはこのような目的を達成するために、一度にすべての適格なタイプの実現を注ぎ込むことができます.つまり、このように注入することができます.
@Autowired
private List<Car> cars;
もしあなたのcarが複数の実現があれば、注入されます.再報告はしません.
org.springframework.beans.factory.NoSuchBeanDefinitionException: 
No unique bean of type [me.arganzheng.study.spring.autowired.Car] is defined: 
expected single matching bean but found 2: [audi, toyota].
しかし、上記の場合は@Resourceを使えばこの問題はありません.
public class AutowiredTest extends BaseSpringTestCase {

    @Resource
    @Qualifier("languageChangesMap")
    private Map<String, String> languageChangesMap;

    @Test
    public void testAutowired() {
        assertNotNull(languageChangesMap);
        System.out.println(languageChangesMap.getClass().getSimpleName());
        System.out.println(languageChangesMap);
    }
}
正常運転:
LinkedHashMap
{pt=pt, br=pt, jp=ja, ja=ja, ind=ind, id=ind, en-rin=en-rIn, in=en-rIn, en=en, gb=en, th=th, ar=ar, eg=ar}
もちろん、@Qualfierを指定しないと、field nameはlangage ChangesMapではないです.同じようにエラーを報告します.
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency [map with value type java.lang.String]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@javax.annotation.Resource(shareable=true, mappedName=, description=, name=, type=class java.lang.Object, authenticationType=CONTAINER, lookup=)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:986)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:843)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:768)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:438)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:416)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:550)
at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:150)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:303)
... 26 more
さらに、@Resourceは、上のList受信のすべての実施形態を実現することができる.
public class AutowiredTest extends BaseSpringTestCase {

    @Resource
    @Qualifier("languageChangesMap")
    private Map<String, String> languageChangesMap;

    @Resource
    private List<Car> cars;

    @Test
    public void testAutowired() {
        assertNotNull(languageChangesMap);
        System.out.println(languageChangesMap.getClass().getSimpleName());
        System.out.println(languageChangesMap);

        assertNotNull(cars);
        System.out.println(cars.getClass().getSimpleName());
        System.out.println(cars);
    }
}
うまくいった:
LinkedHashMap
{pt=pt, br=pt, jp=ja, ja=ja, ind=ind, id=ind, en-rin=en-rIn, in=en-rIn, en=en, gb=en, th=th, ar=ar, eg=ar}
ArrayList
[me.arganzheng.study.spring.autowired.Audi@579584da, me.arganzheng.study.spring.autowired.Toyota@19453122]
これは@Resource注がCommon AnnotationBeabin PostProcessorプロセッサを使っています.AutowiredAnnotationBeanPostProcessorとは同じ著者ではないからです.ここでは分析しません.興味のある学生は自分でコードを見て研究してもいいです.
最終的な結論は以下の通りです.
1、@Autowiredと@Inject
autowired by type      @Qualifier      autowired by qualifier name(    。  :  autowired by bean name!)
   autowired by type   (           ),    autowired by field name(    )
2、@Resource
   autowired by field name
   autowired by field name  ,     autowired by type
     @Qualifier      autowired by qualifier name
   autowired by qualifier name  ,     autowired by field name。        autowired by field name  ,       autowired by type 。
テストプロジェクトはGitHubに保存されています.標準的なmavenプロジェクトです.興味がある人はcloneで現地に行ってテストをしてみてください.
追加
Spring公式文書にこのような言葉があると同僚が指摘しました.
However, although you can use this convention to refer to specific beans by name, @Autowired is fundamentally about type-driven injection with optional semantic qualifiers. This means that qualifier values, even with the bean name fallback, always have narrowing semantics within the set of type matches; they do not semantically express a reference to a unique bean id.
つまり@Autowiredに@Qualfierを加えても、autwired by typeです.Qualfierは限定語で、フィルタ条件だけです.コードを再フォローしましたが、これは確かです.Springデザインのこれ@Qualfier nameはbean nameと同じではありません.彼はちょっと似ている.しかし、これが唯一のものなら、効果的にはbean nameに等しいです.実现すると、Springは先にgetByTypeで、list candicatesを得て、それからqualifer nameによってフィルタリングを行います.
ランボルギーニをもう一つ定義します.ここで@Qualfierで指定します.
package me.arganzheng.study.spring.autowired;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component
@Qualifier("luxury")
public class Lamborghini implements Car {

}
ロールス・ロイスをもう一つ定義します.ここでわざと@Namedで指定します.
package me.arganzheng.study.spring.autowired;

import javax.inject.Named;

import org.springframework.stereotype.Component;

@Component
@Named("luxury")
public class RollsRoyce implements Car {

}
注入定義の豪華車をテストします.
package me.arganzheng.study.spring.autowired;

import static junit.framework.Assert.assertNotNull;

import java.util.List;

import me.arganzheng.study.BaseSpringTestCase;

import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

/** * * @author zhengzhibin * */
public class AutowiredTest extends BaseSpringTestCase {

    @Autowired
    @Qualifier("luxury")
    private List<Car> luxuryCars;

    @Test
    public void testAutowired() {

        assertNotNull(luxuryCars);
        System.out.println(luxuryCars.getClass().getSimpleName());
        System.out.println(luxuryCars);
    }

}
運転結果は以下の通りです.
ArrayList
[me.arganzheng.study.spring.autowired.Lamborghini@66b875e1, me.arganzheng.study.spring.autowired.RollsRoyce@58433b76]
追加:Autowiring modes
Springは4種類のatowireモードをサポートしています.XMLプロファイルを使用すると、atowire属性で指定できます.
no. (Default) No autowiring. Bean references must be defined via a ref element. Changing the default setting is not recommended for larger deployments, because specifying collaborators explicitly gives greater control and clarity. To some extent, it documents the structure of a system.
byName. Autowiring by property name. Spring looks for a bean with the same name as the property that needs to be autowired. For example, if a bean definition is set to autowire by name, and it contains a master property (that is, it has a setMaster(..) method), Spring looks for a bean definition named master, and uses it to set the property.
byType. Allows a property to be autowired if exactly one bean of the property type exists in the container. If more than one exists, a fatal exception is thrown, which indicates that you may not use byType autowiring for that bean. If there are no matching beans, nothing happens; the property is not set.
constructor. Analogous to byType, but applies to constructor arguments. If there is not exactly one bean of the constructor argument type in the container, a fatal error is raised.
@Autowired、@Injectまたは@Resourceコメントを使うと、やや複雑で、失敗退化過程があり、Qualfierを導入します.基本原理は同じです.