spirng 的DI(dependency injection )依赖注入是我们在开发中最常用的,如setter注入、构造方法注入,还有我们的自动装配@Autowired、@Resource注入,它可以帮助我们更好的关注我们的业务开发,而无需关注DI依赖注入的实现细节。
为什么有时候我们还可以将一个接口的多个实现类通过map的形式进行注入呢?难道你在开发的过程中对spring如何实现通过一个注解就完成这些就不好奇吗?
为了更清晰的理解概念,本文将采用xml和java配置类结合的方式进行讲解。
两个实体类Person、Car
public class Person {
private String name;
private Car car;
// public Person(String name, Car car) {
// this.name = name;
// this.car = car;
// }
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
}
public class Car {
private String name;
private String color;
private String price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
}
使用xml方式完成setter注入,很简单的setter属性注入。
<bean id="car" class="com.upanda.app.test.Car">bean>
<bean id="person" class="com.upanda.app.test.Person">
<property name="car" ref="car"/>
<property name="name" value="coyhzx"/>
bean>
使用java配置类的方式完成setter注入,个人理解~就是调用setter方法注入属性。
public void setCar(Car car) {
this.car = car;
}
使用xml方式完成构造方法注入,很简单的setter属性注入。
<bean id="car" class="com.upanda.app.test.Car">bean>
<bean id="person" class="com.upanda.app.test.Person">
<constructor-arg name="name" value="coyhzx"/>
<constructor-arg name="car" ref="car"/>
bean>
java配置类的方式完成构造方法注入,个人理解就是通过构造方法注入。
public Person(String name, Car car) {
this.name = name;
this.car = car;
}
<bean id="person" class="com.upanda.app.test.Person" autowire="byName" >
bean>
至于java配置类方式的注入,最常见的就是service中,注入dao了,此处类似。
@Autowired
private Car car;
@Autowried自动装配的方式有三种,在Autowire枚举类中有定义
Autowire#NO:表示没有自动装配的常量(注入:并不代表这种就是不能自动装配)。
Autowire#BY_NAME:表示通过名称自动装配。
Autowire#BY_TYPE:表示通过类型自动装配。
在AutowireCapableBeanFactory类中定义了五种依赖注入,包括上述三种的自动装配。
AUTOWIRE_NO:表示没有外部的自动装配,但是BeanFactoryAware和注解驱动的注入仍旧能应用。
应用场景:在创建bean、指定autowire方式、bean的属性注入时应用。
AUTOWIRE_BY_NAME:表示通过名称依赖注入。
应用场景:在创建bean、指定autowire方式、bean的属性注入时应用。
AUTOWIRE_BY_TYPE:表示通过类型依赖注入。
应用场景:在创建bean、指定autowire方式、bean的属性注入时应用。
AUTOWIRE_CONSTRUCTOR:表示通过构造函数进行依赖注入。
应用场景:在创建bean、指定autowire方式。
AUTOWIRE_AUTODETECT:表示通过内省bean类依赖注入。
应用场景:在创建bean、指定autowire方式。
/**
* Constant that indicates no externally defined autowiring. Note that
* BeanFactoryAware etc and annotation-driven injection will still be applied.
* @see #createBean 创建bean时
* @see #autowire 自动注入
* @see #autowireBeanProperties bean的属性注入
*/
//指示没有外部定义的自动装配的常数。注意BeanFactoryAware等和注解驱动的注入将仍然适用。
int AUTOWIRE_NO = 0;
/**
* Constant that indicates autowiring bean properties by name
* (applying to all bean property setters).
* @see #createBean
* @see #autowire
* @see #autowireBeanProperties
*/
//通过名称指示自动装配Bean属性的常数,应用于所有bean属性设置
int AUTOWIRE_BY_NAME = 1;
/**
* Constant that indicates autowiring bean properties by type
* (applying to all bean property setters).
* @see #createBean
* @see #autowire
* @see #autowireBeanProperties
*/
//指示按类型自动装配Bean属性的常量,应用于所有bean属性设置
int AUTOWIRE_BY_TYPE = 2;
/**
* Constant that indicates autowiring the greediest constructor that
* can be satisfied (involves resolving the appropriate constructor).
* @see #createBean
* @see #autowire
*/
//指示自动装配可以满足最贪婪的构造函数的常数(涉及解析适当的构造函数)
int AUTOWIRE_CONSTRUCTOR = 3;
/**
* Constant that indicates determining an appropriate autowire strategy
* through introspection of the bean class.
* @see #createBean
* @see #autowire
* @deprecated as of Spring 3.0: If you are using mixed autowiring strategies,
* prefer annotation-based autowiring for clearer demarcation of autowiring needs.
*/
//指示通过内省bean类确定适当的自动装配策略的常数
@Deprecated
int AUTOWIRE_AUTODETECT = 4;
总结一下,其实依赖注入和自动装配的概率有点模糊,总之就是我们可以通过spring ioc容器自动的完成了我们的依赖注入和自动装配,如果非要说有什么区别,通过我刚刚给出的两个类里定义的依赖方式,可以发现依赖注入的本质是包含了自动装配。
好了,接下来我们就来具体看看源码是怎么完成 自动装配的。
在该方法中,重点就是从beanFacotry容器中解析依赖----后续是beanFactory根据类型查找Bean,这个不复杂,但是代码量还有点大,有兴趣的朋友可以继续跟踪一下。
最后获取到参数之后,bean的实例化时就是通过有参构造进行反射实例化bean。
protected Object resolveAutowiredArgument(MethodParameter param, String beanName,
@Nullable Set<String> autowiredBeanNames, TypeConverter typeConverter, boolean fallback) {
Class<?> paramType = param.getParameterType();
if (InjectionPoint.class.isAssignableFrom(paramType)) {
InjectionPoint injectionPoint = currentInjectionPoint.get();
if (injectionPoint == null) {
throw new IllegalStateException("No current InjectionPoint available for " + param);
}
return injectionPoint;
}
try {
//重点,从beanFacotry容器中解析依赖----后续是beanFactory根据类型查找Bean。
return this.beanFactory.resolveDependency(
new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter);
}
}
其他注入方式大同小异,有兴趣的可以自行了解一下。
@Autowired: 是spring自动装配的注解,默认按照类型byType进行自动装配。
@Resource:是jdk自带的一个注解,在spring中也具有自动装配功能,其中可以通过指定name,通过名称进行装配,也可以指定type,按照类型进行自动装配。同时指定同时指定name和type,这样只有两者都满足才能进行自动装配。
@Autowired和@Resource这两个注解都是通过后置处理器进行解析完成自动装配,属性填充的。
@Autowired注解是被AutowiredAnnotationBeanPostProcessor进行解析完成自动装配的。
@Resource注解是被CommonAnnotationBeanPostProcessor进行解析完成自动装配的。
两者直接或间接的实现了InstantiationAwareBeanPostProcessor接口,都有一个相同的方法postProcessProperties,处理bean的自动装配属性。
CommonAnnotationBeanPostProcessor#postProcessProperties方法。
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
}
return pvs;
}
AutowiredAnnotationBeanPostProcessor#postProcessProperties方法。
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
具体的依赖注入过程其实我在spring核心源码分析的第二篇文章的依赖注入中讲了,不重复叙述了。
整体的ioc容器,依赖注入的思想在一次次的源码分析之后,我并没有觉得很复杂。但是这也只是spring的核心思想之一,也是最基础的。只有在充分了解了bean的初始化过程,以及容器的初始化过程,在后续,我们才能慢慢的理解spring boot是为何、如何一步步将其做成启动依赖的自动装配。
只有自己用心思考,深入源码,才能在日常遇到问题时,做到处变不惊,冷静分析、解决问题。
学习优秀的代码编程设计思想,提升自我的代码编程设计思想。
上一篇:4、spring核心源码解析之自定义bean三种方式@import、ImportSelector、ImportBeanDefinitionRegistrar
下一篇:6、spring核心源码解析之aop概况流程1