Spring中依赖注入底层原理与源码分析
Posted 愉悦滴帮主)
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring中依赖注入底层原理与源码分析相关的知识,希望对你有一定的参考价值。
Spring中依赖注入底层原理与源码分析
前言:Spring框架是一套非常成熟的框架,同时也被大多数开发者所喜欢。在Spring中我们通常用到@Autowired注解,也就是我们常说的依赖注入。那么本篇文章带大家一起分析@Autowired注解的底层逻辑是什么,它是如何帮助Spring实现依赖注入的。这也是面试中常问到的。下面我们开具体分析。
Spring中到底有几种依赖方式?
在了解Spring中依赖注入底层原理之前先问大家一个问题,那就是Spring中到底有几种依赖方式?网上有的说3种,有的说5种。在这里我们从Spring的源头去分析,首先依赖注入分为手动注入和自动注入两种情况。手动注入:指的是开发者自己在XML文件给对应的bean手动的赋值。自动注入值得是Spring自动帮助我们进行依赖注入。
情况一:手动注入之set方法注入,例如:
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--扫描service包-->
<context:component-scan base-package="com.autowired.service"></context:component-scan>
<!--装载bean -->
<bean name="userService" class="com.autowired.service.UserService">
<property name="orderService" ref="orderService"></property>
</bean>
<bean name="orderService" class="com.autowired.service.OrderService"></bean>
</beans>
@Component
public class UserService {
private OrderService orderService;
public void setOrderService(OrderService orderService) {
this.orderService = orderService;
}
public void test(){
System.out.println(orderService);
}
}
情况一:手动注入之构造方法注入,例如:
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--扫描service包-->
<context:component-scan base-package="com.autowired.service"></context:component-scan>
<!--装载bean -->
<bean name="userService" class="com.autowired.service.UserService">
<constructor-arg index="0" ref="orderService"></constructor-arg> </bean>
<bean name="orderService" class="com.autowired.service.OrderService"></bean>
</beans>
package com.autowired.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class UserService {
private OrderService orderService;
public UserService(OrderService orderService) {
this.orderService = orderService;
}
public void test(){
System.out.println(orderService);
}
}
情况二:自动注入之XML自动注入
1.set方法注入
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--扫描service包-->
<context:component-scan base-package="com.autowired.service"></context:component-scan>
<!--装载bean -->
<bean name="orderService" class="com.autowired.service.OrderService"></bean>
<!--autowire属性指定类型(byType,byName) 自动注入 -->
<bean name="userService" class="com.autowired.service.UserService" autowire="byName"></bean>
</beans>
@Component
public class UserService {
private OrderService orderService;
public void setOrderService(OrderService orderService) {
this.orderService = orderService;
}
public void test(){
System.out.println(orderService);
}
}
set方法注入的自动注入同样的也分为两种方式:
- 一种是byName,byName的工作流程:我们以UserService这个类为例,Spring在启动的时候会解析你的XML文件,解析过后发现你是通过byName方式去自动注入的,然后就会去UserService这个类去找所有的Set方法,取Set方法的名字(例如:setOrderService---->orderService),然后根据orderService这个beanName去Spring容器中去找这个bean对象,然后将这个bean对象赋值给UserService的OrderService属性。
- 一种是byType。byType的工作流程:我们以UserService这个类为例,Spring在启动的时候会解析你的XML文件,解析过后发现你是通过byType方式去自动注入的,,然后就会去UserService这个类去找所有的Set方法,取Set方法的第一个参数的类型(OrderService),然后根据这个类型去Spring容器中去找对应的bean对象,然后将这个bean对象赋值给UserService的OrderService属性。
注意:Set方法的依赖注入byType方式,有可能造成找到多个bean对象。Spring中有一套机制去筛选出唯一的一个bean。我们后面去了解这套机制。
2.构造方法注入
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--扫描service包-->
<context:component-scan base-package="com.autowired.service"></context:component-scan>
<!--装载bean -->
<bean name="orderService" class="com.autowired.service.OrderService"></bean>
<!--autowire属性指定类型(byType,byName) 自动注入 -->
<bean name="userService" class="com.autowired.service.UserService" autowire="constructor"></bean>
</beans>
package com.autowired.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class UserService {
private OrderService orderService;
public UserService(OrderService orderService) {
this.orderService = orderService;
}
public void test(){
System.out.println(orderService);
}
}
构造方法注入的工作流程:我们还是以UserService这个类为例,Spring在启动的时候会解析你的XML文件,解析过后发现你是通过constructor方式去自动注入的,然后就会去UserService这个类去找构造方法,取构造方法的类型(OrderService),然后根据OrderService这个类型去Spring容器中去找这个bean对象,如果发现找到了多个对象,在根据构造方法的参数名(orderService)去找,找到唯一的一个,然后将这个bean对象赋值给UserService的OrderService属性。
注意:Spring在使用XML时默认将<bean name="userService" class="com.autowired.service.UserService" autowire="byName">autowire这个功能时关闭的。
情况二:@Autowired注解的自动注入
随着Spring的发展,Spring将XML中的一些功能以注解的形式抽取出来,并作出了一些优化。例如:<bean>标签抽取成@bean,autowire抽取成@Autowired注解等等。。
@Autowired的注解有两种使用方式:分别是ClassPathXmlApplicationContext 的XML配置文件形式和AnnotationConfigApplicationContext的配置类的形式。
/**
* 使用AnnotationConfigApplicationContext可以实现基于Java的配置类加载Spring的应用上下文。
* 避免使用application.xml进行配置。相比XML配置,更加便捷。
*/
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("Spring.xml");
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
在使用XML配置文件需要添加下面配置: 打开注解使用权限
<!--打开注解使用权限 -->
<context:annotation-config></context:annotation-config>
用@Autowired注解存在多种方式:例如在属性,构造方法,普通方法上注入。
//注意:@Autowired只能加一次,这里为了阅读效果才这样写。
@Component
public class UserService {
@Autowired
private OrderService orderService;
@Autowired
public UserService(OrderService orderService) {
this.orderService = orderService;
}
@Autowired
public void fangfa(OrderService orderService,OrderService orderService1){
this.orderService = orderService;
}
public void test(){
System.out.println(orderService);
}
}
普通方法注入的工作过程:首先会根据普通方法的第一个参数的类型去Spring中去找bean,如果找到多个bean对象,再根据第一个参数的名称去确定唯一的一个。这样参数一就有值了,也就是先byType再byName。参数二同参数一。
总结:
无论在是属性,构造,普通方法用@Autowired注解,都是通过先byType再byName的形式。
1.Spring中到底有几种依赖方式?
1.手动注入(set方法、构造方法)
2.自动注入
1.XML自动注入(set方法、构造方法)
2.@Autowired注解的自动注入(属性、构造方法、普通方法)
@Autowired注解的底层原理
如果想要了解@Autowired底层原理,我们要去看AutowiredAnnotationBeanPostProcessor这个类实现了@Autowired注解的功能。
@Autowired注解的底层原理工作流程
@Autowired底层原理之注入点扫描
1.寻找注入点调用方法
InjectionMetadata表示当前这个类所有注入点的集合。InjectedElement表示注入点,这个类有两个子类:AutowiredFieldElement表示属性的注入点、AutowiredMethodElement表示方法的注入点。
注入点就是在某个类中加了@Autowired注解的某个属性或方法。
//寻找注入点
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
//获取beanType中的注入点
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
注入点的定义
public abstract static class InjectedElement {
//是否注入的是一个方法
protected final Member member;
//是否注入的是一个字段
protected final boolean isField;
@Nullable
protected final PropertyDescriptor pd;
@Nullable
protected volatile Boolean skip;
....省略
}
寻找注入点的外层逻辑。
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// 先去缓存中寻找注入点,如果找不到则调用真正寻找的方法buildAutowiringMetadata
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
//寻找当前Clazz中的注入点,把所有注入点整合成为一个InjectionMetadata对象
metadata = buildAutowiringMetadata(clazz);
//将找到的注入点放入缓存,方便下次调用
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
寻找注入点的内部逻辑。
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
//判断是不是候选者类,比如说类名,如果是以”Java“开头的则不是候选者类
if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
return InjectionMetadata.EMPTY;
}
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
//遍历属性,看是否有@Autowired,@Value,@Inject(注入)注解
ReflectionUtils.doWithLocalFields(targetClass, field -> {
MergedAnnotation<?> ann = findAutowiredAnnotation(field);
//如果存在@Autowired,@Value,@Inject注解中的其中一个
if (ann != null) {
//如果字段是静态(static)的,则直接进行返回,不进行注入
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
//是否required(是否必须注入)
boolean required = determineRequiredStatus(ann);
//生成一个注入点AutowiredFieldElement
currElements.add(new AutowiredFieldElement(field, required));
}
});
//遍历方法,看是否有@Autowired,@Value,@Inject注解
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
//静态方法不能用来注入属性
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static methods: " + method);
}
return;
}
//方法的参数为0,不能用来注入属性
if (method.getParameterCount() == 0) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation should only be used on methods with parameters: " +
method);
}
}
是否required(是否必须注入)
boolean required = determineRequiredStatus(ann);
//根据方法找出对应的属性
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
//生成一个注入点AutowiredFieldElement
currElements.add(new AutowiredMethodElement(method, required, pd));
}
});
//将注入点放入集合中
elements.addAll(0, currElements);
//去父类寻找注入点
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return InjectionMetadata.forElements(elements, clazz);
}
以上就完成了注入点的扫描,并将所有的注入点封装为一个List<InjectionMetadata>,InjectionMetadata表示当前这个类所有注入点的集合
@Autowired底层原理之注入点注入
注入点进行注入之属性注入逻辑,这个方法属于AutowiredFieldElement这个类。
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
Object value;
if (this.cached) {
//当前注入点已经注入过了,有缓存了,则利用cachedFieldValue去找对应的bean
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
else {
//Spring在真正查找属性对应的对象之前,会先将该属性的描述封装成一个
DependencyDescriptor desc = new DependencyDescriptor(fieldDependencyDescriptor, this.required);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
Object value;
try {
//根据field去寻找合适的bean
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
} catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
//找到bean之后
synchronized (this) {
if (!this.cached) {
Object cachedFieldValue = null;
if (value != null || this.required) {
cachedFieldValue = desc;
//注册当前的bean依赖了哪些其他的bean的name
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
//对得到的对象进行缓存
this.cachedFieldValue = new AutowiredAnnotationBeanPostProcessor.ShortcutDependencyDescriptor(
desc, autowiredBeanName, field.getType());
}
}
} else {
this.cachedFieldValue = null;
}
this.cached = true;
}
}
}
//反射设值
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
根据field去寻找合适的bean的逻辑
@Override
@Nullable
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
//DependencyDescriptor表示一个依赖,可以是一个属性字段,可能是一个构造方法参数,可能是一个set方法参数
//根据descriptor属性去BeanFactory中去找到bean
descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
//如果依赖的类型是Optional
if (Optional.class == descriptor.getDependencyType()) {
return createOptionalDependency(descriptor, requestingBeanName);
}
//如果依赖的类型是ObjectFactory
else if (ObjectFactory.class == descriptor.getDependencyType() ||
ObjectProvider.class == descriptor.getDependencyType()) {
return new DefaultListableBeanFactory.DependencyObjectProvider(descriptor, requestingBeanName);
}
//如果依赖的类型是javaxInjectProviderClass
else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
return new DefaultListableBeanFactory.Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
}
else {
//在使用@Autowired注解时,也可以使用@Lazy注解,到时候注入的会是一个代理对象
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
if (result == null) {
//通过解析descriptor(属性)找到bean对象
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
}
@Autowired底层原理之候选者筛选
通过解析descriptor(属性)找到bean对象
/**
* 根据属性信息,去得到bean对象
* @param descriptor 属性
* @param beanName 当前bean的名字
* @param autowiredBeanNames 所有bean名称的集合
* @param typeConverter 属性类型
* @return
* @throws BeansException
*/
@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
try {
/**
* 如果DependencyDescriptor是一个ShortcutDependencyDescriptor,
* 那么会直接根据beanName从beanFactory中拿到一个bean,
* 再利用@Autowired注解来进行依赖注入时会利用ShortcutDependencyDescriptor来进行依赖注入的缓存,
* 表示当解析完某个依赖信息后,会把依赖的bean的beanName缓存起来
*/
Object shortcut = descriptor.resolveShortcut(this);
if (shortcut != null) {
return shortcut;
}
//获取descriptor具体的类型,某个field的类型或某个方法参数的类型
Class<?> type = descriptor.getDependencyType();
//获取@Value注解中所配置的值
//是否通过@Value注解指定的值
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);
//解析Spring EL表达式,解析”#“符号(可以进行运算,可以写某个bean的名字)
value = evaluateBeanDefinitionString(strVal, bd);
}
//如果得到的value值与field属性的类型,不匹配,则需要进行类型转换,需要开发者自己实现类型转换器的功能
//Spring并没有帮我们实现。如果没有类型转换器则会抛出异常。
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
try {
return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
}
catch (UnsupportedOperationException ex) {
// A custom TypeConverter which does not support TypeDescriptor resolution...
return (descriptor.getField() != null ?
converter.convertIfNecessary(value, type, descriptor.getField()) :
converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
}
}
//没有使用@Value注解的情况下走下面的代码
//如果要注入的依赖的类型是一个Map,Array,Collection
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
//直接返回所有的候选者
return multipleBeans;
}
//如果注入的依赖的类型不是一个Map,Array,Collection
//通过by_type找,可能找到多个bean对象,这里的value,可能时具体的对象,也可能暂时只是Class对象。也就是寻找候选者。
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
//如果matchingBeans为空
if (matchingBeans.isEmpty()) {
//判断@Autowired注解的isRequired是否为true
if (isRequired(descriptor)) {
//如果是true,则抛出异常
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
return null;
}
String autowiredBeanName;
Object instanceCandidate;
//根据type找到了多个bean对象
if (matchingBeans.size() > 1) {
//找到多个,去尝试确定出唯一的一个bean的名字
/**
* 筛选逻辑:去遍历所有的候选者是否有@Primary注解,如果只有一个bean有@Primary注解则返回这个bean对象的名字。
* 如果有多个bean都没有@Primary注解,则选出优先级最高的bean对象的名字。通过@Priority来定义优先级,数字越小
* 优先级越高。如果上述步骤都没有招待唯一的bean对象,则通过by_name去找。
*
* 如果都没有找到,则只能返回为空。
*/
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
//如果筛选后,返回为空
if (autowiredBeanName == null) {
if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
//如果找到多个,并且当前依赖是required,或者不是数组或Collection或Map,则抛出异常
return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
}
else {
// 直接返回为空
return null;
}
}
instanceCandidate = matchingBeans.get(autowiredBeanName);
}
else {
// 根据类型只能找到一个bean对象
Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
autowiredBeanName = entry.getKey();
instanceCandidate = entry.getValue();
}
//后续代码就是对找到的bean对象进行一些封装和校验
if (autowiredBeanNames != null) {
autowiredBeanNames.add(autowiredBeanName);
}
if (instanceCandidate instanceof Class) {
instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}
Object result = instanceCandidate;
if (result instanceof NullBean) {
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
result = null;
}
if (!ClassUtils.isAssignableValue(type, result)) {
throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
}
return result;
}
finally {
ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
}
}
以上是关于Spring中依赖注入底层原理与源码分析的主要内容,如果未能解决你的问题,请参考以下文章
深入浅出Spring原理及实战「源码原理实战」从底层角度去分析研究PropertySourcesPlaceholderConfigurer的原理及实战注入机制