SpringBoot自定义注解+启动自动注册Bean完整实现

ps:如果这篇帖子对于还在找工作和找实习的你有所帮助,可以关注我,给本贴点赞、评论、收藏并订阅专栏;同时不要吝啬您的花花

一、核心实现原理

SpringBoot启动时会加载BeanDefinition并实例化Bean,借助Spring提供的ImportBeanDefinitionRegistrar扩展接口,可在Bean定义注册阶段,扫描带有自定义注解的类,手动将其注册为Spring Bean,全程无侵入、贴合Spring原生生命周期。

核心关键点:自定义标记注解 + 注解扫描注册器 + 启动启用配置,三步完成自动注册

二、完整代码实现

2.1 自定义标记注解(AutoRegisterBean)

创建自定义注解,用于标记需要自动注册为Bean的类,支持指定Bean名称、作用域等属性。

import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;

import java.lang.annotation.*;

/**
 * 自定义注解:标记类在SpringBoot启动时自动注册为Bean
 * 继承@Component元注解,兼容Spring原生Bean管理机制
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface AutoRegisterBean {

    /**
     * Bean名称,等价于@Component的value属性
     */
    @AliasFor(annotation = Component.class)
    String value() default "";

    /**
     * Bean作用域:单例/多例
     */
    String scope() default "singleton";

    /**
     * 是否懒加载
     */
    boolean lazyInit() default false;
}

2.2 实现Bean注册器(AutoBeanDefinitionRegistrar)

实现ImportBeanDefinitionRegistrar接口,重写registerBeanDefinitions方法,扫描自定义注解并注册BeanDefinition。

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.ClassUtils;

import java.io.IOException;
import java.util.Map;
import java.util.Set;

/**
 * 自定义Bean注册器:扫描@AutoRegisterBean注解,启动时自动注册Bean
 */
public class AutoBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    /**
     * 注册Bean定义的核心方法
     * @param importingClassMetadata 导入类的元数据
     * @param registry Bean定义注册器
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        try {
            // 1. 获取当前项目的基础包路径(启动类所在包)
            String basePackage = ClassUtils.getPackageName(importingClassMetadata.getClassName());

            // 2. 构建注解扫描器,仅扫描@AutoRegisterBean标记的类
            MetadataReaderFactory metadataReaderFactory = (MetadataReaderFactory) ClassUtils.forName(
                    "org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider$DefaultMetadataReaderFactory",
                    this.getClass().getClassLoader()
            ).newInstance();

            // 3. 遍历基础包下所有类,筛选注解标记类
            Set<MetadataReader> metadataReaders = new org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider(false)
                    .addIncludeFilter(new AnnotationTypeFilter(AutoRegisterBean.class))
                    .findCandidateComponents(basePackage);

            // 4. 遍历筛选结果,手动注册Bean
            for (MetadataReader reader : metadataReaders) {
                AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
                // 获取类全限定名
                String className = annotationMetadata.getClassName();
                // 获取自定义注解属性
                Map<String, Object> annotationAttrs = annotationMetadata.getAnnotationAttributes(AutoRegisterBean.class.getName());

                // 构建BeanDefinition
                BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(className);
                // 设置作用域
                builder.setScope((String) annotationAttrs.get("scope"));
                // 设置懒加载
                builder.setLazyInit((boolean) annotationAttrs.get("lazyInit"));

                BeanDefinition beanDefinition = builder.getBeanDefinition();
                // 获取Bean名称(优先取注解value,无则取类名首字母小写)
                String beanName = (String) annotationAttrs.get("value");
                if (beanName.isEmpty()) {
                    beanName = ClassUtils.getShortNameAsProperty(className);
                }

                // 注册BeanDefinition
                if (!registry.containsBeanDefinition(beanName)) {
                    registry.registerBeanDefinition(beanName, beanDefinition);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("自定义注解Bean注册失败", e);
        }
    }
}

2.3 自定义启用注解(EnableAutoRegisterBean)

创建启用注解,通过@Import导入注册器,实现一键启用自动注册功能。

import org.springframework.context.annotation.Import;
import java.lang.annotation.*;

/**
 * 启用注解:开启自定义Bean自动注册功能
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AutoBeanDefinitionRegistrar.class)
public @interface EnableAutoRegisterBean {
}

2.4 SpringBoot启动类配置

在启动类添加@EnableAutoRegisterBean注解,启用自动注册功能。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * SpringBoot启动类
 */
@SpringBootApplication
@EnableAutoRegisterBean // 开启自定义Bean自动注册
public class CustomAnnotationApplication {
    public static void main(String[] args) {
        SpringApplication.run(CustomAnnotationApplication.class, args);
    }
}

2.5 测试Bean(标记自定义注解)

创建业务类,标记@AutoRegisterBean注解,启动时自动注册为Bean。

import lombok.extern.slf4j.Slf4j;
import javax.annotation.PostConstruct;

/**
 * 测试Bean:通过自定义注解自动注册
 */
@Slf4j
@AutoRegisterBean(value = "customTestBean", scope = "singleton", lazyInit = false)
public class CustomTestBean {

    /**
     * Bean初始化后执行,验证注册成功
     */
    @PostConstruct
    public void init() {
        log.info("===== 自定义注解Bean注册成功,Bean名称:customTestBean =====");
    }

    public void testMethod() {
        log.info("自定义注册Bean的方法执行成功");
    }
}

2.6 验证注册结果

编写测试类,从Spring容器获取Bean并调用方法,验证注册生效。

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class CustomBeanTest {

    // 注入自动注册的Bean
    @Autowired
    private CustomTestBean customTestBean;

    @Test
    public void testCustomBean() {
        // 调用Bean方法,验证注册成功
        customTestBean.testMethod();
    }
}

三、关键扩展与优化

  • 多包扫描适配:修改注册器逻辑,支持指定扫描包路径,避免仅扫描启动类包
  • 条件注册:结合@Conditional注解,实现按环境、配置动态注册Bean
  • Bean初始化回调:支持自定义初始化、销毁方法,贴合Spring生命周期
  • 排除规则:添加exclude属性,排除指定类不注册为Bean

四、核心注意事项

1. 注册器需实现ImportBeanDefinitionRegistrar,不可直接用@Component,否则无法参与BeanDefinition注册阶段

2. 自定义注解继承@Component,可兼容Spring依赖注入、AOP等原生特性

3. 避免重复注册:注册前判断registry是否包含BeanDefinition,防止Bean名称冲突

ps:如果这篇帖子对于还在找工作和找实习的你有所帮助,可以关注我,给本贴点赞、评论、收藏并订阅专栏;同时不要吝啬您的花花

Spring 文章被收录于专栏

本专栏聚焦Spring全生态体系,从IoC/AOP核心原理入手,覆盖Spring Boot自动配置、事务管理、Web开发等实战内容。拆解循环依赖、动态代理等高频面试难点,助力开发者从入门到精通,打通单体到微服务的技术链路,解决企业级开发痛点,提升架构设计与问题排查能力,成为Java后端进阶的必备技术专栏。

全部评论

相关推荐

03-05 16:22
已编辑
西安邮电大学 Web前端
点赞 评论 收藏
分享
评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务