Spring 高级 工厂后处理器模拟实现-@Bean

一、初步扫描到所有被@Bean注解标注的方法的信息

package com.mangoubiubiu.show.a05;

import com.mangoubiubiu.show.a05.component.ComponentScanPostProcessor;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.JmxEnabled;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.stereotype.Component;

import java.awt.*;
import java.io.IOException;
import java.util.Set;

@Slf4j
public class A05Application {

    private MetadataReader reader;

    public static void main(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();
        //将config注册到容器里面
        context.registerBean("config",Config.class);
//        context.registerBean(ComponentScanPostProcessor.class);
//        context.registerBean(MapperScannerConfigurer.class,beanDefinition -> {
//            beanDefinition.getPropertyValues().add("basePackage","com.mangoubiubiu.show.a05.mapper");
//        });


        CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
        //不走类加载 效率比反射高
        //先读取Config.class类的信息
        MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/mangoubiubiu/show/a05/Config.class"));

        //拿到所有被@Bean注解标注的方法的信息
        Set<MethodMetadata> annotatedMethods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
        for (MethodMetadata method:annotatedMethods) {
            System.out.println(method);
        }
        //初始化容器
        context.refresh();
        for (String name: context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        context.close();
    }
}
package com.mangoubiubiu.show.a05;

import com.alibaba.druid.pool.DruidDataSource;
import com.mangoubiubiu.show.a05.component.Bean2;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

@Configuration
@ComponentScan("com.mangoubiubiu.show.a05.component")
public class Config {


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

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

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


    @Bean(initMethod = "init")
    public DruidDataSource druidDataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/zeal_user");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }





}

成功拿到config被@Bean 标注的方法信息。

二、进阶拿到BeanDefinition将相应的bean 注入到ioc容器里面

package com.mangoubiubiu.show.a05;

import com.mangoubiubiu.show.a05.component.ComponentScanPostProcessor;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.JmxEnabled;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.stereotype.Component;

import java.awt.*;
import java.io.IOException;
import java.util.Set;

@Slf4j
public class A05Application {

    private MetadataReader reader;

    public static void main(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();
        //将config注册到容器里面
        context.registerBean("config",Config.class);
//        context.registerBean(ComponentScanPostProcessor.class);
//        context.registerBean(MapperScannerConfigurer.class,beanDefinition -> {
//            beanDefinition.getPropertyValues().add("basePackage","com.mangoubiubiu.show.a05.mapper");
//        });


        CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
        //不走类加载 效率比反射高
        //先读取Config.class类的信息
        MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/mangoubiubiu/show/a05/Config.class"));

        //拿到所有被@Bean注解标注的方法的信息
        Set<MethodMetadata> annotatedMethods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
        for (MethodMetadata method:annotatedMethods) {
            System.out.println(method);

            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
            //第一个参数 method.getMethodName() 获取方法名,哪个方法作为工厂方法 第二个参数 工厂对象 只需要指定工厂对象在容器里面的bean 名称
            builder.setFactoryMethodOnBean(method.getMethodName(),"config");
            //拿到BeanDefinition
            AbstractBeanDefinition bd = builder.getBeanDefinition();
            context.getDefaultListableBeanFactory().registerBeanDefinition(method.getMethodName(),bd);
        }


        //初始化容器

        context.refresh();

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

        context.close();
    }



}

进阶发现sqlSessionFactoryBean 多了一个DataSource 参数,这个参数默认要指定一个自动装配,不指定自动装配,没有办法解析工厂方法的参数

三、进阶创建BeanDefinition指定一个装配模式。

对于构造方法的参数,对于工厂方法的参数 如果要用自动装配的话,选择的装配模式是AUTOWIRE_CONSTRUCTOR

package com.mangoubiubiu.show.a05;

import com.mangoubiubiu.show.a05.component.ComponentScanPostProcessor;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.JmxEnabled;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.stereotype.Component;

import java.awt.*;
import java.io.IOException;
import java.util.Set;

@Slf4j
public class A05Application {

    private MetadataReader reader;

    public static void main(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();
        //将config注册到容器里面
        context.registerBean("config",Config.class);
//        context.registerBean(ComponentScanPostProcessor.class);
//        context.registerBean(MapperScannerConfigurer.class,beanDefinition -> {
//            beanDefinition.getPropertyValues().add("basePackage","com.mangoubiubiu.show.a05.mapper");
//        });


        CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
        //不走类加载 效率比反射高
        //先读取Config.class类的信息
        MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/mangoubiubiu/show/a05/Config.class"));

        //拿到所有被@Bean注解标注的方法的信息
        Set<MethodMetadata> annotatedMethods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
        for (MethodMetadata method:annotatedMethods) {
            System.out.println(method);

            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
            //第一个参数 method.getMethodName() 获取方法名,哪个方法作为工厂方法 第二个参数 工厂对象 只需要指定工厂对象在容器里面的bean 名称
            builder.setFactoryMethodOnBean(method.getMethodName(),"config");
            //拿到BeanDefinition
            //对于构造方法的参数,对于工厂方法的参数 如果要用自动装配的话,选择的装配模式是AUTOWIRE_CONSTRUCTOR
            builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
            AbstractBeanDefinition bd = builder.getBeanDefinition();
            context.getDefaultListableBeanFactory().registerBeanDefinition(method.getMethodName(),bd);
        }


        //初始化容器

        context.refresh();

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

        context.close();
    }



}

运行发现sqlSessionFactoryBean 被成功注入到IOC容器里面

但是initMethod 没有被解析

四、解析Bean相关属性 ,抽取成AtBeanPostProcessor

package com.mangoubiubiu.show.a05;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;

import java.io.IOException;
import java.util.Set;

public class AtBeanPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        try {
            CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
            //不走类加载 效率比反射高
            //先读取Config.class类的信息
            MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/mangoubiubiu/show/a05/Config.class"));

            //拿到所有被@Bean注解标注的方法的信息
            Set<MethodMetadata> annotatedMethods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
            for (MethodMetadata method:annotatedMethods) {
                System.out.println(method);
                BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
                //其他方法没有配置initMethod 获取到的是空字符串
                String initMethod = method.getAllAnnotationAttributes(Bean.class.getName()).get("initMethod").get(0).toString();
                if(initMethod.length()>0){
                    //设置初始化方法
                    builder.setInitMethodName(initMethod);
                }


                //第一个参数 method.getMethodName() 获取方法名,哪个方法作为工厂方法 第二个参数 工厂对象 只需要指定工厂对象在容器里面的bean 名称
                builder.setFactoryMethodOnBean(method.getMethodName(),"config");
                //拿到BeanDefinition
                //对于构造方法的参数,对于工厂方法的参数 如果要用自动装配的话,选择的装配模式是AUTOWIRE_CONSTRUCTOR
                builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
                AbstractBeanDefinition bd = builder.getBeanDefinition();
                if (configurableListableBeanFactory instanceof DefaultListableBeanFactory) {
                    DefaultListableBeanFactory beanFactory = ((DefaultListableBeanFactory) configurableListableBeanFactory);
                    beanFactory.registerBeanDefinition(method.getMethodName(),bd);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }


    }
}
package com.mangoubiubiu.show.a05;

import com.mangoubiubiu.show.a05.component.ComponentScanPostProcessor;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.JmxEnabled;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.stereotype.Component;

import java.awt.*;
import java.io.IOException;
import java.util.List;
import java.util.Set;

@Slf4j
public class A05Application {

    private MetadataReader reader;

    public static void main(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();
        //将config注册到容器里面
        context.registerBean("config",Config.class);
//        context.registerBean(ComponentScanPostProcessor.class);
//        context.registerBean(MapperScannerConfigurer.class,beanDefinition -> {
//            beanDefinition.getPropertyValues().add("basePackage","com.mangoubiubiu.show.a05.mapper");
//        });

        context.registerBean(AtBeanPostProcessor.class);

        //初始化容器

        context.refresh();

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

        context.close();
    }



}

五、总结

1 读取Config.class的源数据信息
 2  读取被@Bean标注的方法信息
 3 针对每一个方法生成 BeanDefinition
 4 指定bean 的名称 指定工厂方法是谁 是哪个Bean的方法
 5 对@Bean的属性做解析

 6 将Bean 注入到IOC容器中

 

 

 

posted @ 2022-08-22 22:38  KwFruit  阅读(34)  评论(0编辑  收藏  举报