优秀的编程知识分享平台

网站首页 > 技术文章 正文

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

nanyue 2024-08-03 17:40:06 技术文章 6 ℃

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

一、自动依赖注入的方式

注解类型

  • 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);
		}
	}
}
最近发表
标签列表