Spring的自动装配

353 阅读5分钟

1. 什么是Spring的自动装配?

在Spring框架中,"自动装配"(autowiring)是指 Spring 的能力去自动处理bean之间的依赖关系。对于任何工作中的bean,只要它在Spring容器中声明,Spring就可以自动为它注入所需的依赖。

Spring提供了几种自动装配模式:

  1. No: 这是默认的设置,它意味着不应进行自动装配。bean的依赖关系必须通过<constructor-arg><property>元素在配置文件中手动设置。

  2. byName: 在此模式下,Spring 会在配置中查找与bean属性名相同的bean,并自动装配它。

  3. byType: 在此模式下,Spring 会在配置中查找与bean属性类型相同的bean,并自动装配它。如果有多个相同类型的bean,会抛出异常。

  4. constructor: 在此模式下,Spring 会在配置中查找与构造函数参数类型相匹配的bean,并自动装配它。

  5. autodetect: 在此模式下,Spring首先尝试使用constructor自动装配,如果不成功,它会使用byType自动装配。

然而,自动装配使得配置更加简洁,但是可能会导致效果不清晰,也可能会引入歧义性。因此,通常在处理大型系统时,我们推荐使用明确的装配,也就是通过使用<constructor-arg><property>元素明确声明依赖关系。

当使用注解进行依赖注入时,Spring 提供了 @Autowired@Inject@Resource 等注解来实现自动装配。这些注解可以被应用在字段、构造器、setter 方法等地方。例如:

@Autowired
private MyBean myBean;

以上述代码为例,Spring将会查找容器中类型为MyBean的bean,并自动注入到myBean字段中。如果找不到,或者找到多个同类型的bean,Spring都会抛出异常。当然,我们可以通过使用@Primary@Qualifier等注解来解决这些问题。

2. 案例

Spring 的自动装配是 Spring IOC (控制反转) 容器的一个重要特性,它允许开发人员不必在 Spring 配置文件中明确指定每个依赖关系,Spring 容器就能自动完成这些操作。

  1. 首先我们需要理解什么是Spring的自动装配(autowiring)。 Spring的自动装配是Spring容器自动满足bean依赖的一种方法。你只需要告诉Spring哪些bean需要自动装配,Spring会在上下文中找到匹配的bean并自动完成装配。在Spring中,我们主要使用@Autowired标注来实现自动装配。

  2. 要使用Spring的自动装配,首先你需要在你的Spring配置中启用它。例如在XML配置中,你可以使用<context:annotation-config/>来启用。

  3. 当Spring容器启动时,它会扫描所有的类,找到带有@Autowired注解的属性,然后试图在容器中找到匹配的bean进行装配。

下面是一个使用@Autowired的简单示例:

@Component
public class FooService {
    private final BarService barService;

    @Autowired
    public FooService(BarService barService) {
        this.barService = barService;
    }
}

在上述代码中,FooService依赖于BarServiceBarService的实例在FooService的构造函数中通过@Autowired注解被自动装配。

然而,有时我们可能会遇到错误。例如,如果Spring容器中没有BarService的实例,或者有多个BarService的实例,Spring就不知道应该装配哪个,这时就会抛出异常。

@Component
public class BarService {
    //...
}
@Component
public class AnotherBarService implements BarService {
    //...
}

现在我们有两个实现了BarService接口的bean,Spring不知道应该选择哪个,所以会抛出NoUniqueBeanDefinitionException异常。

在这种情况下,我们可以使用@Primary注解来指定应该优先考虑哪个bean。例如:

@Primary
@Component
public class BarService {
    //...
}

或者我们可以使用@Qualifier注解来指定具体装配哪个bean:

@Component
public class FooService {
    private final BarService barService;

    @Autowired
    public FooService(@Qualifier("anotherBarService") BarService barService) {
        this.barService = barService;
    }
}

以上,就是Spring自动装配的基本原理和使用方法。

至于Spring自动装配的源码分析,这涉及到Spring的内部实现,可能会比较复杂。简单地说,Spring在初始化bean的过程中,会通过反射机制,扫描类的属性和构造函数,找到标注有@Autowired的 地方,然后从容器中查找匹配的bean进行装配。这个过程是在AutowiredAnnotationBeanPostProcessor这个类中完成的。

3. 来看一下AutowiredAnnotationBeanPostProcessor这个类,来深入理解其工作原理

AutowiredAnnotationBeanPostProcessor 是一个 BeanPostProcessor,它主要用于处理 @Autowired@Value 注解。让我们详细解析一下其工作原理:

AutowiredAnnotationBeanPostProcessor 的主要工作流程是在 Bean 的创建过程中,Spring会调用它的两个主要方法 postProcessPropertiespostProcessMergedBeanDefinition

1. postProcessMergedBeanDefinition:

该方法在初始化Bean之前被调用,用于解析类和其父类中的 @Autowired@Value 注解,并将解析的结果存储在 InjectionMetadata 中以备后用。

@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
	InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
	metadata.checkConfigMembers(beanDefinition);
}

2. postProcessProperties:

该方法在初始化Bean时被调用,用于完成属性的注入。它首先查找对应的 InjectionMetadata,然后调用 InjectionMetadata#inject 完成属性注入。

@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws BeanCreationException {
	if (bean.getClass() != this.targetClass && this.targetClass != null) {
		if (this.targetSource != null) {
			if (!this.targetSource.matches(bean.getClass(), bean.getClass().getClassLoader())) {
				throw new BeanCreationException("Injection of autowired dependencies failed");
			}
		}
		else {
			if (this.targetClass.isInstance(bean)) {
				this.targetSource = getSingletonTargetSource(bean);
			}
			else {
				this.targetClass = null;
			}
		}
	}
	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 != null ? "Could not autowire bean: " + beanName : "Could not autowire. There is a circular reference in bean."),
				ex);
	}
}

AutowiredAnnotationBeanPostProcessor 使用了模板方法设计模式,通过AutowiredAnnotationBeanPostProcessor#doCreateInjectionMetadata 方法完成注解的解析,然后通过 InjectionMetadata#inject 方法完成属性的注入。

这样设计的好处是 AutowiredAnnotationBeanPostProcessor 可以支持不同的注入方式,比如通过 @Autowired 注解可以实现 byType 注入,通过 @Value 注解可以实现基本类型和字符串的注入。

总的来说,AutowiredAnnotationBeanPostProcessor 的主要工作就是解析注解并完成属性的注入。