讲讲springboot中自定义的@Enable模块

@Enable可以装载第三方模块,根据配置进行依赖注入,把组件初始化到spring容器中使用。
在springboot启动类 xxApplication.java类中,我们常见到的有@SpringBootApplication和@MapperScan注解。
我们进入到@SpringBootApplication中可以看到使用了自动装配注解@EnableAutoConfiguration,和组件扫描注解@ComponentScan
下面我们就以@EnableAutoConfiguration为例进行说说@Eable模块装配:

@EnableAutoConfiguration自动装配注解的实现:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    Class<?>[] exclude() default {};
    String[] excludeName() default {};
}
  • 前三个都是定义注解使用;
  • @Inherited表示该注解会被子类继承;
  • @AutoConfigurationPackage将主配置类(即@SpringBootApplication 标注的类)所在的包及其子包里的所有类都纳入 Spring 容器;
  • @Import注解导入资源获取bean。
  • 最重要的实现就是导入了AutoConfigurationImportSelector类,该类实现了一大堆Aware,拿到对应对象进行操作,具体自己看看吧

接下来到了我们的重头戏,实现一个自定义的@Enable模块。

基于注解实现一个自定义的@Enable模块 @Import

参考@EnableWebmvc
功能:在启动的时候导入一个自定义bean,并且控制台输出一句"自定义注解@HelloWorldConfig学习 ---- 初始化"。

  1. 新建一个@HelloWorld注解

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    @Documented
    @Import({HelloWorldConfig.class})
    public @interface HelloWorld {
    }
    • 可以看到导入HelloWorldConfig配置类
  2. 新建HelloWorldConfig配置类

    //@Configuration
    public class HelloWorldConfig {
    @Bean
    Hi hi() {
        System.out.println("自定义注解@Enable模块学习 ---- 初始化");
        return new Hi();
    }
    }
    • 跟使用配置类导入组件很相似,但是不需要再加上@Configuration注解,因为@Enable导入的是第三方模块,你并不知道包名,也不需要将配置类装进容器。
    • Hi为自定义bean类,使用@HelloWorld注解后就可以在项目中使用Hi对象了。
      public class Hi {
      public String sayHi() {
      return "helloWorld";
      }
      }
  3. 在springboot启动类使用注解

    @HelloWorld
    @MapperScan("cn.shafish.springbootlearn.mapper")
    @SpringBootApplication
    public class SpringbootLearnApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootLearnApplication.class, args);
    }
    }
  4. 控制台输出
    启动springboot项目,看控制台输出

  5. 项目中使用Hi对象

    @RestController
    public class TestController {
    @Autowired
    Hi hi;
    
    @GetMapping("/hi")
    public Object test() {
        return hi.sayHi();
    }
    }

    浏览器访问:

基于接口实现自定义@Enable模块

参考@EnableCaching,你就可以根据不同的环境装配不同的bean。
比如:我们需要在Linux环境中导入HelloWorldLinux类bean、window则导入HelloWorldWindow类。

  1. 定义@HelloWorldSelector注解

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    @Documented
    @Import({HelloWorldConfigSelector.class})
    public @interface HelloWorldSelector {
    boolean isLinux() default false;
    }
    • 当然了,根据不同的配置装配不同的bean,你得有个参数标记一下 -- isLinux。
    • 使用@Import导入了该注解的配置类HelloWorldConfigSelector
  2. 配置类的实现

    public class HelloWorldConfigSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        Map<String, Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(HelloWorldSelector.class.getName());
        Boolean isLinux = (Boolean)annotationAttributes.get("isLinux");
        if(isLinux) {//根据参数装入不同的bean
            return new String[]{HelloWorldConfigLinux.class.getName()};
        } else {
            return new String[]{HelloWorldConfigWindow.class.getName()};
        }
    }
    }
    • 使用selector得实现ImportSelector,重写其selectImports方法,该方法就是返回bean类的。
    • 使用getAnnotationAttributes获取我们定义的HelloWorldSelector注解,然后根据里面的参数isLinux使用条件判断返回不同的bean。
      public class HelloWorldConfigLinux {
      @Bean
      Hi hi() {
      System.out.println("使用接口Selector定义例子,for Linux");
      return new Hi();
      }
      }
    • 对应window就把 System.out.println("使用接口Selector定义例子,for Linux");改为System.out.println("使用接口Selector定义例子,for Wiindow");即可。
  3. 在springboot启动类使用注解

    @HelloWorldSelector(isLinux = true)
    @MapperScan("cn.shafish.springbootlearn.mapper")
    @SpringBootApplication
    public class SpringbootLearnApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootLearnApplication.class, args);
    }
    }
    • 强制设置参数为Linux
  4. 输出

总结

我们的自定义@Enable模块可以使用两种不同的方式实现:

  1. 使用基本的@Import注解,对应@EnableWebmvc
  2. 基于ImportSelector接口的实现,对应@EnableCaching