首页 > 其他分享 >揭秘@Autowired:手把手教你复刻Spring依赖注入魔法

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

时间:2024-07-20 19:29:10浏览次数:14  
标签:Autowired Spring MyAutowired bean 注解 复刻 注入

文章目录

手写一个@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 相同的功能和灵活性,还需要深入理解和扩展上述基础实现。

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

标签:Autowired,Spring,MyAutowired,bean,注解,复刻,注入
From: https://blog.csdn.net/weixin_68020300/article/details/140526978

相关文章

  • 类明显存在却报 package not found, Java程序中专门被其他工程所依赖的common jar用sp
    先上官方链接:https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/maven-plugin/examples/repackage-classifier.html在使用SpringBoot构建通用JAR库时,尤其是当通springboot默认的过spring-boot-maven-plugin插件打包时。如果遇到了类存在但报“packagenotfound......
  • 解决 SpringBoot 应用中 MySQL 时区配置引起的时间不一致问题
    在开发SpringBoot项目时,表中有两个时间字段一个通过Java代码使用newDate()方法获取当前时间再插入数据库另一个是使用MySQL的CURRENT_TIMESTAMP作为默认值实际运行时发现数据库中的这两个时间值不一致,代码插入的时间比数据库自动生成的时间早了8小时,最终发现是y......
  • Spring boot 与 json_schema ,请求和响应 校验
    java中如何使用json_schema对json进行校验在Java中使用JSONSchema对JSON进行校验,你首先需要选择一个合适的库。一个常用的库是json-schema-validator。以下是如何使用它的基本步骤:添加依赖如果你使用Maven,可以在pom.xml中添加以下依赖:<dependency><groupId>com.g......
  • springboot系列十: 自定义转换器,处理JSON,内容协商
    文章目录自定义转换器基本介绍应用实例查看源码注意事项和细节处理JSON需求说明应用实例内容协商基本介绍应用实例debug源码优先返回xml注意事项和细节⬅️上一篇:springboot系列九:接收参数相关注解......
  • 在Spring Boot中实现OAuth2.0认证
    在SpringBoot中实现OAuth2.0认证大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!OAuth2.0是一种用于授权的协议,它使得用户可以授权第三方应用程序访问他们在某服务提供商上的资源,而无需共享他们的凭据。SpringBoot提供了对OAuth2.0的原生支持,可以方......
  • 在Spring Boot中实现WebSocket实时通信
    在SpringBoot中实现WebSocket实时通信大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在现代web应用中,实时通信功能越来越受到重视。WebSocket协议是一种在单个TCP连接上进行全双工通信的协议,允许客户端和服务器之间进行实时数据传输。SpringBoot......
  • 使用Java和Spring MVC构建Web应用
    使用Java和SpringMVC构建Web应用大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在现代企业中,Web应用程序是最常见的应用类型之一。SpringMVC是一个强大且流行的JavaWeb框架,用于构建功能强大且易于维护的Web应用程序。本文将通过实际示例展示如......
  • Spring Boot+WebSocket向前端推送消息
     ​ 博客主页:   南来_北往......
  • Springboot 启动时Bean的创建与注入(二)-面试热点-springboot源码解读-xunznux
    Springboot启动时Bean的创建与注入,以及对应的源码解读文章目录Springboot启动时Bean的创建与注入,以及对应的源码解读11、getBean:200,AbstractBeanFactory(org.springframework.beans.factory.support)12、doGetBean:335,AbstractBeanFactory(org.springframework......
  • java项目(knife4j使用,静态资源未放在static资源包下,公共字段自动填充,Spring Cache与Spr
    Knife4j(生成接口文档)使用swagger你只需要按照它的规范去定义接口及接口相关的信息,就可以做到生成接口文档,以及在线接口调试页面。官网:https://swagger.io/Knife4j是为JavaMVC框架集成Swagger生成Api文档的增强解决方案。使用方式1、导入knife4j的maven坐标<dependency>......