Spring手动挡装配Bean之Enable模式

本文深入探讨Spring的@Enable驱动模式,解释了如何通过注解和接口编程自定义Enable模式,包括@Configuration方式和ImportSelector/ImportBeanDefinitionRegistrar接口方式。通过分析源码,揭示了ConfigurationClassPostProcessor在解析@Import注解中的重要作用,帮助理解SpringBoot自动装配的原理。
摘要由CSDN通过智能技术生成

前言

在SpringBoot开发中,我们经常见到Enable的模式开发,例如在SpringCloud中需要使用feign,此时需要开启Feign的注册,打上@EnableFeignClients注解,即可扫描带@FeignClient注解的FeignClient。又或是开启异步的功能,你需要加上@EnableAsyn注解。这种模式被称为“Enable模式”,也被视作SpringBoot中的“手动挡式自动装配”。本篇文章将解析Enable模式是什么,且分析源码了解底层操作。

同时,本篇文章也是下一篇介绍SpringBoot"自动档式自动装配"的魔法的铺垫式文章,为什么这么说呢?因为在自动挡中,@EnableAutoConfiguration注解起到了关键作用,同时它也是Enable驱动模式。或许读者对此注解感到陌生,但对于@SpringBootApplication这个注解一定不会陌生,因为在SpringBoot引导类上一般都会打上、这么一个注解,而@EnableAutoConfiguration元标注了@SpringBootApplication

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
   
		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
   
  // ...
}

在Spring式的注解中,注解是具有"派生性"的,比如日常开发经常用到的一些声明Bean的注解:

  • @Service
  • @Controller
  • @Configuration
  • @Component

他们均被@Component元标注,例如@Service:

@Target({
   ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
   
  // ...
}

而Spring通常解析注解时,会将注解递归解析出来,并放入Map中,也就是说,会解析到最后一层,在判断是否需要视为Bean注册到IOC容器中的时候,只需要判断是否有@Component注解即可,所以不管是@Service还是@Controller,都视为@Component,将其视作SpringBean注册到IOC容器中。

需要注意的是,在Spring4才支持递归的方式解析注解,也就是不管注解有多少层,最终都能识别到最底层的关键性注解例如@Component,SpringBoot1.0开始就使用了Spring4,所以可以放心其“派生性”。但在Spring3,派生性只支持一层,如果我自定义一个注解,元标注了@Service,那么到达关键注解@Component时需要2层解析,Spring3只解析一层,并没有递归解析,此时就做不到派生性了。

在Spring中,注解都有它自己的一套方式,比如“派生性”,除此之外还有属性覆盖性,还有属性别名等等一系列的Spring专有注解模式。

综上所述,理解@Enable驱动模式,对于理解SpringBoot自动装配是必不可少的。

@Enable驱动模式

在@Enable驱动模式的开发中,可以发现,其都被@Import注解所元标注,例如@EnableFeignClients

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
   
  // ...
}

而Import注解中的value,一般而言都会实现ImportBeanDefinitionRegistrar或ImportSelector这两个接口,又或者是类头被@Configuration注解标注,那么从这点来看,Enable模式大致分为两种方式:

  • 注解驱动:在类中有配置注解,如@Configuration、@Bean等等
  • 接口编程:实现ImportBeanDefinitionRegistrar或ImportSelector这两个接口

然后将以上类放入@Import的value中即可,再给其加上一个华丽的外表,Enable*,这样一个Enable模式就完成了。

自定义Enable模式

下面我们自定义一个Enable模块。

注解驱动

@Configuration方式

先来一个最简单的注解驱动。首先自定义一个Enable注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(StringBean.class)
public @interface EnableStringBean {
   
}

其中Import中定义的配置类如下

@Configuration
public class StringBean {
   
    @Bean
    public String stringBean(){
   
        return "Hello,world";
    }
}

其实这里StringBean配置类也可以不标注@Configuration注解,在Spring3.0中限制只解析@Configuration,在后面的版本中,只要类中有@Bean、@ComponentScan等等配置注解,都可以被解析处理

@Configuration
@EnableStringBean
public class StringBeanContextBootstrap {
   
    public static void main(String[] args) {
   
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(StringBeanContextBootstrap.class);

        String string = context.getBean("stringBean", String.class);
        System.out.println(string);
        context.close();
    }
}

在需要装配的类上打上我们的自定义注解@EnableStringBean,装配到上下文中,控制台打印
在这里插入图片描述
可见,我们的Enable模块自动注册了一个Bean到上下文中

接口编程

ImportSelector接口方式

这种方式需要实现以上接口,并实现其selectImports方法。该方法返回一个字符串数组,这些字符串是需要被注册到Spring中的Bean的全限定类名。

这次我们来个更加动态的方式,首先重新定义Enable注解的属性

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(StringBeanRegister.class)
public @interface EnableStringBean {
   
    StringBean.StringType type();
}

新增一个type属性,我们可以选择性的装载某个Bean。接下来是import中的StringBeanRegister定义

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值