Day04: 依赖注入的魔法:@Autowired实现原理深度揭秘

大家好,我是一名Java开发工程师。今天我们要聊的话题是Spring中那个我们每天都在使用,却可能从未深入了解的魔法——@Autowired依赖注入。当你在字段上写下这个简单的注解时,Spring背后究竟施展了什么样的魔法?

一、从日常开发中的@Autowired说起

相信每位Spring开发者都写过这样的代码:


@Service
public class OrderService {
    @Autowired
    private PaymentService paymentService;

    // 或者构造器注入
    private final UserService userService;

    public OrderService(UserService userService) {
        this.userService = userService;
    }
}

这两种注入方式看似结果相同,但底层实现却大不相同。让我们从Spring处理@Autowired的核心处理器开始探索。

二、核心魔法师:AutowiredAnnotationBeanPostProcessor

AutowiredAnnotationBeanPostProcessor是Spring处理@Autowired注解的核心类,它是一个BeanPostProcessor,专门负责处理自动装配逻辑。

public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {

    // 支持的注解类型,默认包含@Autowired和@Value
    private final Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>(4);

    public AutowiredAnnotationBeanPostProcessor() {
        this.autowiredAnnotationTypes.add(Autowired.class);
        this.autowiredAnnotationTypes.add(Value.class);
        // 如果JSR-330的@Inject存在,也支持
        try {
            this.autowiredAnnotationTypes.add(ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
        } catch (ClassNotFoundException ex) {
            // JSR-330 API not available - simply skip.
        }
    }

    // ...其他核心方法
}

三、依赖注入处理流程全景图

Spring处理依赖注入的主要流程可以分为以下几个阶段:

  • 元数据收集阶段:找出所有需要注入的点(字段、方法等)
  • 依赖解析阶段:确定每个注入点需要注入的Bean
  • 依赖注入阶段:实际进行注入操作

1. 元数据收集:postProcessMergedBeanDefinition()

在Bean实例化后、初始化前,Spring会调用此方法收集注入元数据:

// AutowiredAnnotationBeanPostProcessor类
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
    // 查找所有需要自动装配的元数据
    InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
    metadata.checkConfigMembers(beanDefinition);
}

private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
    // 使用缓存提高性能
    String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName();
    InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);

    if (metadata == null) {
        // 实际解析注解的代码
        metadata = buildAutowiringMetadata(clazz);
        this.injectionMetadataCache.put(cacheKey, metadata);
    }
    return metadata;
}

2. 依赖解析与注入:postProcessProperties()

这是实际进行依赖注入的核心方法:

// AutowiredAnnotationBeanPostProcessor类
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    // 获取之前收集的注入元数据
    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    try {
        // 实际注入操作
        metadata.inject(bean, beanName, pvs);
    } catch (Throwable ex) {
        throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
    }
    return pvs;
}

四、字段注入 vs 构造器注入:源码层面的差异

虽然最终效果相同,但字段注入和构造器注入在Spring内部的实现路径却大不相同。

1. 字段注入实现细节

对于字段注入,Spring会创建AutowiredFieldElement:

// AutowiredAnnotationBeanPostProcessor内部类
private class AutowiredFieldElement extends InjectionMetadata.InjectedElement {
    private final boolean required;

    private volatile boolean cached = false;

    public AutowiredFieldElement(Field field, boolean required) {
        super(field, null);
        this.required = required;
    }

    protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
        Field field = (Field) this.member;
        Object value;

        if (this.cached) {
            value = resolvedCachedArgument(beanName, this.cachedFieldValue);
        } else {
            // 创建依赖描述符
            DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
            // 解析依赖
            value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);

            synchronized (this) {
                if (!this.cached) {
                    if (value != null || this.required) {
                        this.cachedFieldValue = desc;
                        registerDependentBeans(beanName, autowiredBeanNames);
                        this.cached = true;
                    }
                }
            }
        }

        if (value != null) {
            // 通过反射设置字段值
            ReflectionUtils.makeAccessible(field);
            field.set(bean, value);
        }
    }
}

2. 构造器注入实现细节

构造器注入的处理则发生在Bean实例化阶段,由ConstructorResolver负责:

// ConstructorResolver类
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {

    // 确定使用的构造函数和参数
    Constructor<?> constructorToUse = null;
    Object[] argsToUse = null;

    // 遍历构造函数,寻找最匹配的
    for (Constructor<?> candidate : candidates) {
        Class<?>[] paramTypes = candidate.getParameterTypes();
        Object[] args = new Object[paramTypes.length];

        // 解析每个参数
        for (int i = 0; i < args.length; i++) {
            ConstructorArgumentValues.ValueHolder valueHolder = mbd.getConstructorArgumentValues().getArgumentValue(i, paramTypes[i]);
            if (valueHolder != null) {
                args[i] = valueHolder.getValue();
            } else {
                // 解析自动装配的依赖
                DependencyDescriptor desc = new DependencyDescriptor(new MethodParameter(candidate, i), true);
                args[i] = beanFactory.resolveDependency(desc, beanName, null, null);
            }
        }

        // 选择最匹配的构造函数
        if (constructorToUse == null || argsToUse.length > args.length) {
            constructorToUse = candidate;
            argsToUse = args;
        }
    }

    // 使用选定的构造函数实例化Bean
    return instantiate(beanName, mbd, constructorToUse, argsToUse);
}

关键差异:

  • 时机不同:构造器注入发生在实例化阶段,字段注入发生在实例化后
  • 处理类不同:构造器由ConstructorResolver处理,字段由AutowiredAnnotationBeanPostProcessor处理
  • 灵活性不同:构造器注入必须满足所有依赖,字段注入可以部分满足

五、依赖解析的核心:resolveDependency()

无论是字段注入还是构造器注入,最终都会调用DefaultListableBeanFactory.resolveDependency():

// DefaultListableBeanFactory类
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

    // 处理@Value注解
    if (descriptor.getDependencyType() == ObjectFactory.class) {
        return new DependencyObjectFactory(descriptor, requestingBeanName);
    }

    // 处理集合类型注入
    if (descriptor.getDependencyType().isArray()) {
        return resolveArray(descriptor.getDependencyType().getComponentType(), requestingBeanName, descriptor);
    } else if (Collection.class.isAssignableFrom(descriptor.getDependencyType())) {
        return resolveCollection(descriptor.getDependencyType(), descriptor.getElementTypeDescriptor(), requestingBeanName, descriptor);
    }

    // 处理Map类型注入
    else if (Map.class.isAssignableFrom(descriptor.getDependencyType())) {
        return resolveMap(descriptor.getDependencyType(), descriptor.getMapKeyType(), descriptor.getElementTypeDescriptor(), requestingBeanName, descriptor);
    }

    // 查找匹配的Bean
    Map<String, Object> matchingBeans = findAutowireCandidates(requestingBeanName, descriptor.getDependencyType(), descriptor);

    if (matchingBeans.isEmpty()) {
        if (descriptor.isRequired()) {
            raiseNoMatchingBeanFound(descriptor.getDependencyType(), descriptor.getAnnotations(), descriptor);
        }
        return null;
    }

    String autowiredBeanName;
    Object instanceCandidate;

    if (matchingBeans.size() > 1) {
        // 处理多个候选Bean的情况
        autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
        instanceCandidate = matchingBeans.get(autowiredBeanName);
    } else {
        Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
        autowiredBeanName = entry.getKey();
        instanceCandidate = entry.getValue();
    }

    if (autowiredBeanNames != null) {
        autowiredBeanNames.add(autowiredBeanName);
    }

    return instanceCandidate;
}

六、实战:自定义自动装配逻辑

理解了原理后,我们可以扩展自动装配的行为。例如,实现一个根据自定义注解注入的处理器:

public class MyCustomAutowiredProcessor extends AutowiredAnnotationBeanPostProcessor {

    public MyCustomAutowiredProcessor() {
        super();
        // 添加我们自定义的注解
        this.autowiredAnnotationTypes.add(MyCustomInject.class);
    }

    @Override
    protected InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
        // 先处理标准的@Autowired注解
        InjectionMetadata metadata = super.buildAutowiringMetadata(clazz);

        // 处理我们自定义的注解
        List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
        ReflectionUtils.doWithLocalFields(clazz, field -> {
            if (field.isAnnotationPresent(MyCustomInject.class)) {
                elements.add(new MyCustomFieldElement(field));
            }
        });

        return new InjectionMetadata(clazz, elements) {
            @Override
            public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable {
                // 先执行父类的注入
                super.inject(target, beanName, pvs);
                // 再执行我们的自定义注入
                for (InjectedElement element : elements) {
                    element.inject(target, beanName, pvs);
                }
            }
        };
    }

    private static class MyCustomFieldElement extends InjectionMetadata.InjectedElement {
        protected MyCustomFieldElement(Field field) {
            super(field, null);
        }

        protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
            Field field = (Field) this.member;
            Object value = // 自定义解析逻辑
                    ReflectionUtils.makeAccessible(field);
            field.set(bean, value);
        }
    }
}

七、常见问题与最佳实践

1.@Autowired vs @Resource vs @Inject:

  • @Autowired:Spring原生,支持required属性
  • @Resource:JSR-250标准,按名称注入
  • @Inject:JSR-330标准,功能类似@Autowired

2.循环依赖问题:

  • 字段注入可以解决大多数循环依赖
  • 构造器注入无法解决循环依赖
  • 最佳实践是尽量避免循环依赖

3.测试友好性:

  • 构造器注入更利于单元测试
  • 字段注入需要依赖Spring容器

4.不可变对象:

  • 构造器注入支持创建不可变对象(final字段)
  • 字段注入无法用于final字段

八、总结:@Autowired的魔法本质

通过今天的分析,我们可以看到@Autowired背后的"魔法"实际上是Spring精心设计的一系列处理流程:

  • 注解解析:通过后处理器识别需要注入的点
  • 依赖查找:根据类型或名称在容器中查找匹配的Bean
  • 依赖注入:通过反射或构造器参数完成实际注入

思考题:在你的项目中,更喜欢使用构造器注入还是字段注入?为什么?欢迎在评论区分享你的观点!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhysunny

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值