1. 什么是Spring的自动装配?
在Spring框架中,"自动装配"(autowiring)是指 Spring 的能力去自动处理bean之间的依赖关系。对于任何工作中的bean,只要它在Spring容器中声明,Spring就可以自动为它注入所需的依赖。
Spring提供了几种自动装配模式:
-
No: 这是默认的设置,它意味着不应进行自动装配。bean的依赖关系必须通过
<constructor-arg>和<property>元素在配置文件中手动设置。 -
byName: 在此模式下,Spring 会在配置中查找与bean属性名相同的bean,并自动装配它。
-
byType: 在此模式下,Spring 会在配置中查找与bean属性类型相同的bean,并自动装配它。如果有多个相同类型的bean,会抛出异常。
-
constructor: 在此模式下,Spring 会在配置中查找与构造函数参数类型相匹配的bean,并自动装配它。
-
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 容器就能自动完成这些操作。
-
首先我们需要理解什么是Spring的自动装配(autowiring)。 Spring的自动装配是Spring容器自动满足bean依赖的一种方法。你只需要告诉Spring哪些bean需要自动装配,Spring会在上下文中找到匹配的bean并自动完成装配。在Spring中,我们主要使用
@Autowired标注来实现自动装配。 -
要使用Spring的自动装配,首先你需要在你的Spring配置中启用它。例如在XML配置中,你可以使用
<context:annotation-config/>来启用。 -
当Spring容器启动时,它会扫描所有的类,找到带有
@Autowired注解的属性,然后试图在容器中找到匹配的bean进行装配。
下面是一个使用@Autowired的简单示例:
@Component
public class FooService {
private final BarService barService;
@Autowired
public FooService(BarService barService) {
this.barService = barService;
}
}
在上述代码中,FooService依赖于BarService。BarService的实例在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会调用它的两个主要方法 postProcessProperties 和 postProcessMergedBeanDefinition。
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 的主要工作就是解析注解并完成属性的注入。