SpringBoot通过自定义注解实现配置类的自动注入

 大家好,这篇文章主要记录一下SpringBoot中通过使用自定义注解实现配置类对象自动注入的方式。能力尚微,如有问题请大家多批评指正。

前言

SpringBoot中通过@ConfigurationProperties@Value注解就可以获取配置文件中的属性定义并绑定到Java Bean或属性上,这也是我们平常使用最多的一种方式。但是小胖在开发过程中就遇到一个问题:在做MQ的开发中,配置文件中会配置多个生产者分别提供不同的业务能力,如果通过@ConfigurationProperties注解来实现的话,这就意味着需要创建多个属性一样的配置类,虽然说可以实现功能,但是很明显,这不是一个很好的设计。场景如下所示:

producer1:
    password: xxx
    app: xxx
    address: url1
    enabled: false
    
producer2:
    password: xxx
    app: xxx
    address: url1
    enabled: false
复制代码

实现思路

在我们日常的开发工作中,经常可以见到的是通过自定义注解+拦截器+反射从而实现对权限的校验或者对实体类字段值格式进行校验。那么,我们是不是也可以参考这个思路达到我们的目的呢?答案是肯定的,其实如果对Mabatis等组件比较熟悉的话,就可以看到这样的设计。我们话不多少,开搞~

开搞

以下内容,为了方便,我们将配置相关内容改为人员(people)

自定义配置类读取配置

首先,有一点是不会改变的,我们需要自定义一个配置类,用于读取配置文件中的配置。这里,我们需要改变一下我们配置文件信息里。将所有的配置信息放到一个类里。


my:
  peoples:
    people1:
      userName: 张三
      userSex: 男
    people2:
      userName: 李四
      userSex: 女
复制代码

然后,定义一个配置类用来接收,这里通过@ConfigurationProperties注解实现对配置的注入。要注意,因为我们在peoples下面有很多的people,因此,属性应给定义的是一个MAP的类型。

@Component
@ConfigurationProperties(prefix = "my",ignoreUnknownFields = false)
public class PeopleConfigs {

    private Map<String, PeopleEntity> peoples;

    public Map<String, PeopleEntity> getPeoples() {
        return peoples;
    }

    public void setPeoples(Map<String, PeopleEntity> peoples) {
        this.peoples = peoples;
    }

    @Override
    public String toString() {
        return "PeopleConfigs{" +
                "peoples=" + peoples +
                '}';
    }
}

public class PeopleEntity {

    private String userName;
    private String userSex;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserSex() {
        return userSex;
    }

    public void setUserSex(String userSex) {
        this.userSex = userSex;
    }

    @Override
    public String toString() {
        return "PeopleEntity{" +
                "userName='" + userName + ''' +
                ", userSex='" + userSex + ''' +
                '}';
    }
}
复制代码

这样,Springboot就会自动加载我们这个配置类。但是,这个的整个PeopleConfigs是一个Bean,并不能达到我们本文的目的,因此我们进行后续的步骤。

自定义注解

我们声明一个运行时的注解,在属性上进行使用。这里定义name用来标记需要注入的是哪个人。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface People {
    String name() default "";
}
复制代码

创建子配置Bean

首先,定义一个autoConfig的配置类,该类通过@EnableConfigurationProperties注解,指定PeopleConfig Bean在本类之前进行装载。通过@Bean方法注解进行bean声明,此处调用的是单个people配置类的bean生成的方法。

@Configuration
@EnableConfigurationProperties({PeopleConfigs.class})
public class PeopleAutoConfig {

    @Autowired
    PeopleConfigs peopleConfigs;

    @Bean
    public PeopleRegister peopleRegister(){
        return new PeopleRegister(peopleConfigs);
    }
}

复制代码

通过反射进行people bean的注入

这里不得不提到BeanPostProcessor类,该类为我们提供了springBoot在bean初始化前后方便我们进行其他自定义操作的一些接口。我们这里通过实现postProcessBeforeInitialization方法,在bean装载之前,通过反射判断对应bean上是否有我们自定义的people注解。如果有,则进行注入操作。详细代码如下:

public class PeopleRegister implements BeanPostProcessor, ApplicationContextAware {

    private final PeopleConfigs peopleConfigs;

    private GenericApplicationContext applicationContext;

    PeopleRegister(PeopleConfigs peopleConfigs){
        this.peopleConfigs = peopleConfigs;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Class<?> beanClass = AopUtils.getTargetClass(bean);
        Field[] fields = beanClass.getDeclaredFields();
        Field[] var5 = fields;
        int var6 = fields.length;

        for(int var7 = 0;var7<var6;var7++){
            Field field = var5[var7];
            People annotation = field.getAnnotation(People.class);
            if (annotation!=null){
                PeopleEntity entity = this.peopleConfigs.getPeoples().get(annotation.name());
                if (!this.applicationContext.containsBean(annotation.name())){
                    ConfigurableListableBeanFactory beanFactory = this.applicationContext.getBeanFactory();
                    Object wrapperBean = beanFactory.initializeBean(entity, annotation.name());
                    beanFactory.registerSingleton(annotation.name(), Objects.requireNonNull(wrapperBean));
                }

                try{
                    field.setAccessible(true);
                    field.set(bean, this.applicationContext.getBean(annotation.name(), PeopleEntity.class));
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = (GenericApplicationContext)applicationContext;
    }
}
复制代码

使用

前面工作进行完成后,接下来就是我们的使用环节,这里,我们仅需要通过@People(name = "人")指定即可:

@Controller
public class BaseController {

    @Autowired
    PeopleConfigs peopleConfigs;
    @People(name = "people1")
    PeopleEntity people1;
    @People(name = "people2")
    PeopleEntity people2;

    @ResponseBody
    @GetMapping("/test")
    public String test() {
        return peopleConfigs.toString()+people1.toString()+people2.toString();
    }
}
复制代码

效果

回顾

本文我们通过自定义注解+反射实现了配置类的自动注入,看完以后是不是觉得如此简单。我们只是把发射注入的逻辑从拦截器换到了ostProcessBeforeInitialization中来而已。

在工作中,其实有很多业务场景可以让我们去进行友好的设计,关键在于我们有没有这个意识,大家一起进步。

延伸

有些JRM看到这个设计,是不是感觉也很想吐,明明能很简单搞得却搞得这么麻烦,还写注册类什么的。其实实际应用中,比如MQ一类的,我们在bean注入后,是希望直接启动连接的,在销毁时希望能自动关闭连接,而不是每次使用的时候都要自己对连接进行管理。因此才如此进行定义和设计。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java SpringBoot是一种使用了Spring框架的快速开发平台,它采用了自动配置和约定优于配置的原则,简化了Java应用程序的开发。自定义注解是在SpringBoot中扩展功能的一种方式,可以通过自定义注解实现一些特定的业务逻辑,使得代码更加简洁和可读。 自定义注解可以用来标记某个方法、类或者字段,通过在注解定义一些元数据,来改变注解所标记的元素的行为。在SpringBoot中,我们可以通过自定义注解来简化一些操作,比如对象自动注入。 在使用自定义注解时,我们还需要编写自定义解析器,通过解析器来读取和处理注解,完成对象的自动注入操作。自定义解析器需要实现Spring框架的BeanPostProcessor接口,该接口提供了对Bean对象进行处理的扩展点。 在自定义解析器中,我们需要重写接口中的两个方法:postProcessBeforeInitialization和postProcessAfterInitialization。在这两个方法中,我们可以通过反射机制获取到标记了我们自定义注解的类或者字段,然后根据实际的业务需求进行处理。 例如,我们可以定义一个自定义注解@AutoInject,然后编写一个自定义解析器AutoInjectAnnotationBeanPostProcessor,用来实现对象的自动注入。在解析器中,我们可以通过反射机制获取到标记了@AutoInject注解的字段,然后使用Spring的依赖注入功能将依赖的对象注入到标记了@AutoInject注解的字段中。 总之,Java SpringBoot的自定义注解和自定义解析器可以用来实现对象的自动注入操作。通过定义注解来标记需要注入的字段,然后通过自定义解析器来读取和处理注解,完成对象的自动注入。这样可以减少重复的代码,提高开发效率,并且使得代码更加清晰和可维护。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值