2_Bean的生命週期和常見的後處理器

LilyFlower發表於2024-06-10

1. Spring Bean生命週期各個階段

首先編寫一個Bean:

@Component
public class LifeCycleBean {
    public LifeCycleBean(){
        System.out.println("Bean 構造");
    }

    @Autowired
    public void autowire(@Value("${JAVA_HOME}")String javaHome){
        System.out.println("Bean 依賴注入:"+javaHome);
    }

    @PostConstruct
    public void init(){
        System.out.println("Bean 初始化");
    }

    @PreDestroy
    public void destroy(){
        System.out.println("Bean 被銷燬");
    }
}

編寫主方法測試程式碼:

@SpringBootApplication
public class A03Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(A03Application.class, args);
        context.close();

    }
}
Bean 構造
Bean 依賴注入:/opt/development_tools/jdk21/jdk-21.0.3
Bean 初始化
2024-06-08T17:06:10.657+08:00  INFO 30992 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 9000 (http) with context path ''
2024-06-08T17:06:10.672+08:00  INFO 30992 --- [           main] com.cherry.a03.A03Application            : Started A03Application in 2.645 seconds (process running for 3.334)
Bean 被銷燬

我們發現。完整的Bean生命週期是:

  1. 透過構造方法例項化Bean物件
  2. 對Bean物件進行依賴注入(該Bean 可能會引入其它的Bean物件)
  3. 對Bean物件進行初始化
  4. Bean物件隨著容器的銷燬而銷燬(當然是在容器銷燬前而銷燬)

由於預設的而BeanFactory對於Bean管理只有核心的功能,而對Bean功能的怎增強則是透過Bean後處理器來完成的,我破門可以使用不同的Bean後處理器針對Bean不同的生命週期進行擴充套件。這裡舉一個簡單的例子:

@Component
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {
    @Override
    public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
        if(beanName.equals("lifeCycleBean")){
            System.out.println("<<<銷燬之前執行 如 @PreDestroy<<<");
        }
    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if(beanName.equals("lifeCycleBean")){
            System.out.println("<<<例項化之前執行,這裡返回的物件會替換掉原來的bean  <<<");
        }
        return null;
    }


    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean")) {
            System.out.println("<<<例項化之後執行,這裡返回false會跳過依賴注入  <<<");
        }
        return true;
    }

    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        if(beanName.equals("lifeCycleBean")){
            System.out.println("<<<依賴注入階段執行,如@Autowired, @AValue, @Resource <<<");
        }
        return pvs;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if(beanName.equals("lifeCycleBean")){
            System.out.println("<<<初始化Bean之前執行,這裡返回的物件會替換掉原來的Bean物件,如@POsrConstruct, @ConfigurationProperties <<<");
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if(beanName.equals("lifeCycleBean")){
            System.out.println("<<< 初始化之後執行,這裡返回的物件會替換掉原來的Bean,如代理增強<<<");
        }
        return bean;
    }
}

執行:

<<<例項化之前執行,這裡返回的物件會替換掉原來的bean  <<<
Bean 構造
<<<例項化之後執行,這裡返回false會跳過依賴注入  <<<
<<<依賴注入階段執行,如@Autowired, @AValue, @Resource <<<
Bean 依賴注入:/opt/development_tools/jdk21/jdk-21.0.3
<<<初始化Bean之前執行,這裡返回的物件會替換掉原來的Bean物件,如@POsrConstruct, @ConfigurationProperties <<<
Bean 初始化
<<< 初始化之後執行,這裡返回的物件會替換掉原來的Bean,如代理增強<<<
2024-06-08T17:31:34.698+08:00  INFO 31600 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 9000 (http) with context path ''
2024-06-08T17:31:34.713+08:00  INFO 31600 --- [           main] com.cherry.a03.A03Application            : Started A03Application in 2.913 seconds (process running for 3.471)
<<<銷燬之前執行 如 @PreDestroy<<<
Bean 被銷燬

2. 模板設計模式

模板設計模式就是在不改變原有程式碼的基礎上,提高現有程式碼的擴充套件能力。

模擬Spring容器編寫一個BeanFactory

public class TestMethodTemplate {
    public static void main(String[] args) {
        MyBeanFactory beanFactory = new MyBeanFactory();
        beanFactory.addBeanPostProcessor(new BeanPostProcessor() {
            @Override
            public void inject(Object bean) {
                System.out.println("解析 @Autowired");
            }
        });

        beanFactory.addBeanPostProcessor(new BeanPostProcessor() {
            @Override
            public void inject(Object bean) {
                System.out.println("解析 @Resource");
            }
        });

        beanFactory.addBeanPostProcessor(new BeanPostProcessor() {
            @Override
            public void inject(Object bean) {
                System.out.println("解析 @Value");
            }
        });
        beanFactory.getBean();
    }

    static class MyBeanFactory{
        public Object getBean(){
            Object bean = new Object();
            System.out.println("構造: "+bean);
            System.out.println("依賴注入: "+bean);  //@Autowired註解解析
            for(BeanPostProcessor processor:processors){
                processor.inject(bean);
            }
            System.out.println("初始化: "+bean);
            return bean;
        }

        // 儲存bean後處理器
        private List<BeanPostProcessor> processors = new ArrayList<>();

        // 想後處理器集合中新增後處理器
        public void addBeanPostProcessor(BeanPostProcessor processor){
            processors.add(processor);
        }

    }

    static interface BeanPostProcessor{
        public void inject(Object bean);   // 對依賴注入階段的功能進行擴充套件
    }
}
構造: java.lang.Object@5f184fc6
依賴注入: java.lang.Object@5f184fc6
解析 @Autowired
解析 @Resource
解析 @Value
初始化: java.lang.Object@5f184fc6

Process finished with exit code 0

將不確定的邏輯操作抽象成介面,將來在特定的時機呼叫來呼叫抽象方法。

3. 常見的Bean後處理器

首先定義幾個Bean物件:

Bean1:

public class Bean1 {
    private Bean2 bean2;
    private Bean3 bean3;
    private String home;

    @Autowired
    public void setBean2(Bean2 bean2){
        System.out.println("@Autowired 生效:"+bean2);
        this.bean2 = bean2;
    }

    @Resource
    public void setBean3(Bean3 bean3){
        System.out.println("@Autowired 生效:"+bean3);
        this.bean3 = bean3;
    }

    @Autowired
    public void setHome(@Value("${JAVA_HOME}")String home){
        System.out.println("@Value 生效:"+home);
        this.home = home;
    }

    @PostConstruct
    public void init(){
        System.out.println("@PostConstruct 生效");
    }

    @PreDestroy
    public void destroy(){
        System.out.println("@PreDestroy 生效");
    }

    @Override
    public String toString() {
        return "Bean1{" +
                "bean2=" + bean2 +
                ", bean3=" + bean3 +
                ", home='" + home + '\'' +
                '}';
    }
}

Bean2:

public class Bean2 {}

Bean3:

public class Bean3 {}

Bean4:

/**
 *java.home=
 * java.version=
 */
@ConfigurationProperties(prefix = "java")
public class Bean4 {
    private String home;
    private String version;

    public String getHome() {
        return home;
    }

    public void setHome(String home) {
        this.home = home;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    @Override
    public String toString() {
        return "Bean4{" +
                "home='" + home + '\'' +
                ", version='" + version + '\'' +
                '}';
    }
}

主程式:

@SpringBootApplication
public class A04Application {
    public static void main(String[] args) {
        // GenericApplicationContext是一個比較容器,它並沒有新增額外的bean處理器
        GenericApplicationContext context = new GenericApplicationContext();
        // 想容器中註冊三個bean
        context.registerBean("bean1", Bean1.class);
        context.registerBean("bean2", Bean2.class);
        context.registerBean("bean3", Bean3.class);
        context.registerBean("bean4",Bean4.class);

        // 新增一個解析@Autowired和@Value註解的後處理器
        context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
        // 新增一個協助@Value值獲取的後處理器,一般配合AutowiredAnnotationBeanPostProcessor後處理器使用
        context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        // 新增一個解析@Resource,@PostConstruct,@PreDestroy註解的後處理器
        context.registerBean(CommonAnnotationBeanPostProcessor.class);
        // 新增對@ConfigurationProperties註解解析的後處理器
        ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());
        //初始化容器--執行beanFactory後處理器,新增bean後處理器,初始化所有單例
        context.refresh();
        System.out.println(context.getBean(Bean4.class));
        //銷燬容器
        context.close();
    }
}

執行結果如下:

@Autowired 生效:com.cherry.a04.Bean3@77167fb7
@Autowired 生效:com.cherry.a04.Bean2@2f112965
@Value 生效:/opt/development_tools/jdk21/jdk-21.0.3
@PostConstruct 生效
Bean4{home='/usr/lib/jvm/jdk-21-oracle-x64', version='21.0.3'}
@PreDestroy 生效

關於Bean後處理器 AutowiredAnnotationBeanPostProcessor 的執行分析:

編寫如下程式碼:

public class DigInAutowired {
    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

        beanFactory.registerSingleton("bean2", Bean2.class);
        beanFactory.registerSingleton("bean3", Bean3.class);
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        // 1.建立一個後處理器物件
        AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
        processor.setBeanFactory(beanFactory);
        Bean1 bean1 = new Bean1();
        processor.postProcessProperties(null,bean1,"bean1"); // 執行依賴注入@Autowired@Value
        System.out.println(bean1);
    }
}

debug進入到postProcessProperties方法中去:

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    // 首先找到有沒有加@Autowired註解的屬性,方法,找到後封裝到 InjectionMetadata 中
    InjectionMetadata metadata = this.findAutowiringMetadata(beanName, bean.getClass(), pvs);
        try {
            //透過反射給屬性賦值,set方法反射呼叫等
            metadata.inject(bean, beanName, pvs);
            return pvs;
        } catch (BeanCreationException var6) {
            BeanCreationException ex = var6;
            throw ex;
        } catch (Throwable var7) {
            Throwable ex = var7;
            throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
        }
    }

除此之外我們還可以使用反射的機制手動獲取@Autowird和@Value註解上的資訊:

public class DigInAutowired {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        beanFactory.registerSingleton("bean2", Bean2.class);
        beanFactory.registerSingleton("bean3", Bean3.class);
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        // 1.建立一個後處理器物件
        AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
        processor.setBeanFactory(beanFactory);
        Bean1 bean1 = new Bean1();
//        processor.postProcessProperties(null,bean1,"bean1"); // 執行依賴注入@Autowired@Value
//        System.out.println(bean1);
        Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata", String.class, Bean1.class);
        findAutowiringMetadata.setAccessible(true);
        //反射呼叫該方法:獲取bean1上加了@Autowired和@Value註解上的資訊,包括成員變數和方法引數
        Object injectionMetaData = (InjectionMetadata)findAutowiringMetadata.invoke(processor,"bean1", Bean1.class, null);
        System.out.println(injectionMetaData);
    }
}

4. 常見工廠後處理器

BeanFactory後處理器的作用:為BeanFactory提供擴充套件。

例如:

首先編寫如下Bean程式碼:

Config:

@Configuration
@ComponentScan("com.cherry.a05.component")
public class Config {
    private Bean1 bean1;
    @Bean
    public Bean1 bean1(){
        return new Bean1();
    }
}

Bean1:

@Component
public class Bean1 {}

編寫主方法檢視此時能否獲取Bean物件

public class A05Application {
    private static final Logger logger = LoggerFactory.getLogger(A05Application.class);
    public static void main(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config",Config.class);
        context.refresh();  //初始化容器
        for(String name:context.getBeanDefinitionNames()){
            System.out.println(name);
        }
    }
}
config

我們發現只有我們手動加入的bean,而組建掃描並未生效,因此被@Bean註解的方法也未加入到容器中。

因此我們們需要加入一個可以解析@@ComponentScan註解資訊的bean工廠後處理器:

public class A05Application {
    private static final Logger logger = LoggerFactory.getLogger(A05Application.class);
    public static void main(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean(ConfigurationClassPostProcessor.class);
        // 掃描mybatias的Mapper介面,並將這些就介面加入到容器中
        context.registerBean(MapperScannerConfigurer.class);
        context.registerBean("config",Config.class);
        context.refresh();  //初始化容器
        for(String name:context.getBeanDefinitionNames()){
            System.out.println(name);
        }
    }
}

此時再次執行:

config
bean2
bean3
bean4
bean1

ConfigurationClassPostProcessor處理器支援對@ComponentScan,,@Bean,@Import,@ImportResource 註解的解析

5. 工廠後處理器的模擬實現

5.1 元件掃描

public class A06ApplicationTest {
    public static void main(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();
        //查詢類上有沒有加@ComponentScan註解
        ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
        if(componentScan != null){
            //獲取註解上的屬性(包路徑)
            for (String p : componentScan.basePackages()) {
//                System.out.println(p);  // com.cherry.a05.component
                // 修改包名轉為路徑 --> 子包下的所有的類
                String packagePath = "classpath*:"+p.replace(".","/")+"/**/*.class";
                // System.out.println(packagePath);
                CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                Resource[] resources = context.getResources(packagePath);
                for(Resource res:resources){
                    // System.out.println(res);
                    MetadataReader reader = factory.getMetadataReader(res);
                    System.out.println(reader.getClassMetadata().getClassName());
                    // 判斷是否加入了@Component
                    System.out.println("是否加了@Component: "+reader.getAnnotationMetadata().hasAnnotation(Component.class.getName()));
                    System.out.println("是否加了@Component及其派生註解: "+reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName()));

                    if(reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName()) ||
                        reader.getAnnotationMetadata().hasAnnotation(Component.class.getName())){
                        // 建立一個BeanDefinition
                        AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(reader.getClassMetadata().getClassName()).getBeanDefinition();
                        // 為BeanDefinition生成Bean Name
                        AnnotationBeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
                        String beanName = beanNameGenerator.generateBeanName(bd, context.getDefaultListableBeanFactory());
                        // 將BeanDefinition加入到BeanFactory中
                        context.getDefaultListableBeanFactory().registerBeanDefinition(beanName, bd);
                    }
                }
            }
        }

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

執行結果如下:

com.cherry.a05.component.Bean2
是否加了@Component: true
是否加了@Component及其派生註解: false
com.cherry.a05.component.Bean3
是否加了@Component: true
是否加了@Component及其派生註解: false
com.cherry.a05.component.Bean4
是否加了@Component: false
是否加了@Component及其派生註解: true
09:19:43.994 [main] INFO com.cherry.a05.component.Bean2 -- 我被spring容器管理了...
09:19:44.011 [main] INFO com.cherry.a05.component.Bean3 -- 我被spring容器管理了...
09:19:44.012 [main] INFO com.cherry.a05.component.Bean4 -- 我被spring容器管理了...
===========
bean2
bean3
bean4

Process finished with exit code 0

把上述的元件掃描抽象出一個BeanFactoryPostProcessor:

public class ComponentScanPostProcessor implements BeanFactoryPostProcessor {
    // postProcessBeanFactory在context.refresh()時呼叫
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        try {
            //查詢類上有沒有加@ComponentScan註解
            ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
            if(componentScan != null){
                //獲取註解上的屬性(包路徑)
                for (String p : componentScan.basePackages()) {
//                System.out.println(p);  // com.cherry.a05.component
                    // 修改包名轉為路徑 --> 子包下的所有的類
                    String packagePath = "classpath*:"+p.replace(".","/")+"/**/*.class";
                    // System.out.println(packagePath);
                    CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                    Resource[] resources = new PathMatchingResourcePatternResolver().getResources(packagePath);
                    for(Resource res:resources){
                        // System.out.println(res);
                        MetadataReader reader = factory.getMetadataReader(res);
                        System.out.println(reader.getClassMetadata().getClassName());
                        // 判斷是否加入了@Component
                        System.out.println("是否加了@Component: "+reader.getAnnotationMetadata().hasAnnotation(Component.class.getName()));
                        System.out.println("是否加了@Component及其派生註解: "+reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName()));

                        if(reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName()) ||
                                reader.getAnnotationMetadata().hasAnnotation(Component.class.getName())){
                            // 建立一個BeanDefinition
                            AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(reader.getClassMetadata().getClassName()).getBeanDefinition();
                            // 為BeanDefinition生成Bean Name
                            AnnotationBeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
                            // 判斷 configurableListableBeanFactory 是否為 DefaultListableBeanFactory 的實現
                            if (configurableListableBeanFactory instanceof DefaultListableBeanFactory beanFactory) {
                                String beanName = beanNameGenerator.generateBeanName(bd, beanFactory);
                                // 將BeanDefinition加入到BeanFactory中
                                beanFactory.registerBeanDefinition(beanName, bd);
                            }
                        }
                    }
                }
            }
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

在主方法直接將bean工廠後處理器註冊到bean工廠中即可:

context.registerBean(ComponentScanPostProcessor.class);

5.2 對@Bean註解的解析

將@Bean標註的方法變成BeanDefinition並加入到容器中。

@SpringBootApplication
public class A06ApplicationTest {
    public static void main(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config",Config.class);
        CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
        // 讀取Config類的資訊
        MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/cherry/a05/Config.class"));
        // 拿到所有被@Bean註解標記的方法
        Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
        for(MethodMetadata metadata:methods){
            System.out.println(metadata.toString());
            // 將方法資訊生成對應的BeanDefinition
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
            builder.setFactoryMethodOnBean(metadata.getMethodName(), "config");
            builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
            AbstractBeanDefinition bd = builder.getBeanDefinition();
            // 加入到BeanFactory中
            context.getDefaultListableBeanFactory().registerBeanDefinition(metadata.getMethodName(),bd);
        }

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

5.3 對@Mapper註解的解析

public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {
    
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) {
        // 掃描Mapper包下的資源
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        try {
            Resource[] resources = resolver.getResources("classpath:com/cherry/a05/mapper/**/*.class");
            CachingMetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
            for(Resource res:resources){
                MetadataReader reader = metadataReaderFactory.getMetadataReader(res);
                ClassMetadata classMetadata = reader.getClassMetadata();
                // 判斷是否為介面
                if (classMetadata.isInterface()) {
                    // 生成對應的 BeanDefinition
                    AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class).
                            addConstructorArgValue(classMetadata.getClassName())
                            .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)//設定自動裝配
                            .getBeanDefinition();
                    AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
                    String beanName = generator.generateBeanName(bd, beanFactory);
                    beanFactory.registerBeanDefinition(beanName, bd);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinitionRegistryPostProcessor.super.postProcessBeanFactory(beanFactory);
    }
}

相關文章