Skip to content
On this page

Spring 第三讲 BeanFactory 后置处理器

pom.xml 准备

xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.5</version>
        <relativePath/>
    </parent>

    <groupId>org.laoshiren</groupId>
    <artifactId>spring-bean-init</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>


        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

    </dependencies>
</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

BeanFactory后置处理器

java
public class MyBeanFactoryPostProcessor {

    public static void main(String[] args) {
        GenericApplicationContext ctx = new GenericApplicationContext();
        ctx.registerBean("config", Config.class);
        // 需要添加BeanFactory后置处理器
        ctx.registerBean(ConfigurationClassPostProcessor.class);
        // 扫描@ComponentScan @Bean @Import @ImportResource
        ctx.refresh();
        for (String beanDefinitionName : ctx.getBeanDefinitionNames()) {
            System.out.println("\t" + beanDefinitionName);
        }
        ctx.close();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

BeanFactoryPostProcessor 是如何工作的?首先我们都知道Spring 里大概流程就是:

  1. 告诉ApplicationContext 资源位置
  2. 把资源加载到BeanFactory 里
  3. ApplicationContext 把BeanFactoryPostProcessor 注册到BeanFactory 中
  4. BeanFactoryPostProcessor 执行对应的操作将Bean 加载到BeanFactory
  5. 实例化Bean
  6. BeanPostProcessor 做后续的操作。
java
public class AbstractApplicationContext {
    public void refresh(){
      // 上面省略
      // Allows post-processing of the bean factory in context subclasses.
			postProcessBeanFactory(beanFactory);
			StartupStep beanPostProcess = 
        this.applicationStartup.start("spring.context.beans.post-process");
			// Invoke factory processors registered as beans in the context.
			invokeBeanFactoryPostProcessors(beanFactory);
      // 下面也省略
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

refresh()方法中把BeanFactory 后置处理器注册到BeanFactory 然后调用了他的方法将Bean 加载到容器中。

那应该如何注册呢

我们以ComponentScan 为例,简单还原一下

  1. 看他的basePackages
  2. 找到对应的resource
  3. 看看带不带Component 注解
  4. 注册对应的BeanDefinition
java
public class MockAnnotationBeanFactoryPostProcessor {

    public static void main(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
        DefaultListableBeanFactory defaultListableBeanFactory = 
          context.getDefaultListableBeanFactory();
        // 查找是否存在 ComponentScan 注解
        ComponentScan componentScan =
                AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
        if (componentScan!=null) {
            for (String basePackage: componentScan.basePackages()) {
                System.out.println(basePackage);
              	// 最终是以字节码的方式找
                String filePath = "classpath:*"+basePackage.replace(".","/")+"/**/*.class";
                System.out.println(filePath);
                Resource[] resources = context.getResources(filePath);
                // 没有context 的情况
                // resources = new PathMatchingResourcePatternResolver().getResources(filePath);
                // 读取类的元信息的工厂
                CachingMetadataReaderFactory metadataReaderFactory =new CachingMetadataReaderFactory();
                // bean Name 生存器
                AnnotationBeanNameGenerator beanNameGenerator =  new AnnotationBeanNameGenerator();
                for (Resource resource : resources) {
                    System.out.println(resource);
                    // 读取类的元信息
                    MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
                    // 类的元信息
                    ClassMetadata classMetadata = metadataReader.getClassMetadata();
                    // 注解元信息
                    AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
                    // 无法获取到他的派生注解 比如@Controller @Service
                    boolean hasComponentAnnotation = annotationMetadata.hasAnnotation(Component.class.getName());
                    // 获取派生注解
                    boolean hasMetaComponentAnnotation = annotationMetadata.hasMetaAnnotation(Component.class.getName());
										// 如果是 Component注解 或者是派生的注解
                    if (hasComponentAnnotation || hasMetaComponentAnnotation) {
                        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                                .genericBeanDefinition(classMetadata.getClassName())
                                .getBeanDefinition();
                        // Bean name 
                        String beanName =
                                beanNameGenerator.generateBeanName(beanDefinition, defaultListableBeanFactory);
                      	// 注册Bean
                        defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinition);
                    }
                }
            }
        }
        context.refresh();
        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }
        context.close();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

@Bean 的解析处理

如何解析Configuration 修饰的类里的 @Bean 注解修饰方法。

  1. 找到类
  2. 找到方法
  3. 转换成BeanDefinition
  4. 加载到BeanFactory
java
public class MockAnnotationConfigurationBeanFactoryPostProcessor {

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

        CachingMetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
        MetadataReader metadataReader =
                metadataReaderFactory.getMetadataReader(new ClassPathResource("com/laoshiren/app/bean/Config.class"));
        // 获取注解元信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        // 获取被注解标注的方法
        Set<MethodMetadata> annotatedMethods = annotationMetadata.getAnnotatedMethods(Bean.class.getName());
        for (MethodMetadata annotatedMethod : annotatedMethods) {
            System.out.println(annotatedMethod);
            // 创建BeanDefinition
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition();
            // 没搞懂如何拿到 config
          	// 第一个参数表示那些被@Bean修饰的方法
          	// 第二个参数需要FactoryBean 那谁是他的factoryBean 呢 这个Bean 被谁创建呢
            beanDefinitionBuilder.setFactoryMethodOnBean(annotatedMethod.getMethodName(), "config")
                    // 有参数的方法需要指定装配模式 默认是No 
              			//在对于构造方法的参数和工厂方法的参数都是CONSTRUCTOR
                    .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
            // 初始化方法
            String initMethod = annotatedMethod.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
            if (StringUtils.hasText(initMethod)) {
                beanDefinitionBuilder.setInitMethodName(initMethod);
            }
            // 同样还有销毁的方法
            // beanDefinitionBuilder.setDestroyMethodName("");
            AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
            // 方法名字作为bean Name
            beanFactory.registerBeanDefinition(annotatedMethod.getMethodName(), beanDefinition);
        }
        context.refresh();
        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }
        context.close();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

@MapperScannerConfigurer

以后自己实现的BeanFactoryPostProcessor都可以直接实现 BeanDefinitionRegistryPostProcessor,这样就不用为了ConfigurableListableBeanFactory 无法直接注册BeanDefinition 而强转了。

java
public class MapperBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        try {
            // 找注解省略
            // 找包省略
            // 找 mapper
            PathMatchingResourcePatternResolver patternResolver = new PathMatchingResourcePatternResolver();
            Resource[] resources = patternResolver
                    .getResources("classpath:com/laoshiren/app/mapper/mapper/**/*.class");
            // 读取元数据
            CachingMetadataReaderFactory readerFactory = new CachingMetadataReaderFactory();
            AnnotationBeanNameGenerator beanNameGenerator =  new AnnotationBeanNameGenerator();
            for (Resource resource : resources) {
                MetadataReader metadataReader = readerFactory.getMetadataReader(resource);
                // 类属性
                ClassMetadata classMetadata = metadataReader.getClassMetadata();
                // 不是接口就跳过
                if (!classMetadata.isInterface()) {
                    continue;
                }       
                // 没有@Mapper 注解就跳过
                boolean hasMapper = annotationMetadata.hasAnnotation(Mapper.class.getName());
                if (!hasMapper) {
                    continue;
                }
                // 准备生成MapperFactoryBean
                AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                        .genericBeanDefinition(MapperFactoryBean.class)
                        // 添加构造方法的值
                        .addConstructorArgValue(classMetadata.getClassName())
                        // 自动装配 去装SqlSessionFactory
                        .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
                        .getBeanDefinition();
                // 需要用各自的FactoryBean 里实现的类的名字
                AbstractBeanDefinition beanNameDefinition = BeanDefinitionBuilder
                        .genericBeanDefinition(classMetadata.getClassName())
                        .getBeanDefinition();
                // 更具beanNameDefinition 创建BeanName
                String beanName = beanNameGenerator.generateBeanName(beanNameDefinition, registry);
                // 直接注册beanDefinition 会导致后注册上来的BeanName 与前面的一致而覆盖(因为都是MapperFactoryBean)
                // 所以需要重新注册一个Bean 就用mapper本身作为name去注册防止覆盖
                // 注册到beanFactory
                registry.registerBeanDefinition(beanName, beanDefinition);

            }

        } catch (IOException e) {
            throw new BeanCreationException(e.getMessage());
        }
    }

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

    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

Released under the MIT License.