• Spring中的注入方式 和使用的注解 详解


    注解:http://www.cnblogs.com/liangxiaofeng/p/6390868.html

    注入方式:http://www.cnblogs.com/java-class/p/4727775.html

    、、、、、、、、、、、、、、、、

    Spring 依赖注入方式详解

     

        平常的Java开发中,程序员在某个类中需要依赖其它类的方法。

        通常是new一个依赖类再调用类实例的方法,这种开发存在的问题是new的类实例不好统一管理。

        Spring提出了依赖注入的思想,即依赖类不由程序员实例化,而是通过Spring容器帮我们new指定实例并且将实例注入到需要该对象的类中。

        依赖注入的另一种说法是"控制反转"。通俗的理解是:平常我们new一个实例,这个实例的控制权是我们程序员。

        而控制反转是指new实例工作不由我们程序员来做而是交给Spring容器来做。

        Spring有多种依赖注入的形式,本篇文章仅介绍Spring通过xml进行IOC配置的方式。

    1.Set注入

        这是最简单的注入方式,假设有一个SpringAction,类中需要实例化一个SpringDao对象,那么就可以定义一个private的SpringDao成员变量,然后创建SpringDao的set方法(这是ioc的注入入口):

    复制代码
    package com.bless.springdemo.action; 
    public class SpringAction { 
    //注入对象springDao 
    private SpringDao springDao; 
    //一定要写被注入对象的set方法 
    public void setSpringDao(SpringDao springDao) { 
    this.springDao = springDao; 
    } 
    
    public void ok(){ 
    springDao.ok(); 
    } 
    }
    复制代码

       随后编写spring的xml文件,<bean>中的name属性是class属性的一个别名,class属性指类的全名,因为在SpringAction中有一个公共属性Springdao,所以要在<bean>标签中创建一个<property>标签指定SpringDao。<property>标签中的name就是SpringAction类中的SpringDao属性名,ref指下面<bean name="springDao"...>,这样其实是spring将SpringDaoImpl对象实例化并且调用SpringAction的setSpringDao方法将SpringDao注入:

    复制代码
    <!--配置bean,配置后该类由spring管理--> 
    <bean name="springAction" class="com.bless.springdemo.action.SpringAction"> 
    <!--(1)依赖注入,配置当前类中相应的属性--> 
    <property name="springDao" ref="springDao"></property> 
    </bean> 
    <bean name="springDao" class="com.bless.springdemo.dao.impl.SpringDaoImpl"></bean> 
    复制代码

    2.构造器注入

         这种方式的注入是指带有参数的构造函数注入,看下面的例子,我创建了两个成员变量SpringDao和User,但是并未设置对象的set方法,所以就不能支持第一种注入方式,这里的注入方式是在SpringAction的构造函数中注入,也就是说在创建SpringAction对象时要将SpringDao和User两个参数值传进来:

    复制代码
    public class SpringAction { 
    //注入对象springDao 
    private SpringDao springDao; 
    private User user; 
    
    public SpringAction(SpringDao springDao,User user){ 
    this.springDao = springDao; 
    this.user = user; 
    System.out.println("构造方法调用springDao和user"); 
    } 
    
    public void save(){ 
    user.setName("卡卡"); 
    springDao.save(user); 
    } 
    } 
    复制代码

         在XML文件中同样不用<property>的形式,而是使用<constructor-arg>标签,ref属性同样指向其它<bean>标签的name属性:

    复制代码
    <!--配置bean,配置后该类由spring管理--> 
    <bean name="springAction" class="com.bless.springdemo.action.SpringAction"> 
    <!--(2)创建构造器注入,如果主类有带参的构造方法则需添加此配置--> 
    <constructor-arg ref="springDao"></constructor-arg> 
    <constructor-arg ref="user"></constructor-arg> 
    </bean> 
    <bean name="springDao" class="com.bless.springdemo.dao.impl.SpringDaoImpl"></bean> 
    <bean name="user" class="com.bless.springdemo.vo.User"></bean> 
    复制代码

         解决构造方法参数的不确定性,你可能会遇到构造方法传入的两参数都是同类型的,为了分清哪个该赋对应值,则需要进行一些小处理:
    下面是设置index,就是参数位置:

    <bean name="springAction" class="com.bless.springdemo.action.SpringAction"> 
    <constructor-arg index="0" ref="springDao"></constructor-arg> 
    <constructor-arg index="1" ref="user"></constructor-arg> 
    </bean> 

         另一种是设置参数类型:

    <constructor-arg type="java.lang.String" ref=""/>

    3.静态工厂的方法注入

         静态工厂顾名思义,就是通过调用静态工厂的方法来获取自己需要的对象,为了让spring管理所有对象,我们不能直接通过"工程类.静态方法()"来获取对象,而是依然通过spring注入的形式获取:

    复制代码
    package com.bless.springdemo.factory; 
    
    import com.bless.springdemo.dao.FactoryDao; 
    import com.bless.springdemo.dao.impl.FactoryDaoImpl; 
    import com.bless.springdemo.dao.impl.StaticFacotryDaoImpl; 
    
    public class DaoFactory { 
    //静态工厂 
    public static final FactoryDao getStaticFactoryDaoImpl(){ 
    return new StaticFacotryDaoImpl(); 
    } 
    }
    复制代码

         同样看关键类,这里我需要注入一个FactoryDao对象,这里看起来跟第一种注入一模一样,但是看随后的xml会发现有很大差别:

    复制代码
    public class SpringAction { 
    //注入对象 
    private FactoryDao staticFactoryDao; 
    
    public void staticFactoryOk(){ 
    staticFactoryDao.saveFactory(); 
    } 
    //注入对象的set方法 
    public void setStaticFactoryDao(FactoryDao staticFactoryDao) { 
    this.staticFactoryDao = staticFactoryDao; 
    } 
    } 
    复制代码

         Spring的IOC配置文件,注意看<bean name="staticFactoryDao">指向的class并不是FactoryDao的实现类,而是指向静态工厂DaoFactory,并且配置 factory-method="getStaticFactoryDaoImpl"指定调用哪个工厂方法:

    复制代码
    <!--配置bean,配置后该类由spring管理--> 
    <bean name="springAction" class="com.bless.springdemo.action.SpringAction" > 
    <!--(3)使用静态工厂的方法注入对象,对应下面的配置文件(3)--> 
    <property name="staticFactoryDao" ref="staticFactoryDao"></property> 
    </property> 
    </bean> 
    <!--(3)此处获取对象的方式是从工厂类中获取静态方法--> 
    <bean name="staticFactoryDao" class="com.bless.springdemo.factory.DaoFactory" factory-method="getStaticFactoryDaoImpl"></bean> 
    复制代码

    4.实例工厂的方法注入

         实例工厂的意思是获取对象实例的方法不是静态的,所以你需要首先new工厂类,再调用普通的实例方法:

    复制代码
    public class DaoFactory { 
    //实例工厂 
    public FactoryDao getFactoryDaoImpl(){ 
    return new FactoryDaoImpl(); 
    } 
    }
    复制代码

        那么下面这个类没什么说的,跟前面也很相似,但是我们需要通过实例工厂类创建FactoryDao对象:

    复制代码
    public class SpringAction { 
    //注入对象 
    private FactoryDao factoryDao; 
    
    public void factoryOk(){ 
    factoryDao.saveFactory(); 
    } 
    
    public void setFactoryDao(FactoryDao factoryDao) { 
    this.factoryDao = factoryDao; 
    } 
    } 
    复制代码

         最后看spring配置文件:

    复制代码
    <!--配置bean,配置后该类由spring管理--> 
    <bean name="springAction" class="com.bless.springdemo.action.SpringAction"> 
    <!--(4)使用实例工厂的方法注入对象,对应下面的配置文件(4)--> 
    <property name="factoryDao" ref="factoryDao"></property> 
    </bean> 
    
    <!--(4)此处获取对象的方式是从工厂类中获取实例方法--> 
    <bean name="daoFactory" class="com.bless.springdemo.factory.DaoFactory"></bean> 
    <bean name="factoryDao" factory-bean="daoFactory" factory-method="getFactoryDaoImpl"></bean> 
    复制代码

    5.总结

        Spring IOC注入方式用得最多的是(1)(2)种,多写多练就会非常熟练。

        另外注意:通过Spring创建的对象默认是单例,如果需要创建多实例对象可以在<bean>标签后面添加一个属性:

    <bean name="..." class="..." scope="prototype">

    、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、

    注解注入顾名思义就是通过注解来实现注入,Spring和注入相关的常见注解有Autowired、Resource、Qualifier、Service、Controller、Repository、Component。

    Autowired是自动注入,自动从spring的上下文找到合适的bean来注入

    Resource用来指定名称注入

    Qualifier和Autowired配合使用,指定bean的名称
    Service,Controller,Repository分别标记类是Service层类,Controller层类,数据存储层的类,spring扫描注解配置时,会标记这些类要生成bean。

    Component是一种泛指,标记类是组件,spring扫描注解配置时,会标记这些类要生成bean。

    Spring对于Bean的依赖注入,支持多种注解方式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @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是个例外,它使用的是CommonAnnotationBeanPostProcessor来处理依赖注入。当然,两者 都是BeanPostProcessor。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @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 Qualified name VS Bean name

    在Spring设计中,Qualified name并不等同于Bean name,后者必须是唯一的,但是前者类似于tag或者group的作用,对特定的bean进行分类。可以达到getByTag(group)的效果。对 于XML配置的bean,可以通过id属性指定bean name(如果没有指定,默认使用类名首字母小写),通过标签指定qualifier name:

    1
    2
    3
    4
    <bean id="lamborghini" class="me.arganzheng.study.spring.autowired.Lamborghini">
    <qualifier value="luxury"/>
    <!-- inject any dependencies required by this bean -->
    </bean>

    如果是通过注解方式,那么可以通过@Qualifier注解指定qualifier name,通过@Named或者@Component(@Service,@Repository等)的value值指定bean name:

    1
    2
    3
    4
    @Component("lamborghini")
    @Qualifier("luxury")
    public class Lamborghini implements Car {
    }

    或者

    1
    2
    3
    4
    5
    @Component
    @Named("lamborghini")
    @Qualifier("luxury")
    public class Lamborghini implements Car {
    }

    同样,如果没有指定bean name,那么Spring会默认是用类名首字母小写(Lamborghini=>lamborghini)。

    三、 通过Anotation注入依赖的方式在XML注入方式之前进行。如果对同一个bean的依赖同时使用了两种注入方式,那么XML的优先。但是不同担心通过Anotation注入的依赖没法注入XML中配置的bean,依赖注入是在bean的注册之后进行的。

    四、目前的autowired by type方式(笔者用的是3.2.3.RELEASE版本),Spring的AutowiredAnnotationBeanPostProcessor 实现都是有”bug”的,也就是说@Autowired和@Inject都是有坑的(称之为坑,不称之为bug是因为貌似是故意的。。)。这是来源于线上 的一个bug,也是这边文章的写作原因。现场如下:

    application-context.xml中有如下定义:

    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    <xml version="1.0" encoding="UTF-8"?>
    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应用的常量定义在如下类中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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";
    }
    }

    然后如果我们在代码中如下声明依赖:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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);
    }
    }

    Guess what,诡异的事情发生了!

    运行结果如下:

    1
    2
    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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    [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)}
    ...
    ed 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。在DefaultListableBeanFactory的这个方法出问题了:

    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
    30
    31
    32
    33
    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;
    }
    ...
    }

    关键在这一句:Map

    严重: Caught exception while allowing TestExecutionListener

    1
    2
    3
    4
    5
    6
    7
    8
    [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了一下,发现跟没有指定qualifie name是一样的执行路径。不是指定了bean name了吗?为什么还是autowired by type呢?仔细查看了一下才发现。DefaultListableBeanFactory的doResolveDependency方法对首先对类型做 了区别:

    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    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();
    }
    }

    如果是Array,Collection或者Map,则根据集合类中元素的类型来进行autowired by type(Map使用value的类型)。为什么这么特殊处理呢?原来,Spring是为了达到这样的目的:让你可以一次注入所有符合类型的实现,也就是 说可以这样子注入:

    @Autowired 
    private List<Car> cars;

    如果你的car有多个实现,那么都会注入进来,不会再报

    1
    2
    3
    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则不会有这个问题: 

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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);
    }
    }

    正常运行:

    1
    2
    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}

    当然,你如果不指定@Qualifier(“languageChangesMap”),同时field name不是languageChangesMap,那么还是一样报错的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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接收所有实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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);
    }
    }

    运行的妥妥的:

    1
    2
    3
    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注解使用的是CommonAnnotationBeanPostProcessor处理器,跟 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即使加了@Qualifier注解,其实也是autowired by type。@Qualifier只是一个限定词,过滤条件而已。重新跟进了一下代码,发现确实是这样子的。Spring设计的这个 @Qualifier name 并不等同于 bean name。他有点类似于一个tag。不过如果这个tag是唯一的化,那么其实效果上等同于bean name。实现上,Spring是先getByType,得到list candicates,然后再根据qualifier name进行过滤。

    再定义一个兰博基尼,这里使用@Qualifier指定:

    1
    2
    3
    4
    5
    6
    7
    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指定:

    1
    2
    3
    4
    5
    6
    7
    package me.arganzheng.study.spring.autowired;
    import javax.inject.Named;
    import org.springframework.stereotype.Component;
    @Component
    @Named("luxury")
    public class RollsRoyce implements Car {
    }

    测试一下注入定义的豪华车:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    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);
    }
    }

    运行结果如下:

    1
    2
    ArrayList
    [me.arganzheng.study.spring.autowired.Lamborghini@66b875e1, me.arganzheng.study.spring.autowired.RollsRoyce@58433b76]

    补充:Autowiring modes

    Spring支持四种autowire模式,当使用XML配置方式时,你可以通过autowire属性指定。

    1
    2
    3
    4
    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注解的时候,则稍微复杂一些,会有一个失败退化过程,并且引入了Qualifier。不过基本原理是一样。

  • 相关阅读:
    非线性滤波:中值滤波;双边滤波
    线性滤波:方框滤波、均值滤波、高斯滤波
    输入输出XML和YAML文件
    图像对比度、亮度值调整
    opencv图像混合,分离颜色通道、多通道图像混合
    访问图像的三种方法
    Opencv
    INSERT增加数据记录
    MySQL 语言大全
    text
  • 原文地址:https://www.cnblogs.com/ConfidentLiu/p/7691111.html
Copyright © 2020-2023  润新知