Spring BeanFactoryPostProcessor

Spring BeanFactoryPostProcessor

Spring中有两种PostProcessor,BeanFactoryPostProcessor和BeanPostProcessor,本文主要介绍BeanFactoryPostProcessor

作用

Spring BeanFactoryPostProcessor

BeanFactoryPostProcessor 可以修改Spring上下文中bean的定义,修改bean的属性等。Spring上下文会自动查找BeanFactoryPostProcessor类型的bean定义,并在其他bean创建的时候调用写BeanFactoryPostProcessor的postProcessBeanFactory方法。

分类

BeanFactoryPostProcessor 又分为两种

  • 常规 BeanFactoryPostProcessor
  • BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessor继承自BeanFactoryPostProcessor,是一种比较特殊的BeanFactoryPostProcessor,BeanDefinitionRegistryPostProcessor中定义的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法要优先于BeanFactoryPostProcessor的postProcessBeanFactory方法执行。

扩展实例

定义注解Handler

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface Handler {

    String code() default "";
}

定义类,并注解Handler

@Handler(code = "orderCreateHandler")
public class OrderHandler {

    public void handle(){
        System.out.println("handle invoked!");
    }
}

扩展BeanDefinitionRegistryPostProcessor

@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        //这里可以自定义注解扫描,生成BeanDefinition并注册到spring上下文
        //自定义代码可以参考Mybatis: org.mybatis.spring.mapper.MapperScannerConfigurer


        //我们这里的逻辑很简单,扫描com.study.demo.handler包下,标有注解Handler的类,并注册到spring上下文
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry);
        scanner.addIncludeFilter(new AnnotationTypeFilter(Handler.class));
        scanner.scan("com.study.demo.handler");
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        //Nothing to do
    }
}

启动Spring Boot 应用,可以看到标有Handler注解的类已经被注入到spring中

@SpringBootApplication
public class Demo implements CommandLineRunner {

    //OrderHandler已经被注入到spring中,这里可以使用Autowired自动注入
    @Autowired
    private OrderHandler orderHandler;

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Demo.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        orderHandler.handle();
    }
}

输出:
handle invoked!

Spring 实例

上面介绍的是一个简单的例子,下面看一下spring 中BeanDefinitionRegistryPostProcessor的应用。
ConfigurationClassPostProcessor 是一个BeanDefinitionRegistryPostProcessor,主要用于解析和注册配置类。我们以解析@Import(spring boot 自动配置的关键)注解为例,时序图如下:
ImportBeanDefinitionRegistrar

核心代码如下:
org.springframework.context.annotation.ConfigurationClassParser#processImports

if (candidate.isAssignable(ImportSelector.class)) {
    //第一种情况
    // Candidate class is an ImportSelector -> delegate to it to determine imports
    Class <?> candidateClass = candidate.loadClass();
    ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
    ParserStrategyUtils.invokeAwareMethods(
        selector, this.environment, this.resourceLoader, this.registry);
    if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
        this.deferredImportSelectors.add(
            new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
    }
    else {
        String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
        Collection < SourceClass > importSourceClasses = asSourceClasses(importClassNames);
        processImports(configClass, currentSourceClass, importSourceClasses, false);
    }
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
    //第二种情况
    // Candidate class is an ImportBeanDefinitionRegistrar ->
    // delegate to it to register additional bean definitions
    Class <?> candidateClass = candidate.loadClass();
    ImportBeanDefinitionRegistrar registrar =
        BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
    ParserStrategyUtils.invokeAwareMethods(
        registrar, this.environment, this.resourceLoader, this.registry);
    configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
    //第三种情况
    // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
    // process it as an @Configuration class
    this.importStack.registerImport(
        currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
    processConfigurationClass(candidate.asConfigClass(configClass));
}

@Import

上面介绍了@Import 的解析与注册,接下来介绍一下@Import 的使用。@Import 将类注册到spring容器中,其核心代码org.springframework.context.annotation.ConfigurationClassParser#processImports有三个分支逻辑,所以有三种使用方式

1. 引入java类

普通类

public class Foo {
    public void foo(){
        System.out.println("Foo.foo() invoked!");
    }
}

@SpringBootApplication
@Import({Foo.class})
public class Demo implements CommandLineRunner {

    @Autowired
    private Foo foo;

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Demo.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        foo.foo();
    }
}

输出:
Foo.foo() invoked!

配置类

public class Config {
    @Bean
    public Bar bar(){
        return new Bar();
    }
}


@SpringBootApplication
@Import({Config.class})
public class Demo implements CommandLineRunner {

    @Autowired
    private Bar bar;

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Demo.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        bar.bar();
    }
}

输出:
Bar.bar() invoked!

2. 引入ImportBeanDefinitionRegistrar

public class FooBar {
    public void fooBar() {
        System.out.println("FooBar.fooBar() invoked!");
    }
}

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(FooBar.class);
        registry.registerBeanDefinition("fooBar", rootBeanDefinition);
    }
}

@SpringBootApplication
@Import({MyImportBeanDefinitionRegistrar.class})
public class Demo implements CommandLineRunner {

    @Autowired
    private FooBar fooBar;

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Demo.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        fooBar.fooBar();
    }
}

输出:
FooBar.fooBar() invoked!

3. 引入ImportSelector

spring 会将selectImports返回的结果注册成bean

public class BarFoo {

    public void barFoo(){
        System.out.println("BarFoo.barFoo() invoked!");
    }
}

public class MyImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.study.demo.domain.BarFoo"};
    }
}

@SpringBootApplication
@Import({MyImportSelector.class})
public class Demo implements CommandLineRunner {

    @Autowired
    private BarFoo barFoo;


    public static void main(String[] args) throws Exception {
        SpringApplication.run(Demo.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        barFoo.barFoo();
    }
}

输出:
BarFoo.barFoo() invoked!

Spring Boot EnableAutoConfiguration

Spring Boot EnableAutoConfiguration 就是用第三种方式实现自动配置的
* EnableAutoConfiguration注解引入AutoConfigurationImportSelector
* AutoConfigurationImportSelector的selectImports查找所有META-INF/spring.factories里的类
* 查找META-INF/spring.factories里的类,通过SpringFactoriesLoader的方法loadFactoryNames

在EnableAutoConfiguration中引入AutoConfigurationImportSelector

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    /**
     * Exclude specific auto-configuration classes such that they will never be applied.
     * @return the classes to exclude
     */
    Class<?>[] exclude() default {};

    /**
     * Exclude specific auto-configuration class names such that they will never be
     * applied.
     * @return the class names to exclude
     * @since 1.3.0
     */
    String[] excludeName() default {};

}

AutoConfigurationImportSelector 的 selectImports 方法,查询所有 META-INF/spring.factories的类。

@Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                .loadMetadata(this.beanClassLoader);
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        //查找目标类
        List<String> configurations = getCandidateConfigurations(annotationMetadata,
                attributes);
        configurations = removeDuplicates(configurations);
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        configurations = filter(configurations, autoConfigurationMetadata);
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return StringUtils.toStringArray(configurations);
    }

getCandidateConfigurations方法中用到了SpringFactoriesLoader的loadFactoryNames方法,这个方法就是查找所有jar包中META-INF/spring.factories文件配置的类

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
            AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
                getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
        Assert.notEmpty(configurations,
                "No auto configuration classes found in META-INF/spring.factories. If you "
                        + "are using a custom packaging, make sure that file is correct.");
        return configurations;
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值