Spring IOC(控制反转)和DI(依赖注入)原理
一、Spring IoC容器和bean简介
Spring Framework实现了控制反转(IoC)原理,IoC也称为依赖注入(DI)。
这是一个过程,通过这个过程,对象定义它们的依赖关系,即它们使用的其他对象,只能通过构造函数参数,工厂方法的参数,或者在构造或从工厂方法返回后在对象实例上设置的属性。
然后容器在创建bean时注入这些依赖项。这个过程基本上是相反的,因此称为控制反转(IoC),bean本身通过使用类的直接构造或诸如服务定位器模式之类的机制来控制其依赖关系的实例化或位置。
org.springframework.beans
和 org.springframework.context
包是Spring框架的IoC容器的基础,该 BeanFactory 接口提供了一种能够管理任何类型对象的高级配置机制。 ApplicationContext 它是 BeanFactory 的一个子接口, 它更容易与Spring的AOP功能集成、消息资源处理(用于国际化)、事件发布和特定于应用程序层的上下文,例如WebApplicationContext 在Web应用程序中使用的上下文。
简而言之,它 BeanFactory 提供了配置框架和基本功能,并 ApplicationContext 添加了更多特定于企业的功能。
在Spring中,构成应用程序主干并由 Spring IoC 容器管理的对象称为 bean。bean是一个由 Spring IoC 容器实例化、组装和管理的对象。Bean及其之间的依赖关系反映在容器使用的配置元数据中。
二、容器概述
org.springframework.context.ApplicationContext
接口代表Spring IoC容器,负责实例化,配置和组装上述bean。容器通过读取配置元数据获取有关要实例化,配置和组装的对象的指令。配置元数据以XML,Java注释或Java代码表示。它允许表达组成应用程序的对象以及这些对象之间丰富的相互依赖性。
ApplicationContext是Spring的开箱即用的几个接口实现 。在独立应用程序中,通常会创建一个 ClassPathXmlApplicationContext 或的实例 FileSystemXmlApplicationContext。虽然XML是定义配置元数据的传统格式,但您可以通过提供少量XML配置来指示容器使用Java注释或代码作为元数据格式,以声明方式启用对这些其他元数据格式的支持。
1 public void testAutoBeanName() { 2 ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("springStyle/declaration/auto/spring-config-bean-name.xml"); 3 UserService proxy = (UserService) ctx.getBean("userService"); 4 String ret = proxy.queryAllUser(); 5 System.out.println("执行结果:" + ret); 6 proxy.saveUser("zhangsan"); 7 }
下图是Spring工作原理的高级视图。您的应用程序类与配置元数据相结合,以便在ApplicationContext创建和初始化之后,拥有一个完全配置且可执行的系统或应用程序。
1. 配置元数据
Spring IoC容器使用一种配置元数据形式; 此配置元数据表示您作为应用程序开发人员如何告诉Spring容器在应用程序中实例化,配置和组装对象。传统上,配置元数据以简单直观的XML格式提供。
在Spring容器中使用其他形式的元数据的方式有:
- 基于注释的配置
- 基于Java的配置
这些bean定义对应于构成应用程序的实际对象。通常,您定义服务层对象,数据访问对象(DAO),表示对象(如Struts Action实例),基础结构对象(如Hibernate SessionFactories,JMS Queues等)。通常,不会在容器中配置细粒度域对象,因为创建和加载域对象通常由DAO和业务逻辑负责。但是,您可以使用Spring与AspectJ的集成来配置在IoC容器控制之外创建的对象。
基于XML的配置元数据的基本结构:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> 6 7 <bean id="..." class="..."> 8 <!-- collaborators and configuration for this bean go here --> 9 </bean> 10 11 <bean id="..." class="..."> 12 <!-- collaborators and configuration for this bean go here --> 13 </bean> 14 15 <!-- more bean definitions go here --> 16 17 </beans>
2. 实例化容器
实例化Spring IoC容器非常简单。提供给 ApplicationContext 构造函数的位置路径实际上是资源字符串,允许容器从各种外部资源(如本地文件系统,Java等)加载配置元数据 CLASSPATH。
以下示例显示了服务层对象 (services.xml)
配置文件:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> 6 7 <!-- services --> 8 9 <bean id="petStore" 10 class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl"> 11 <property name="accountDao" ref="accountDao"/> 12 <property name="itemDao" ref="itemDao"/> 13 <!-- additional collaborators and configuration for this bean go here --> 14 </bean> 15 16 <!-- more bean definitions for services go here --> 17 18 </beans>
以下示例显示了数据访问对象 daos.xml
文件
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> 6 7 <bean id="accountDao" 8 class="org.springframework.samples.jpetstore.dao.ibatis.SqlMapAccountDao"> 9 <!-- additional collaborators and configuration for this bean go here --> 10 </bean> 11 12 <bean id="itemDao" class="org.springframework.samples.jpetstore.dao.ibatis.SqlMapItemDao"> 13 <!-- additional collaborators and configuration for this bean go here --> 14 </bean> 15 16 <!-- more bean definitions for data access objects go here --> 17 18 </beans>
在前面的示例中,服务层由类组成 PetStoreServiceImpl,并且类型 SqlMapAccountDao 和 SqlMapItemDao 的两个数据访问对象基于 iBatis 对象/关系映射框架。该property name元素是指JavaBean属性的名称,以及ref元素指的是另一个bean定义的名称。id和ref元素之间的这种联系表达了协作对象之间的依赖关系。
3. 使用容器
ApplicationContext 是高级工厂的接口,能够维护不同bean及其依赖项的注册表。使用该方法,T getBean(Stringname, Class<T> requiredType) 您可以检索Bean的实例。
在 ApplicationContext 可以读取bean定义并访问它们,如下所示:
1 //创建和配置bean 2 ApplicationContext context = new ClassPathXmlApplicationContext(new String [] { “services.xml”,“daos.xml” }); //检索已配置的实例 3 PetStoreServiceImpl service = context.getBean(“petStore”,PetStoreServiceImpl .class); //使用已配置的实例列表 4 userList service.getUsernameList(); 5
三、Bean概述
Spring IoC容器管理一个或多个bean。这些bean是使用您提供给容器的配置元数据创建的,例如,以XML <bean/>定义的形式 。
在容器本身内,这些bean定义表示为 BeanDefinition 对象,其中包含(以及其他信息)以下元数据:
-
包限定的类名:通常是正在定义的bean的实际实现类。
-
Bean行为配置元素,说明bean在容器中的行为方式(范围,生命周期回调等)。
-
引用bean执行其工作所需的其他bean; 这些引用也称为 协作者 或 依赖项。
-
要在新创建的对象中设置的其他配置设置,例如,在管理连接池的Bean中使用的连接数,或池的大小限制。
每个bean都有一个或多个标识符。这些标识符在托管bean的容器中必须是唯一的。bean通常只有一个标识符,但如果它需要多个标识符,则额外的标识符可以被视为别名。
在基于XML的配置元数据中,使用 id(
和/或)name属性指定bean标识符。该id
属性允许您指定一个id,并且因为它是一个真正的XML元素ID属性,所以当其他元素引用id时,XML解析器可以进行一些额外的验证。因此,它是指定bean标识符的首选方式。但是,XML规范确实限制了XML ID中合法的字符。这通常不是约束,但如果您需要使用其中一个特殊的XML字符,或者想要向bean引入其他别名,您还可以在name属性中指定它们 ,用逗号(,
),分号(;
)分隔,或者是空格。
如果没有显式提供名称或标识,则容器会为该bean生成唯一的名称。但是,如果要通过名称引用该bean,则必须通过使用ref元素或服务位置样式查找来提供名称。不提供名称的动机与使用内部bean和自动装配协作者有关。
bean定义本质上是用于创建一个或多个对象的方法。容器在被询问时查看命名bean的方法,并使用由该bean定义封装的配置元数据来创建(或获取)实际对象。如果使用基于XML的配置元数据,则指定要在元素的class属性中实例化的对象的类型(或类)。此 class 属性在内部是实例 Class上的属性 BeanDefinition,通常是必需的。可以通过Class以下两种方式之一使用该属性:
-
通常,在容器本身通过反向调用其构造函数直接创建bean的情况下指定要构造的bean类,稍微等同于使用new运算符的Java代码。
-
指定包含实际的类static将被调用以创建该对象,在容器调用一个不常见的情况工厂方法static工厂的一类方法来创建bean。从调用static工厂方法返回的对象类型可以完全是同一个类或另一个类
四、依赖性
1. 依赖注入
依赖注入(DI)是一个过程,通过这个过程,对象定义它们的依赖关系,即它们使用的其他对象,只能通过构造函数参数,工厂方法的参数或在构造或返回对象实例后在对象实例上设置的属性。从工厂方法。然后容器在创建bean时注入这些依赖项。这个过程基本上是相反的,因此名称 Inversion of Control(IoC),bean本身通过使用类的直接构造或服务定位器模式来控制其依赖项的实例化或位置。
使用DI原理的代码更清晰,当对象提供其依赖项时,解耦更有效。该对象不查找其依赖项,也不知道依赖项的位置或类。因此,您的类变得更容易测试,特别是当依赖关系在接口或抽象基类上时,这允许在单元测试中使用存根或模拟实现。
DI存在两个主要变体,基于构造函数的依赖注入 和 基于Setter的依赖注入。
基于构造函数 的DI由容器调用具有多个参数的构造函数来完成,每个参数表示一个依赖项。调用static
具有特定参数的工厂方法来构造bean几乎是等效的,本讨论同样处理构造函数和static
工厂方法的参数。以下示例显示了一个只能通过构造函数注入进行依赖注入的类。请注意,此类没有什么 特别之处,它是一个POJO,它不依赖于容器特定的接口,基类或注释。
基于setter 的DI是在调用无参数构造函数或无参数static
工厂方法来实例化bean之后,通过容器调用bean上的setter方法来完成的。
容器执行bean依赖性解析,如下所示:
-
使用
ApplicationContext
描述所有bean的配置元数据创建和初始化。可以通过XML,Java代码或注释指定配置元数据。 -
对于每个bean,如果使用的是依赖于普通构造函数的,那么它的依赖关系将以属性,构造函数参数或static-factory方法的参数的形式表示。实际创建 bean 时,会将这些依赖项提供给bean 。
-
每个属性或构造函数参数都是要设置的值的实际定义,或者是对容器中另一个bean的引用。
-
作为值的每个属性或构造函数参数都从其指定格式转换为该属性或构造函数参数的实际类型。默认情况下,Spring能够转换成字符串格式提供给所有的内置类型,比如数值
int
,long
,String
,boolean
,等。
Spring容器在创建容器时验证每个bean的配置,包括验证bean引用属性是否引用有效bean。但是,在实际创建 bean之前,不会设置bean属性本身。创建容器时会创建单例作用域并设置为预先实例化(默认值)的Bean。范围有singleton、prototype、request、session、global session。否则,仅在请求时才创建bean。创建bean可能会导致创建bean的图形,因为bean的依赖关系及其依赖关系(依此类推)被创建和分配。
2. 使用依赖depend-on
如果bean是另一个bean的依赖项,通常意味着将一个bean设置为另一个bean的属性。 通常,您可以使用基于XML的配置元数据中的<ref />元素来完成此操作。 但是,有时bean之间的依赖关系不那么直接; 例如,需要触发类中的静态初始化程序,例如数据库驱动程序注册。 在初始化使用此元素的bean之前,depends-on属性可以显式强制初始化一个或多个bean。 以下示例使用depends-on属性表示对单个bean的依赖关系:
1 <bean id="beanOne" class="ExampleBean" depends-on="manager"/> 2 3 <bean id="manager" class="ManagerBean" />
要表示对多个bean的依赖关系,请提供bean名称列表作为depends-on属性的值,使用逗号,空格和分号作为有效分隔符:
1 <bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao"> 2 <property name="manager" ref="manager" /> 3 </bean> 4 5 <bean id="manager" class="ManagerBean" /> 6 <bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
3. Lazy-initialized beans
默认情况下,ApplicationContext实现会急切地创建和配置所有单例bean,作为初始化过程的一部分。 通常,这种预先实例化是可取的,因为配置或周围环境中的错误是立即发现的,而不是几小时甚至几天后。 如果不希望出现这种情况,可以通过将bean定义标记为延迟初始化来阻止单例bean的预实例化。 延迟初始化的bean告诉IoC容器在第一次请求时创建bean实例,而不是在启动时。
在XML中,此行为由<bean />元素上的lazy-init属性控制; 例如:
1 <bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/> 2 3 <bean name="not.lazy" class="com.foo.AnotherBean"/>
当ApplicationContext使用前面的配置时,在ApplicationContext启动时,不会急切地预先实例化名为lazy的bean,而是非常预先实例化not lazy bean。
但是,当延迟初始化的bean是未进行延迟初始化的单例bean的依赖项时,ApplicationContext会在启动时创建延迟初始化的bean,因为它必须满足单例的依赖关系。 惰性初始化的bean被注入到其他地方的单独的bean中,而这个bean并不是惰性初始化的。
您还可以使用<beans />元素上的default-lazy-init属性在容器级别控制延迟初始化; 例如:
1 <beans default-lazy-init="true"> 2 <!-- no beans will be pre-instantiated... --> 3 </beans>
4. 自动装配合作者 Autowiring collaborators
Spring容器可以自动连接协作bean之间的关系。您可以通过检查ApplicationContext的内容,允许Spring自动为您的bean解析协作者(其他bean)。自动装配具有以下优点:
- 自动装配可以显着减少指定属性或构造函数参数的需要。
- 自动装配可以随着对象的发展更新配置。例如,如果需要向类添加依赖项,则可以自动满足该依赖项,而无需修改配置。因此,自动装配在开发期间尤其有用,而不会在代码库变得更稳定时否定切换到显式布线的选项。
使用基于XML的配置元数据时,可以使用<bean />元素的autowire属性为bean定义指定autowire模式。自动装配功能有五种模式:no(不自动装载)、byName(根据实例变量名称自动装载)、byType(根据实例变量类型自动装载)、constructor(根据构造方法参数类型自动装载)、autodetect(byType方式和constructor方式结合)。您指定每个bean的自动装配,因此可以选择要自动装配的那些。
自动装配的限制和缺点:
- property和constructor-arg设置中的显式依赖项始终覆盖自动装配。您无法自动装配所谓的简单属性,例如基元,字符串和类(以及此类简单属性的数组)。这种限制是按设计的。
- 自动装配不如显式布线精确。尽管如上表所示,Spring会小心避免在可能产生意外结果的歧义的情况下进行猜测,但不再明确记录Spring管理对象之间的关系。
- 可能无法为可能从Spring容器生成文档的工具提供接线信息。
- 容器中的多个bean定义可以匹配setter方法或构造函数参数指定的类型以进行自动装配。对于数组,集合或地图,这不一定是个问题。但是,对于期望单个值的依赖关系,这种模糊性不是任意解决的。如果没有可用的唯一bean定义,则抛出异常。
相关注解:
@Autowired注解的作用是由AutowiredAnnotationBeanPostProcessor实现的,查看该类的源码会发现它实现了MergedBeanDefinitionPostProcessor接口,进而实现了接口中的postProcessMergedBeanDefinition方法,@Autowired注解正是通过这个方法实现注入类型的预解析,将需要依赖注入的属性信息封装到InjectionMetadata类中,InjectionMetadata类中包含了哪些需要注入的元素及元素要注入到哪个目标类中。
1 @Override 2 public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) { 3 InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null); 4 metadata.checkConfigMembers(beanDefinition); 5 }
Spring容器在启动的时候会执行AbstractApplicationContext类的refresh方法,refresh方法中registerBeanPostProcessors(beanFactory)完成了对AutowiredAnnotationBeanPostProcessor的注册。
1 @Override 2 public void refresh() throws BeansException, IllegalStateException { 3 synchronized (this.startupShutdownMonitor) { 4 // Prepare this context for refreshing. 5 prepareRefresh(); 6 7 // Tell the subclass to refresh the internal bean factory. 8 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 9 10 // Prepare the bean factory for use in this context. 11 prepareBeanFactory(beanFactory); 12 13 try { 14 // Allows post-processing of the bean factory in context subclasses. 15 postProcessBeanFactory(beanFactory); 16 17 // Invoke factory processors registered as beans in the context. 18 invokeBeanFactoryPostProcessors(beanFactory); 19 20 // Register bean processors that intercept bean creation. 21 registerBeanPostProcessors(beanFactory); 22 23 // Initialize message source for this context. 24 initMessageSource(); 25 26 // Initialize event multicaster for this context. 27 initApplicationEventMulticaster(); 28 29 // Initialize other special beans in specific context subclasses. 30 onRefresh(); 31 32 // Check for listener beans and register them. 33 registerListeners(); 34 35 // Instantiate all remaining (non-lazy-init) singletons. 36 finishBeanFactoryInitialization(beanFactory); 37 38 // Last step: publish corresponding event. 39 finishRefresh(); 40 } 41 42 catch (BeansException ex) { 43 if (logger.isWarnEnabled()) { 44 logger.warn("Exception encountered during context initialization - " + 45 "cancelling refresh attempt: " + ex); 46 } 47 48 // Destroy already created singletons to avoid dangling resources. 49 destroyBeans(); 50 51 // Reset 'active' flag. 52 cancelRefresh(ex); 53 54 // Propagate exception to caller. 55 throw ex; 56 } 57 58 finally { 59 // Reset common introspection caches in Spring's core, since we 60 // might not ever need metadata for singleton beans anymore... 61 resetCommonCaches(); 62 } 63 } 64 }
当执行finishBeanFactoryInitialization(beanFactory)方法对非延迟初始化的单例bean进行初始化时,会执行到AbstractAutowireCapableBeanFactory类的doCreateBean方法
1 // Allow post-processors to modify the merged bean definition. 2 synchronized (mbd.postProcessingLock) { 3 if (!mbd.postProcessed) { 4 try { 5 applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); 6 } 7 catch (Throwable ex) { 8 throw new BeanCreationException(mbd.getResourceDescription(), beanName, 9 "Post-processing of merged bean definition failed", ex); 10 } 11 mbd.postProcessed = true; 12 } 13 }
在这段代码中会执行applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName),深入到这个applyMergedBeanDefinitionPostProcessors方法中,
1 protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) { 2 for (BeanPostProcessor bp : getBeanPostProcessors()) { 3 if (bp instanceof MergedBeanDefinitionPostProcessor) { 4 MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp; 5 bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName); 6 } 7 } 8 }
会发现这里调用的是AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition方法
在AbstractAutowireCapableBeanFactory#doCreateBean方法中有一段执行populateBean方法实现对属性的注入
1 // Initialize the bean instance. 2 Object exposedObject = bean; 3 try { 4 populateBean(beanName, mbd, instanceWrapper); 5 exposedObject = initializeBean(beanName, exposedObject, mbd); 6 } 7 catch (Throwable ex) { 8 ... 9 }
下面是AbstractAutowireCapableBeanFactory#populateBean方法中的一段代码
1 if (hasInstAwareBpps) { 2 if (pvs == null) { 3 pvs = mbd.getPropertyValues(); 4 } 5 for (BeanPostProcessor bp : getBeanPostProcessors()) { 6 if (bp instanceof InstantiationAwareBeanPostProcessor) { 7 InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; 8 PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); 9 if (pvsToUse == null) { 10 if (filteredPds == null) { 11 filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); 12 } 13 pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); 14 if (pvsToUse == null) { 15 return; 16 } 17 } 18 pvs = pvsToUse; 19 } 20 } 21 } 22 if (needsDepCheck) { 23 if (filteredPds == null) { 24 filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); 25 } 26 checkDependencies(beanName, mbd, filteredPds, pvs); 27 }
这段代码中会遍历所有注册过的BeanPostProcessor接口实现类的实例,如果实例属于InstantiationAwareBeanPostProcessor类型的,则执行实例类的postProcessPropertyValues方法。
1 @Deprecated 2 @Override 3 public PropertyValues postProcessPropertyValues( 4 PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) { 5 6 return postProcessProperties(pvs, bean, beanName); 7 }
1 @Override 2 public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { 3 InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); 4 try { 5 metadata.inject(bean, beanName, pvs); 6 } 7 catch (BeanCreationException ex) { 8 throw ex; 9 } 10 catch (Throwable ex) { 11 throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex); 12 } 13 return pvs; 14 }
metadata.inject(bean, beanName, pvs)代码的执行会进入如下inject方法中,在这里完成依赖的注入。
1 public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { 2 Collection<InjectedElement> checkedElements = this.checkedElements; 3 Collection<InjectedElement> elementsToIterate = 4 (checkedElements != null ? checkedElements : this.injectedElements); 5 if (!elementsToIterate.isEmpty()) { 6 for (InjectedElement element : elementsToIterate) { 7 if (logger.isTraceEnabled()) { 8 logger.trace("Processing injected element of bean '" + beanName + "': " + element); 9 } 10 element.inject(target, beanName, pvs); 11 } 12 } 13 }
InjectedElement有两个子类,分别是AutowiredFieldElement和AutowiredMethodElement。AutowiredFieldElement用于对标注在属性上的注入,AutowiredMethodElement用于对标注在方法上的注入。LookupElement表示有关注释字段或setter方法的通用注入信息的类,支持@Resource和相关注释。
AutowiredFieldElement#inject和AutowiredMethodElement#inject中部分代码,两种方式的注入过程都差不多,根据需要注入的元素的描述信息,按类型或名称查找需要的依赖值,如果依赖没有实例化先实例化依赖,然后使用反射进行赋值。
1 if (value != null) { 2 ReflectionUtils.makeAccessible(field); 3 field.set(bean, value); 4 }
1 if (arguments != null) { 2 try { 3 ReflectionUtils.makeAccessible(method); 4 method.invoke(bean, arguments); 5 } 6 catch (InvocationTargetException ex) { 7 throw ex.getTargetException(); 8 } 9 }
@Autowired是spring框架提供的实现依赖注入的注解,主要支持在set方法,field,构造函数中完成bean注入,注入方式为通过类型查找bean,即byType的,如果存在多个同一类型的bean,则使用@Qualifier来指定注入哪个beanName的bean。
@Resource注解在功能和目的上,等效于Autowried+Qualifier注解。@Resource是基于bean的名字,即beanName,来从spring的IOC容器查找bean注入的,而@Autowried是基于类型byType来查找bean注入的。
Resource注解则顺序不同,它有如下几种可能的情况:
- Resource注解指定了name属性和type属性 策略:首先进行按名称匹配策略: 匹配name属性和bean的id,如果匹配,则判断查找到的bean是否是type属性指定的类型,如果是type属性指定的类型,则匹配成功。如果不是type属性指定的类型,则抛出异常,提示匹配失败;如果name属性跟bean的id不匹配,则抛出异常提示没有bean的id匹配name属性
- Resource注解指定了name属性,未指定type属性 策略:查找bean的id为name属性的bean,查找到,不关心类型为什么,都是匹配成功;如果找不到name属性指定的bean id,则匹配失败,抛出异常
- Resource注解指定了type属性,未指定name属性 策略:首先进行按名称匹配策略: 匹配属性名和bean的id,如果匹配,则判断查找到的bean是否是type属性指定的类型,如果是type属性指定的类型,则匹配成功。如果不是type属性指定的类型,则抛出异常,提示匹配失败;其次进行按类型匹配策略: 如果属性名跟bean的id不匹配,则查找类型为type的bean,如果仅仅找到一个,自动装配成功,其它情况失败。
- Resource注解未指定type属性和name属性 策略:首先进行按属性名匹配策略,匹配则注入成功;如果属性名不匹配,则进行类型匹配策略,只有为一个类型匹配才成功,其他情况都失败。
@Inject也是基于类型来查找bean注入的,如果需要指定名称beanName,则可以结合使用@Named注解,而@Autowired是结合@Qualifier注解来指定名称beanName。
spring依赖注入注解的实现原理:
- 在spring框架内部实现当中,注解实现注入主要是通过bean后置处理器BeanPostProcessor接口的实现类来生效的。BeanPostProcessor后置处理器是在spring容器启动时,创建bean对象实例后,马上执行的,对bean对象实例进行加工处理。
- @Autowired是通过BeanPostProcessor接口的实现类AutowiredAnnotationBeanPostProcessor来实现对bean对象对其他bean对象的依赖注入的;
- @Resource和@Inject是通过BeanPostProcessor接口的实现类CommonAnnotationBeanPostProcessor来实现的,其中如名字所述,即公共注解CommonAnotation,CommonAnnotationBeanPostProcessor是spring中统一处理JDK中定义的注解的一个BeanPostProcessor。该类会处理的注解还包括@PostConstruct,@PreDestroy等。
相关文章参考: