百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

Spring的三种依赖注入方式的使用和源码实现分析

toyiye 2024-08-17 00:15 11 浏览 0 评论

(头条代码排版存在问题,可点击底部链接查看原文)

一、自动依赖注入的方式

注解类型

  • spring提供了基于注解的属性自动注入特性,其中可以可用的注解包括spring自身提供的@Autowired和@Value,其中@Autowired是我们在项目中最常用来注入对象属性的,@Value注解通常用于注入属性文件properties的值,除此之外还可以使用JSR-330提供的注解@Inject,类型为javax.inject.Inject。如下:
@Component
public class NettyServer {
 @Autowired
 private WebSocketService webSocketService;
 /**
 * 监听端口号
 */
 @Value("${netty.port}")
 private int port;
 
 // 省略其他代码
}
  • 关于以上注解的更多特性可参考:Spring实现依赖注入的三个注解:@Autowired,@Resource,@Inject

注解方式

  • 在使用@Autowired注解进行自动属性注入时,通常可以通过以下三种方式进行配置,分别为:构造函数注入,属性值注入,setter方法注入。如下:
@RestController
public class AccountController {
 // 属性值注入
 @Autowired
 private AccountService accountService;
 private UserService userService;
 private IndexService indexService;
 
 // 构造函数注入
 @Autowired
 public AccountController(UserService userService) {
 this.userService = userService;
 }
 
 // setter方法注入
 public void setIndexService(IndexService indexService) {
 this.indexService = indexService;
 }
 
 // 省略其他代码
}
  • 当通过构造函数和setter方法进行注入时,由于构造函数和setter方法都可以有多个参数,而@Autowired的required的值又是true,如下为@Autowired注解的定义:
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
	 // 依赖是必须的
	boolean required() default true;
}
  • 即默认需要所有属性值都存在,不能为null,否则会创建对象失败,如下:
// 构造函数注入
@Autowired
public AccountController(UserService userService, IndexService indexService) {
 this.userService = userService;
 this.indexService = indexService;
}
  • 所以如果某个属性值不是必须的,则可以使用Java8的Optional或者spring5的@Nullable来修饰,如下假如IndexServer不是必需的:
  • Optional的使用:
private Optional<IndexService> indexService;
// 构造函数注入
@Autowired
public AccountController(UserService userService, Optional<IndexService> indexService) {
 this.userService = userService;
 this.indexService = indexService;
}

@Nullable的使用:

// 构造函数注入
@Autowired
public AccountController(UserService userService, @Nullable IndexService indexService) {
 this.userService = userService;
 this.indexService = indexService;
}
  • 如果不想使用以上注解,则可以使用setter方式或属性值注入,或者不要将不需要注入的类放在构造函数或者方法上。

二、自动依赖注入的实现

  • 依赖注入是spring的IOC容器的一个重要特性,通过依赖注入来自动解决类对象之间的引用关系,即由spring来创建bean对象,并且在spring的IOC容器内部自动查找或者创建该bean对象所依赖的其他bean对象,从而保证整个bean对象的属性值的完整性。
  • 由以上分析可知,spring可以基于构造函数注入,属性值注入和setter方法注入,在spring的内部实现当中,这三种方式的实现是存在差别的。

构造函数注入

  • Java在创建每个对象实例时,都需要调用该对象对应的类的构造函数,所以spring在创建bean对象时,也会选择其中一个构造函数来创建该对象。当该类存在多个构造函数时,只能有一个构造函数使用required为true的@Autowired注解,其他构造函数如果也使用了@Autowired,则需要设置required为false。

spring选择构造函数的规则

  1. 选择能够成功注入最多bean对象的使用了@Autowired注解的构造函数,即基于贪婪的策略,注意不是选择包含最多参数这么简单,而是能够从spring的IOC容器获取bean对象并注入成功最多的构造函数。
  2. 或者如果某个类的所有构造函数都没有使用@Autowired注解,则spring会使用该类的默认构造函数,即如果没有显式定义任何构造函数,则使用默认的无参构造函数;如果只存在一个,则调用这个;如果存在多个且没有无参构造函数,也没有使用@Autowired注解,则会编译出错或者idea会提示构造函数有误,因为这种方式,spring无法确定使用哪个构造函数。
  • 以上选择构造函数的构造函数的核心源码实现如下:在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory类的createBeanInstance方法定义:
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
	// Make sure bean class is actually resolved at this point.
	// 类加载
	Class<?> beanClass = resolveBeanClass(mbd, beanName);
 
 // 省略其他代码
	// 查找该类的所有的构造函数,包括使用了@Autowired注解和没有使用@Autowired注解的
	Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
	// 基于贪婪原则选择能注入最多bean对象的构造函数
	if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
			mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
	 // 贪婪选择构造函数,执行构造函数属性注入与创建bean对象		
		return autowireConstructor(beanName, mbd, ctors, args);
	}
	// 指定了特定偏好的构造函数,默认为null
	ctors = mbd.getPreferredConstructors();
	if (ctors != null) {
		return autowireConstructor(beanName, mbd, ctors, null);
	}
	// 使用无参构造函数创建bean对象实例
	return instantiateBean(beanName, mbd);
}

构造函数属性注入

  • 以上分析了spring选择构造函数的规则,对于贪婪选择能注入最多bean对象的构造函数和完成构造函数属性注入,创建bean对象是在AbstractAutowireCapableBeanFactory类的autowireConstructor方法实现的:
protected BeanWrapper autowireConstructor(
		String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] ctors, @Nullable Object[] explicitArgs) {
	return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
}
  • 具体在ConstructorResolver类的autowireConstructor方法实现构造函数的选择和构造函数的属性依赖注入,autowireConstructor的核心实现如下:如果只存在一个显式定义的构造函数,则使用这个构造函数;否则先基于构造函数的参数个数对所有构造函数进行降序排序,然后遍历检查这些构造函数。选中最合适的构造函数后,则进行构造函数的属性对象的注入。注意使用构造函数进行属性注入存在循环依赖问题,具体后面文章详细分析spring的解决方案和无法解决的情况。请参考:Spring的构造函数注入的循环依赖问题
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
		@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
	 // 省略其他代码
		// 只有一个构造函数,则使用该构造函数即可
		if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
			Constructor<?> uniqueCandidate = candidates[0];
			if (uniqueCandidate.getParameterCount() == 0) {
				synchronized (mbd.constructorArgumentLock) {
					mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
					mbd.constructorArgumentsResolved = true;
					mbd.resolvedConstructorArguments = EMPTY_ARGS;
				}
				bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS));
				return bw;
			}
		}
		// 省略其他代码
		// 基于构造函数的参数个数排序,越多的越前,即降序排序
		AutowireUtils.sortConstructors(candidates);
		
		// 从所有构造函数中,基于贪婪规则筛选出能注入成功最多bean对象的构造函数
		for (Constructor<?> candidate : candidates) {
			// 构造函数的参数的类型
			Class<?>[] paramTypes = candidate.getParameterTypes();
			// 省略其他代码
			ArgumentsHolder argsHolder;
			if (resolvedValues != null) {
				try {
					// 省略其他代码
					// 获取构造函数参数对应的bean对象,在这里解决构造函数依赖注入
					argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
							getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
				}
				// 省略其他代码
			}
		
		// 省略其他代码
	bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
	return bw;
}
  • 在以上代码中,主要是在createArgumentArray方法处理构造函数的属性注入问题,最终会调用到BeanFactory的getBean方法从BeanFactory获取所依赖的其他对象。如果BeanFactory当前还不存在该依赖的bean对象,则会在getBean方法中创建该bean对象并返回。所以如果该被依赖的bean对象如果也在构造函数中依赖了当前正在创建的bean对象,则该依赖的bean对象就无法创建了,故出现了循环依赖问题,导致程序异常退出。createArgumentArray的核心实现如下:
private ArgumentsHolder createArgumentArray(
		String beanName, RootBeanDefinition mbd, @Nullable ConstructorArgumentValues resolvedValues,
		BeanWrapper bw, Class<?>[] paramTypes, @Nullable String[] paramNames, Executable executable,
		boolean autowiring, boolean fallback) throws UnsatisfiedDependencyException {
	// 省略其他代码
	// 遍历构造函数的参数列表
	for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {
	
		// 省略其他代码
		
			try {
				// 处理@Autowired自动注入的对象属性
				Object autowiredArgument = resolveAutowiredArgument(
						methodParam, beanName, autowiredBeanNames, converter, fallback);
				args.rawArguments[paramIndex] = autowiredArgument;
				args.arguments[paramIndex] = autowiredArgument;
				args.preparedArguments[paramIndex] = new AutowiredArgumentMarker();
				args.resolveNecessary = true;
			}
			
			// 省略其他代码
			
		}
	}
	// 省略其他代码
	return args;
}

属性值注入和setter方法注入

  • 属性值注入和setter方法注入是spring在创建该bean对象成功后,即调用构造函数创建了bean对象之后,在对该bean对象的属性值进行赋值时处理的,故属性值注入和setter方法注入不存在循环依赖问题,因为此时对象已经创建成功了,在这步进行属性注入主要是避免依赖的属性值为null。具体的方法调用顺序为:
  1. AbstractBeanFactory的getBean方法:getBean调用doGetBean方法,doGetBean方法内部调用createBean方法。
public Object getBean(String name) throws BeansException {
	return doGetBean(name, null, null, false);
}
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
 // 省略其他代码
	if (mbd.isSingleton()) {
		// 创建bean对象实例并注册到单例bean映射map中
		sharedInstance = getSingleton(beanName, () -> {
			try {
				// bean对象实例创建
				return createBean(beanName, mbd, args);
			}
		}
	}
	// 省略其他代码
}
  1. AbstractAutowireCapableBeanFactory类定义createBean的方法实现,在createBean方法内部调用doCreateBean方法完成bean对象的创建,包括调用populateBean方法进行属性赋值。populateBean方法的定义如下:
// 属性值赋值
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
	// 省略其他代码
	if (hasInstAwareBpps) {
		if (pvs == null) {
			pvs = mbd.getPropertyValues();
		}
		// 调用InstantiationAwareBeanPostProcessor的postProcessProperties和postProcessPropertyValues
		// 即AutowiredAnnotationBeanPostProcessor
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
			if (bp instanceof InstantiationAwareBeanPostProcessor) {
				InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
				// 进行属性处理,包括属性注入和setter方法注入
				PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
				
				// 省略其他代码
			}
		}
	}
	
	// 省略其他代码
}
  1. 所以属性值注入和setter方法注入是在AutowiredAnnotationBeanPostProcessor这个类的postProcessProperties方法处理的,AutowiredAnnotationBeanPostProcessor是一个BeanPostProcessor,在创建BeanFactory对象时会创建该BeanPostProcessor对象。
  • 在AutowiredAnnotationBeanPostProcessor内部是先进行属性注入,在进行方法注入,核心实现如下:
  1. 属性和setter方法进行属性值注入
// 属性和setter方法进行属性值注入
@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;
}

AutowiredAnnotationBeanPostProcessor内部初始化postProcessProperties中所使用的InjectionMetadata的方法:其中属性值注入是在AutowiredFieldElement定义的,方法注入是在AutowiredMethodElement定义的,这两个都是AutowiredAnnotationBeanPostProcessor的内部类。由以下代码可知,在elements数组中,属性值解析器AutowiredFieldElement在数组前面,故先遍历到;方法注入解析器AutowiredMethodElement在数组后面,故后遍历到。

private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
	List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
	Class<?> targetClass = clazz;
	do {
		final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
		
 // 1. 添加属性注入解析器AutowiredFieldElement到elements
		ReflectionUtils.doWithLocalFields(targetClass, field -> {
			AnnotationAttributes ann = findAutowiredAnnotation(field);
			if (ann != null) {
				// 省略其他代码
				boolean required = determineRequiredStatus(ann);
				// 属性注入解析器AutowiredFieldElement
				currElements.add(new AutowiredFieldElement(field, required));
			}
		});
 // 2. 添加方法注入解析器AutowiredMethodElement到elements
		ReflectionUtils.doWithLocalMethods(targetClass, method -> {
			// 省略其他代码
			AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
			if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
				// 省略其他代码
				boolean required = determineRequiredStatus(ann);
				PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
				// 方法注入解析器AutowiredMethodElement
				currElements.add(new AutowiredMethodElement(method, required, pd));
			}
		});
		elements.addAll(0, currElements);
		targetClass = targetClass.getSuperclass();
	}
	while (targetClass != null && targetClass != Object.class);
	return new InjectionMetadata(clazz, elements);
}
  1. InjectionMetadata的解析过程:遍历elements并调用AutowiredFieldElement或者AutowiredMethodElement的inject方法。AutowiredFieldElement先遍历到,AutowiredMethodElement后遍历到,故先进行属性注入,在进行方法注入。
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
	Collection<InjectedElement> checkedElements = this.checkedElements;
	Collection<InjectedElement> elementsToIterate =
			(checkedElements != null ? checkedElements : this.injectedElements);
	if (!elementsToIterate.isEmpty()) {
	
	 // 遍历elements并调用对应的inject方法
		for (InjectedElement element : elementsToIterate) {
			if (logger.isTraceEnabled()) {
				logger.trace("Processing injected element of bean '" + beanName + "': " + element);
			}
			element.inject(target, beanName, pvs);
		}
	}
}

相关推荐

# Python 3 # Python 3字典Dictionary(1)

Python3字典字典是另一种可变容器模型,且可存储任意类型对象。字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中,格式如...

Python第八课:数据类型中的字典及其函数与方法

Python3字典字典是另一种可变容器模型,且可存储任意类型对象。字典的每个键值...

Python中字典详解(python 中字典)

字典是Python中使用键进行索引的重要数据结构。它们是无序的项序列(键值对),这意味着顺序不被保留。键是不可变的。与列表一样,字典的值可以保存异构数据,即整数、浮点、字符串、NaN、布尔值、列表、数...

Python3.9又更新了:dict内置新功能,正式版十月见面

机器之心报道参与:一鸣、JaminPython3.8的热乎劲还没过去,Python就又双叒叕要更新了。近日,3.9版本的第四个alpha版已经开源。从文档中,我们可以看到官方透露的对dic...

Python3 基本数据类型详解(python三种基本数据类型)

文章来源:加米谷大数据Python中的变量不需要声明。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。在Python中,变量就是变量,它没有类型,我们所说的"类型"是变...

一文掌握Python的字典(python字典用法大全)

字典是Python中最强大、最灵活的内置数据结构之一。它们允许存储键值对,从而实现高效的数据检索、操作和组织。本文深入探讨了字典,涵盖了它们的创建、操作和高级用法,以帮助中级Python开发...

超级完整|Python字典详解(python字典的方法或操作)

一、字典概述01字典的格式Python字典是一种可变容器模型,且可存储任意类型对象,如字符串、数字、元组等其他容器模型。字典的每个键值key=>value对用冒号:分割,每个对之间用逗号,...

Python3.9版本新特性:字典合并操作的详细解读

处于测试阶段的Python3.9版本中有一个新特性:我们在使用Python字典时,将能够编写出更可读、更紧凑的代码啦!Python版本你现在使用哪种版本的Python?3.7分?3.5分?还是2.7...

python 自学,字典3(一些例子)(python字典有哪些基本操作)

例子11;如何批量复制字典里的内容2;如何批量修改字典的内容3;如何批量修改字典里某些指定的内容...

Python3.9中的字典合并和更新,几乎影响了所有Python程序员

全文共2837字,预计学习时长9分钟Python3.9正在积极开发,并计划于今年10月发布。2月26日,开发团队发布了alpha4版本。该版本引入了新的合并(|)和更新(|=)运算符,这个新特性几乎...

Python3大字典:《Python3自学速查手册.pdf》限时下载中

最近有人会想了,2022了,想学Python晚不晚,学习python有前途吗?IT行业行业薪资高,发展前景好,是很多求职群里严重的香饽饽,而要进入这个高薪行业,也不是那么轻而易举的,拿信工专业的大学生...

python学习——字典(python字典基本操作)

字典Python的字典数据类型是基于hash散列算法实现的,采用键值对(key:value)的形式,根据key的值计算value的地址,具有非常快的查取和插入速度。但它是无序的,包含的元素个数不限,值...

324页清华教授撰写【Python 3 菜鸟查询手册】火了,小白入门字典

如何入门学习python...

Python3.9中的字典合并和更新,了解一下

全文共2837字,预计学习时长9分钟Python3.9正在积极开发,并计划于今年10月发布。2月26日,开发团队发布了alpha4版本。该版本引入了新的合并(|)和更新(|=)运算符,这个新特性几乎...

python3基础之字典(python中字典的基本操作)

字典和列表一样,也是python内置的一种数据结构。字典的结构如下图:列表用中括号[]把元素包起来,而字典是用大括号{}把元素包起来,只不过字典的每一个元素都包含键和值两部分。键和值是一一对应的...

取消回复欢迎 发表评论:

请填写验证码