揭秘@Autowired:手把手教你复刻Spring依赖注入魔法

手写一个@Autowired注解实现自动注入

众所周知,@AutowiredSpring 框架中的一个注解,用于自动装配(auto-wiringbean。当你在一个 Spring 管理的 bean 中使用@Autowired注解时,Spring 容器会在创建该 bean 的过程中自动寻找匹配的 bean,并将其注入到被注解的字段、构造函数或者 setter 方法中。
我相信大家都用过@Autowired,但是不少人对其实现原理和如何实现代码了知甚少。基于此,我们今天一起来手动实现一个@Autowired注解。
在这里插入图片描述

@Autowired注解的作用

在开始之前,我们先来一起回顾一下,@Autowired注解的主要作用。

  1. 自动注入依赖:通过@Autowired,你可以告诉 Spring 容器,你需要某个类型的 bean,容器会自动找到合适的 bean 并注入到你的类中,无需手动查找和创建实例。
  2. 类型安全:@Autowired支持类型检查,这意味着如果容器中没有找到匹配的 bean,或者有多个同类型的 beanSpring 将会抛出异常,从而帮助你确保依赖注入的正确性。
  3. 支持多种注入方式:@Autowired可以用于注入字段、setter 方法或者构造函数。这提供了灵活的配置选项,可以根据实际情况选择最适合的注入方式。
  4. 条件注入:通过结合使用@Autowired和其他注解(如@Qualifier@Primary等),可以实现条件化的 bean 注入,例如指定注入特定名称的bean或者在多个同类型 bean 中选择一个优先级更高的 bean
  5. 简化配置:使用@Autowired可以减少配置文件中的 bean 定义,使得代码更加简洁,提高了开发效率。

下面看一个简单的例子,假设你有一个UserService类,它依赖于一个UserRepository类的实例:

@Service
public class UserService {

    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    // ... 其他方法
}

在上面的代码中,UserService类有一个构造函数,它接受一个UserRepository类型的参数。通过在参数上使用@Autowired注解,Spring 容器会在创建UserService的实例时自动注入一个UserRepository的实例。

@Autowired的实现原理

@Autowired 注解的实现原理涉及到Spring框架的多个核心组件和过程。下面我们来简单的总结一下@Autowired的基本实现原理。

  1. 注解处理:当 Spring 应用程序启动时,它会扫描类路径上的所有类,寻找带有@Autowired注解的字段、构造函数和 setter 方法。Spring 使用BeanPostProcessor(特别是AutowiredAnnotationBeanPostProcessor)来处理这些注解。
  2. 元数据收集:在处理@Autowired注解时,Spring 会收集相关的元数据,例如需要注入的bean的类型或名称。如果使用了@Qualifier注解,Spring 还会收集指定的 bean 名称。
  3. 依赖查找:一旦 Spring 识别出需要自动装配的依赖项,它会在应用程序上下文中查找匹配的 beanSpring 会根据类型(或者类型和名称)来寻找合适的候选者。
  4. 候选者匹配:如果有多个同类型的 bean 可以作为依赖项,Spring 会根据@Qualifier注解或者@Primary注解来选择一个合适的 bean。如果没有明确的选择,Spring 会抛出一个异常。
  5. 依赖注入:找到匹配的 bean 后,Spring 会在适当的时机(例如在 bean 的构造函数中或者在 setter 方法上调用)将依赖项注入到目标对象中。如果是字段注入,Spring 会使用反射来设置字段的值。
  6. 生命周期管理:一旦依赖项被注入,Spring 容器会管理这些bean 的生命周期,包括创建、初始化、使用和销毁。这意味着 Spring 会自动处理 bean 的依赖关系,确保它们在使用时是可用的。
  7. 异常处理:如果在容器中找不到匹配的 bean,或者存在多个候选者但没有明确的选择,Spring 会抛出NoSuchBeanDefinitionExceptionAmbiguousBeanDefinitionException异常,从而通知开发者存在配置问题。

手写一个@MyAutowired注解

根据上面的原理,我们来实现一个简单的@MyAutowired注解,使其满足@Autowired 注解的一部分能力。

定义@MyAutowired注解

首先,我们定义@MyAutowired注解,它将包含必要的元数据,并标记在需要自动注入的字段、构造函数或方法上。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD})
public @interface MyAutowired {
    // 可以添加@Qualifier支持
    String value() default "";
}

创建注解处理器

接下来,我们需要创建一个BeanPostProcessor的实现,它会在 Spring 容器启动时处理@MyAutowired注解。


import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.Map;

@Component
public class MyAutowiredAnnotationBeanPostProcessor implements BeanPostProcessor {

    private final BeanDefinitionRegistry registry;

    public MyAutowiredAnnotationBeanPostProcessor(BeanDefinitionRegistry registry) {
        this.registry = registry;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Class<?> clazz = bean.getClass();
        Field[] fields = clazz.getDeclaredFields();

        for (Field field : fields) {
            if (field.isAnnotationPresent(MyAutowired.class)) {
                field.setAccessible(true);
                MyAutowired autowired = field.getAnnotation(MyAutowired.class);
                String beanNameToInject = autowired.value();

                // 根据beanNameToInject获取bean,这里需要自定义查找逻辑
                Object beanToInject = getBeanFromRegistry(beanNameToInject, field.getType());

                if (beanToInject != null) {
                    field.set(bean, beanToInject);
                }
            }
        }

        return bean;
    }

    private Object getBeanFromRegistry(String beanName, Class<?> requiredType) {
        // 自定义查找bean的逻辑,这里简化处理,直接使用类型查找
        Map<String, Object> beansOfType = registry.getBeansOfType(requiredType);
        if (!beansOfType.isEmpty()) {
            return beansOfType.values().iterator().next();
        }
        return null;
    }
}

集成自定义处理器

接下来,我们需要确保自定义的BeanPostProcessorSpring 容器识别并使用。

import org.springframework.context.annotation.Configuration;

@Configuration
public class MyAutowiredConfig {

    // 注册自定义的BeanPostProcessor
    public MyAutowiredAnnotationBeanPostProcessor myAutowiredAnnotationBeanPostProcessor(BeanDefinitionRegistry registry) {
        return new MyAutowiredAnnotationBeanPostProcessor(registry);
    }
}

这个实现是非常简化的,也是仿写@Autowired的,并借助 Spring 做了偷懒处理。它也没有处理多个候选 bean 的情况,也没有处理构造函数和方法的注入。在实际应用中,可能需要更复杂的逻辑来处理这些情况,并且需要确保自定义的注解处理器在 Spring 容器启动时被注册。

本文中的示例提供了一个基本的框架,大家可以根据实际需求进一步扩展和完善它。在实现过程中,需要大家深入了解 Spring 的工作机制,包括 bean 的生命周期、bean 的定义和注册过程,以及反射和注解的处理等。

总结

手动实现一个类似 Spring 框架中 @Autowired 注解的功能,涉及到多个步骤和对 Spring 核心机制的理解。下面是按照文章内容整理的详细总结:

@Autowired 主要功能

  • 自动注入依赖:在不显式指定的情况下,Spring 容器能自动查找并注入所需的 bean 实例。
  • 类型安全:确保注入的 bean 类型正确,否则抛出异常。
  • 支持多种注入方式:字段、构造函数、setter 方法。
  • 条件注入:结合其他注解如 @Qualifier@Primary 进行更精确的注入控制。
  • 简化配置:减少 XML 或其他配置文件中的冗余,提高开发效率。

@Autowired 实现原理

  1. 注解处理Spring 使用 BeanPostProcessor(特别是 AutowiredAnnotationBeanPostProcessor)处理 @Autowired 注解。
  2. 元数据收集:收集被注解元素的类型和名称信息。
  3. 依赖查找:在上下文中寻找匹配的 bean
  4. 候选者匹配:根据 @Qualifier@Primary 选择具体 bean
  5. 依赖注入:通过反射或调用 setter 方法注入 bean
  6. 生命周期管理Spring 容器管理 bean 的整个生命周期。
  7. 异常处理:当找不到匹配 bean 或存在多个候选者时抛出异常。

手写 @MyAutowired 注解

  1. 定义注解:使用 @Retention@Target 指定其运行时可见性和可应用于哪些元素。
  2. 创建处理器:实现 BeanPostProcessor 接口,重写 postProcessBeforeInitialization 方法处理注解。
  3. 集成处理器:通过配置类注册自定义的 BeanPostProcessor 实现。

注意事项

  • 实现中未处理多个候选 bean 的情况。
  • 没有处理构造函数和方法的注入。
  • 需要深入了解 Spring 的工作原理,包括 bean 的生命周期、定义与注册,以及反射和注解处理。

通过上述步骤,可以构建一个简化的 @MyAutowired 实现,但要达到与 @Autowired 相同的功能和灵活性,还需要深入理解和扩展上述基础实现。

更多手写系列教程参考:《知识星球》

  • 22
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值