Spring源码分析---BeanFactory 后置处理器 05

来源:Spring

5.1 常见的 BeanFactory 后置处理器

先引入要用到的依赖:

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.3.0</version>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.15</version>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

需要用到的类信息:

package test.bean.a05;
@Configuration
@ComponentScan("test.bean.a05.component")
public class Config {

    @Bean
    public Bean1 bean1() {
        return new Bean1();
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }

    @Bean(initMethod = "init")
    public DruidDataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/advanced_spring");
        dataSource.setName("root");
        dataSource.setPassword("123456");
        return dataSource;
    }
}
package test.bean.a05;
@Slf4j
public class Bean1 {
    public Bean1() {
        System.out.println("我被 Spring 管理啦");
    }
}
package test.bean.a05.component;
@Slf4j
@Component
public class Bean2 {
    public Bean2() {
        log.info("我被 Spring 管理啦");
    }
}

继续使用 GenericApplicationContext 作为容器,向容器中注册 config:

public static void main(String[] args) {
    GenericApplicationContext context = new GenericApplicationContext();
    context.registerBean("config", Config.class);

    context.refresh();

    for (String name : context.getBeanDefinitionNames()) {
        System.out.println(name);
    }

    context.close();
}
config

并没有打印出除 config 以外的 Bean 信息,也就是说 Config 类中的 @ComponentScan 和 @Bean 注解都没有生效。

根据经验,显然是因为缺少某个后置处理器。

public static void main(String[] args) {
    GenericApplicationContext context = new GenericApplicationContext();
    context.registerBean("config", Config.class);
    // @ComponentScan @Bean @Import @ImportResource
    context.registerBean(ConfigurationClassPostProcessor.class);
    
}
test.bean.a05.component.Bean2 - 我被 Spring 管理啦
test.bean.a05.Bean1 - 我被 Spring 管理啦
com.alibaba.druid.pool.DruidDataSource - {dataSource-1,root} inited
config
org.springframework.context.annotation.ConfigurationClassPostProcessor
bean2
bean1
sqlSessionFactoryBean
dataSource

在使用 MyBatis 时,经常会使用到 @Mapper 注解,而这个注解的解析也需要使用到特定的 BeanFactory 后置处理器。

以下两个接口被 @Mapper 注解标记:

package test.bean.a05.mapper;
@Mapper
public interface Mapper1 {
}

@Mapper
public interface Mapper2 {
}

然后添加解析 @Mapper 注解的后置处理器:

public static void main(String[] args) {
    GenericApplicationContext context = new GenericApplicationContext();
    context.registerBean("config", Config.class);
    // @ComponentScan @Bean @Import @ImportResource
    context.registerBean(ConfigurationClassPostProcessor.class);
    context.registerBean(MapperScannerConfigurer.class,
                i -> i.getPropertyValues().add("basePackage", "test.bean.a05.mapper"));
    
}

其中的 basePackage 是 MapperScannerConfigurer 中的一个成员变量,表示需要扫描的包路径,设置的值恰好是被 @Mapper 注解标记的接口所在的包路径。

控制台打印的信息中增加了:

mapper1
mapper2

除此之外,还有一些常用的后置处理器并没有在上述信息中体现。

5.2 模拟实现
移除向容器中添加的 ConfigurationClassPostProcessor 和 MapperScannerConfigurer 两个后置处理器,自行编码模拟它们功能的实现。

组件扫描之 @ComponentScan

在 Bean2 所在包路径下再增加两个类,用于后续测试:

package test.bean.a05.component;
@Slf4j
@Controller
public class Bean3 {
    public Bean3() {
        log.info("我被 Spring 管理啦");
    }
}
@Slf4j
public class Bean4 {
    public Bean4() {
        log.info("我被 Spring 管理啦");
    }
}

编写 ComponentScanPostProcessor 用于实现 @ComponentScan 注解的解析:

public class ComponentScanPostProcessor implements BeanDefinitionRegistryPostProcessor {

    /**
     * 调用 context.refresh() 方法时回调
     */
    @Override
    @SneakyThrows
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
        if (componentScan != null) {
            for (String packageName : componentScan.basePackages()) {
                System.out.println(packageName);
                // test.bean.a05.component -> classpath*:test/bean/a05/component/**/**.class
                String path = "classpath*:" + packageName.replace(".", "/") + "/**/**.class";
                //  Resource[] resources = context.getResources(path);
                Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
                CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
                for (Resource resource : resources) {
                    MetadataReader reader = factory.getMetadataReader(resource);
                    AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
//                    System.out.println("类名: " + reader.getClassMetadata().getClassName());
//                    System.out.println("是否加了 @Component: " + annotationMetadata.hasAnnotation(Component.class.getName()));
//                    System.out.println("是否加了 @Component 派生: " + annotationMetadata.hasMetaAnnotation(Component.class.getName()));
                    if (annotationMetadata.hasAnnotation(Component.class.getName())
                            || annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
                        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(reader.getClassMetadata().getClassName())
                                .getBeanDefinition();
                        String name = generator.generateBeanName(beanDefinition, registry);
                        registry.registerBeanDefinition(name, beanDefinition);
                    }
                }
            }
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }
}

然后再测试:

public static void main(String[] args) {
    GenericApplicationContext context = new GenericApplicationContext();
    context.registerBean("config", Config.class);
    context.registerBean(ComponentScanPostProcessor.class);

    context.refresh();

    for (String name : context.getBeanDefinitionNames()) {
        System.out.println(name);
    }

    context.close();
}
test.bean.a05.component
test.bean.a05.component.Bean2      : 我被 Spring 管理啦
test.bean.a05.component.Bean3      : 我被 Spring 管理啦
config
test.bean.a05.ComponentScanPostProcessor
bean2
bean3

没使用 ConfigurationClassPostProcessor 也实现了 @ComponentScan 注解的解析!

@Bean 的解析

Config 类中再增加一个方法作为干扰项:

@Configuration
@ComponentScan("test.bean.a05.component")
public class Config {

    public Bean2 bean2() {
        return new Bean2();
    }
    
}

与解析 @ComponentScan 一样,自行编写一个 BeanFactoryPostProcessor 的实现类用于解析 @Bean 注解:


public class AtBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    @SneakyThrows
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
        MetadataReader reader = factory.getMetadataReader(new ClassPathResource("test/bean/a05/Config.class"));
        Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
        for (MethodMetadata method : methods) {
            System.out.println(method);
            String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();

            String methodName = method.getMethodName();
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition()
                    .setFactoryMethodOnBean(methodName, "config")
                    // 工厂方法、构造方法的注入模式使用构造器模式
                    .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
            if (StringUtils.hasLength(initMethod)) {
                builder.setInitMethodName(initMethod);
            }
            AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
            registry.registerBeanDefinition(methodName, beanDefinition);
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }
}

在构造 BeanDefinition 时调用了 setAutowireMode() 方法设置注入模式,这是因为在 Config 类中有一特殊的被 @Bean 标记的方法:

@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
    SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    sqlSessionFactoryBean.setDataSource(dataSource);
    return sqlSessionFactoryBean;
}

接收一个 DataSource 类型的参数,需要将容器中这个类型的 Bean 进行注入,设置的 AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR 注入模式则能完成这个功能。

public static void main(String[] args) {
    GenericApplicationContext context = new GenericApplicationContext();
    context.registerBean(AtBeanPostProcessor.class);

    context.refresh();

    for (String name : context.getBeanDefinitionNames()) {
        System.out.println(name);
    }

    context.close();
}
test.bean.a05.Config.bean1()
test.bean.a05.Config.sqlSessionFactoryBean(javax.sql.DataSource)
test.bean.a05.Config.dataSource()
test.bean.a05.Bean1                : 我被 Spring 管理啦
com.alibaba.druid.pool.DruidDataSource   : {dataSource-1,root} inited
config
test.bean.a05.AtBeanPostProcessor
bean1
sqlSessionFactoryBean
dataSource

@Mapper 的解析

@Mapper 注解是在接口上使用的,但根据前文内容可知,@Mapper 被解析后在 Spring 容器中也存在与被标记的接口相关的 Bean。

难道 Spring 能管理接口?

那肯定是不行的,Spring 只能管理对象这是毋庸置疑的。那这些接口是怎么变成对象被 Spring 管理的呢?

这依赖于 MapperFactoryBean 将接口转换为对象。

在 Config 添加注册 Mapper1 和 Mapper2 的方法:

@Bean
public MapperFactoryBean<Mapper1> mapper1(SqlSessionFactory sqlSessionFactory) {
    MapperFactoryBean<Mapper1> factoryBean = new MapperFactoryBean<>(Mapper1.class);
    factoryBean.setSqlSessionFactory(sqlSessionFactory);
    return factoryBean;
}

@Bean
public MapperFactoryBean<Mapper2> mapper2(SqlSessionFactory sqlSessionFactory) {
    MapperFactoryBean<Mapper2> factoryBean = new MapperFactoryBean<>(Mapper2.class);
    factoryBean.setSqlSessionFactory(sqlSessionFactory);
    return factoryBean;
}

再运行 main() 方法可以看到容器中存在名为 mapper1 和 mapper2 的 Bean。

这种方式虽然可以完成 Mapper 接口的注册,但每次只能单个注册,不能批量注册。

移除 Config 类中的 mapper1() 和 mapper2() 方法,自行编写 BeanDefinitionRegistryPostProcessor 接口的实现类完成 @Mapper 注解的解析:


public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    @SneakyThrows
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource[] resources = resolver.getResources("classpath:test/bean/a05/mapper/**/*.class");
        AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
        CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
        for (Resource resource : resources) {
            MetadataReader reader = factory.getMetadataReader(resource);
            ClassMetadata classMetadata = reader.getClassMetadata();
            if (classMetadata.isInterface()) {
                AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class)
                        .addConstructorArgValue(classMetadata.getClassName())
                        .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
                        .getBeanDefinition();
                AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName())
                        .getBeanDefinition();
                String name = generator.generateBeanName(bd, registry);
                registry.registerBeanDefinition(name, beanDefinition);
            }
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}

再测试下:

@SneakyThrows
public static void main(String[] args) {
    GenericApplicationContext context = new GenericApplicationContext();
    context.registerBean("config", Config.class);

    context.registerBean(AtBeanPostProcessor.class);
    /*
     * AtBeanPostProcessor 的注册不能少,因为需要容器中存在 SqlSessionFactoryBean
     * 而 SqlSessionFactoryBean 是在配置类中利用 @Bean 进行注册的
     */
    context.registerBean(MapperPostProcessor.class);

    context.refresh();

    for (String name : context.getBeanDefinitionNames()) {
        System.out.println(name);
    }

    context.close();
}
test.bean.a05.Config.bean1()
test.bean.a05.Config.sqlSessionFactoryBean(javax.sql.DataSource)
test.bean.a05.Config.dataSource()
test.bean.a05.Bean1                : 我被 Spring 管理啦
com.alibaba.druid.pool.DruidDataSource   : {dataSource-1,root} inited
config
test.bean.a05.AtBeanPostProcessor
test.bean.a05.MapperPostProcessor
bean1
sqlSessionFactoryBean
dataSource
mapper1
mapper2

容器中存在 mapper1 和 mapper2 两个 Bean。

另外-------注册创建完成的 Bean
如果要将 Bean 添加到 Spring 容器中,需要先根据配置文件或注解信息为每一个 Bean 生成一个 BeanDefinition,然后将这些 BeanDefinition 添加到 BeanDefinitionRegistry 中,当创建 Bean 对象时,直接从 BeanDefinitionRegistry 中获取 BeanDefinition 来生成 Bean。

如果生成的 Bean 是单例的,Spring 会将它们保存到 SingletonBeanRegistry 中,后续需要时从这里面寻找,避免重复创建。

那么向 Spring 容器中添加单例 Bean 时,可以跳过注册 BeanDefinition,直接向 SingletonBeanRegistry 中添加创建完成的 Bean。既然添加的是创建完成的 Bean,所以 这个 Bean 不会经过 Spring 的生命周期。

SingletonBeanRegistry 是一个接口,它有一个子接口名为 ConfigurableListableBeanFactory,而这恰好是 BeanFactoryPostProcessor 接口中抽象方法的参数:

@FunctionalInterface
public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

尝试使用 BeanFactoryPostProcessor 注册创建完成的 Bean:

@Slf4j
public class TestBeanFactoryPostProcessor {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.registerBean("bean2", Bean2.class);
        context.registerBean(MyBeanFactoryPostProcessor.class);
        context.refresh();

        Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
        System.out.println(">>>>>>>>>>>>>>>>>>");
        System.out.println(context.getBean(Bean1.class));
    }

    static class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            Bean1 bean1 = new Bean1();
            bean1.setName("test");
            beanFactory.registerSingleton("bean1", bean1);
        }
    }

    @Getter
    @ToString
    static class Bean1 {
        @Setter
        private String name;
        private Bean2 bean2;

        @Autowired
        private void setBean2(Bean2 bean2) {
            log.debug("依赖注入 bean2");
            this.bean2 = bean2;
        }

        @PostConstruct
        public void init() {
            log.debug("初始化...");
        }
    }

    static class Bean2 {
    }
}
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
bean2
testBeanFactoryPostProcessor.MyBeanFactoryPostProcessor
>>>>>>>>>>>>>>>>>>
TestBeanFactoryPostProcessor.Bean1(name=test, bean2=null)

BeanDefinition 的名称数组中不包含 bean1,也没有输出任何与经过 Spring 生命周期相关的日志信息,容器中 bean1 里注入的 bean2 也是 null。这表明通过这种方式注册的 Bean 不会注册 BeanDefinition,也不会经过 Spring 生命周期。

  • 17
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值