SpringBoot的@Enable* 注解的工作原理

    SpringBoot 提供了@EnableAutoConfiguration@EnableConfigurationProperties@EnableAsync等注解用来启用某些特性。

工作原理

    每个以 Enable 开头的注解中,都有 @Import 注解,这个注解是用来导入一个或者多个类(由Spring管理),或者配置类(配置类中的beanSpring管理),因此 @Import 可以代替 @Component@Configuration等注解。
    在 Import 源码中定义了Class数组的value属性,官方注释是可以将带有Configuration注解或者实现了ImportSelectorImportBeanDefinitionRegistrar接口的类,还有其他符合要求的class交给Spring管理。
    也就是说,SpringBoot@Enable*的实现是在@Import注解中指定需要的配置类,包括带有@Configuration的类,实现了ImportSelector或者ImportBeanDefinitionRegistrar接口的类并在实现的方法中将需要的Bean注册到Spring容器中。

例如:

@EnableAutoConfiguration 的配置类中是实现了 ImportSelector 接口
@EnableConfigurationProperties 的配置类中实现了 ImportSelector 接口
@EnableAsync 配置类中实现了 ImportSelector 接口
@EnableWebMvc 的配置类中使用了 @Configuration 注解

与配置启用相关的类和注解

  • Import 注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
    Class<?>[] value();
}
  • ImportSelector接口
public interface ImportSelector {
    /**
     * 实现此方法,Spring 会将所有返回值注册到容器中
     */
    String[] selectImports(AnnotationMetadata importingClassMetadata);
}
  • ImportBeanDefinitionRegistrar 接口
public interface ImportBeanDefinitionRegistrar {
    /**
    * 实现此方法,需要手动将 Bean 注册到 Spring 容器中
    */
    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}

案例

  • ImportSelector 案例
// 注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(value = BeanImportSelector.class)
public @interface EnableBean {
}
// 准备两个类
public class UserDto {
}
public class UserVo {
}
// 配置类
public class BeanConfiguration {
    @Bean
    public UserDto userDto1(){
        return new UserDto();
    }
    @Bean
    public UserVo userVo1(){
        return new UserVo();
    }
}
// ImportSelector 实现类
public class BeanImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        // 将 bean 注入到 spring 容器
        return new String[] { UserVo.class.getName(), "com.p7.boot.enable.test.dto.UserDto",
                BeanConfiguration.class.getName() };
    }
}

// 启动类
@SpringBootApplication
// 启动特性
@EnableBean
public class App {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
        System.out.println(context.getBeansOfType(UserDto.class));
        System.out.println(context.getBeansOfType(UserVo.class));
        context.close();
    }
}
  • ImportBeanDefinitionRegistrar案例,继续使用上面的案例
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(value = LogImportBeanDefinitionRegistrar.class)
public @interface EnableLog {
    String[] packages();
}

// 启动 @EnableLog 特性,在加载 Bean 时,根据包名打印日志
public class LogBeanPostProcessor implements BeanPostProcessor {
    List<String> packageList;
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // 遍历 LogImportBeanDefinitionRegistrar 穿过来的所有包名
        for (String packageName : packageList) {
            if (bean.getClass().getName().startsWith(packageName)) {
                System.out.println(bean.getClass().getName() + " Log ...");
            }
        }
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    public List<String> getPackageList() {
        return packageList;
    }
    public void setPackageList(List<String> packageList) {
        this.packageList = packageList;
    }
}

// 实现 ImportBeanDefinitionRegistrar 接口
public class LogImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 获取 EnableLog 的所有属性
        Map<String, Object> attr = importingClassMetadata.getAnnotationAttributes(EnableLog.class.getName());
        // 得到 packages 属性所有的值
        String[] packages = (String[]) attr.get("packages");
        List<String> packageList = Arrays.asList(packages);
        // 生成 LogBeanPostProcessor 对象,并将所有包含的包传给该对象
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(LogBeanPostProcessor.class.getName());
        builder.addPropertyValue("packageList", packageList);
        // 将 LogBeanPostProcessor 对象注册到 Spring 中
        registry.registerBeanDefinition(LogBeanPostProcessor.class.getName(), builder.getBeanDefinition());
    }
}

// 启动类
@SpringBootApplication
@EnableBean
// 配置 com.p7.boot.enable.test.vo 下所有的类注册到 Spring 容器前,打印日志
@EnableLog(packages = "com.p7.boot.enable.test.vo")
public class App {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
        context.close();
    }
}

猜你喜欢

转载自blog.csdn.net/qq_30038111/article/details/80198967