Spring底层分析--5.BeanFactory后处理器

目录

 

一.ConfigurationClassPostProcessor后处理器

二.MapperScannerConfigurer

三.@ComponetScan组件扫描

1.原理

2.验证

3.抽取方法

三.@Bean实现

1.原理

2.验证

3.抽取方法

四.@Mapper实现

1.原理

2.验证

(1)创建两个mapper,在com.jjh.mapper包下:

(2)在配置类中来实现:

3.抽取方法


 

一.ConfigurationClassPostProcessor后处理器

先创建一个配置类

@Configuration
public class Config {

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

    @Bean
    public DataSource dataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://lcoalhost:3306?mybatis?useSSL=false&serverTimeZone=Asia/Shanghai");
        dataSource.setDriverClassName("com.jdbc.cj.mysql.Driver");
        dataSource.setUsername("jjh123");
        dataSource.setPassword("123456");
        return dataSource;
    }
}

创建GenericApplicationContext容器

 public static void main(String[] args) {

        GenericApplicationContext context  = new GenericApplicationContext();

        context.registerBean("config", Config.class);


        context.refresh();

        System.out.println(context.getBean("config"));

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

    }

运行程序:

18:18:31.329 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Refreshing org.springframework.context.support.GenericApplicationContext@1990a65e
18:18:31.348 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'config'
com.jjh.configure.Config@4cc451f2
config

我们发现只有config本身这个类被定义了,我们需要把他里面@Bean注解的bean都定义

添加ConfigurationClassPostProcessor

//设置ConfigurationClassPostProcessor
        context.registerBean(ConfigurationClassPostProcessor.class);//@Configuration @Bean @Import @ImportResource

再次运行:

config
org.springframework.context.annotation.ConfigurationClassPostProcessor
sqlSessionFactoryBean
dataSource

进程已结束,退出代码0

我们看到这三个bean也都定义了

二.MapperScannerConfigurer

下面我们定义两个mapper接口

@Mapper
public interface Mapper1 {
}


@Mapper
public interface Mapper2 {
}

然后为了能够定义这个被@Mapper注解的接口

添加:MapperScannerConfigurer

  //设置@Mapper注解解析
        context.registerBean(MapperScannerConfigurer.class,beanDefinition -> {
            beanDefinition.getPropertyValues().add("basePackage","com.jjh.mapper");
        });

我们需要给basePackage属性赋值

然后运行:

config
org.springframework.context.annotation.ConfigurationClassPostProcessor
org.mybatis.spring.mapper.MapperScannerConfigurer
sqlSessionFactoryBean
dataSource
mapper1
mapper2
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

进程已结束,退出代码0

我们可以看到mapper1和mapper2都被定义了,除此之外还加入了其他一些beanFactory后处理器

三.@ComponetScan组件扫描

1.原理

我们想要获取@ComponetScan(basePackages="")中的包名下的文件,然后判断是否被加上了@Componet注解,以此来创建bean对象

2.验证

首先,我们给Config类加上@ComponetScan注解

@Configuration
@ComponentScan("com.jjh.component")
public class Config {


}

这里指定了包名为com.jjh.component,下面我们来测试

//获取哪个类上有注解
        ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
        //获取componentScan的basePackages属性
        assert componentScan != null;
        String[] packages = componentScan.basePackages();
        //创建注解读取工厂
        CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();

        for (String p : packages) {

            String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";

            //获取这个路径下的文件
            for (Resource resource : context.getResources(path)) {
                //返回这个类的一些信息
                MetadataReader reader = factory.getMetadataReader(resource);
                System.out.println("类名:"+reader.getClassMetadata().getClassName());
                System.out.println("是否有@Component:"+reader.getAnnotationMetadata().hasAnnotation(Component.class.getName()));
                System.out.println("是否有@Component的派生:"+reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName()));
            }



        }

注意:这里最终需要把com.jjh.component变换为classpath*:com/jjh/component/**/*.class,

/**值的是这个包底下的所有子包,以.class结尾的文件

我们看一下哪些类注解了@Component

@Component
public class Bean1 {
}

public class Bean2 {
}

@Component
@ConfigurationProperties(prefix = "java")
public class Bean3 {
}


@Controller
public class Bean4 {
}

我们看到Bean2没有注解,然后Bean4注解的是@Controller,这个我们都知道,它的效果和@Component是一样的

下面我们来运行一下:

类名:com.jjh.component.Bean1
是否有@Component:true
是否有@Component的派生:false
类名:com.jjh.component.Bean2
是否有@Component:false
是否有@Component的派生:false
类名:com.jjh.component.Bean3
是否有@Component:true
是否有@Component的派生:false
类名:com.jjh.component.Bean4
是否有@Component:false
是否有@Component的派生:true

可以看到,成功的知道了哪些类含有@Component注解以及@Controller

下面,我们只需要把带注解的类注册到beanFactory中就可以了:

   if(reader.getAnnotationMetadata().hasAnnotation(Component.class.getName())
            ||reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName())){
                   //如果加了注解,我们需要给它注册到beanFactory中
                   AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                           .genericBeanDefinition(reader.getClassMetadata().getClassName()).getBeanDefinition();

                   DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
                   //生成bean名称
                   String name = generator.generateBeanName(beanDefinition,beanFactory);
                   //注册bean定义
                   beanFactory.registerBeanDefinition(name,beanDefinition);

注意:我们这里需要一个bean名称生成器,在之前定义一个:

  // 创建Bean名称生成器
        BeanNameGenerator generator = new AnnotationBeanNameGenerator();

现在,让我们获取这些bean名称,看一下是否定义成功:

 context.refresh();


         //获取bean名称
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }


运行:

config
bean1
bean3
bean4

可以看到,成功的把加了@Component和@Controller注解的bean定义成功了!

3.抽取方法

这样看起来很复杂,我们把这个流程抽取为一个类方法,然后把这个类注册到beanFactory中去,它就会自动执行

这里要实现BeanFactoryPostProcessor接口中的postProcessBeanFactory()方法,它会在context.

refresh()时调用,来解析@ComponentScan

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        try {
            //获取哪个类上有注解
            ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
            //获取componentScan的basePackages属性
            assert componentScan != null;
            String[] packages = componentScan.basePackages();
            //创建注解读取工厂
            CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
            // 创建Bean名称生成器
            BeanNameGenerator generator = new AnnotationBeanNameGenerator();

            for (String p : packages) {

                String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";

                //获取这个路径下的文件
                for (Resource resource : new PathMatchingResourcePatternResolver().getResources(path)) {
                    //返回这个类的一些信息
                    MetadataReader reader = factory.getMetadataReader(resource);
                    // System.out.println("类名:"+reader.getClassMetadata().getClassName());
                    // System.out.println("是否有@Component:"+reader.getAnnotationMetadata().hasAnnotation(Component.class.getName()));
                    // System.out.println("是否有@Component的派生:"+reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName()));

                    if(reader.getAnnotationMetadata().hasAnnotation(Component.class.getName())
                            ||reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName())){
                        //如果加了注解,我们需要给它注册到beanFactory中
                        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                                .genericBeanDefinition(reader.getClassMetadata().getClassName()).getBeanDefinition();


                            //生成bean名称
                            String name = generator.generateBeanName(beanDefinition, (BeanDefinitionRegistry) beanFactory);
                            //注册bean定义
                            ((BeanDefinitionRegistry) beanFactory).registerBeanDefinition(name,beanDefinition);


                    }
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

把它注册到context中:

  //添加我的beanFactory后处理器
        context.registerBean(MyBeanFactoryPostProcessor.class);

运行结果:

config
com.jjh.configure.MyBeanFactoryPostProcessor
bean1
bean3
bean4

可以看到同理

三.@Bean实现

1.原理

 同理,查找看哪个类上有@Configuration注解,然后获取这个类中带@Bean注解的方法,通过工厂方法,生产bean,然后把bean定义注册到beanFactory中即可

2.验证

创建配置类Config

@Configuration
@ComponentScan("com.jjh.component")
public class Config {

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

    @Bean
    public DataSource dataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://lcoalhost:3306?mybatis?useSSL=false&serverTimeZone=Asia/Shanghai");
        dataSource.setDriverClassName("com.jdbc.cj.mysql.Driver");
        dataSource.setUsername("jjh123");
        dataSource.setPassword("123456");
        return dataSource;
    }
}

然后写以下代码:

  CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
        //读取Config配置类
        MetadataReader metadataReader = factory.getMetadataReader(new ClassPathResource("com/jjh/configure/Config.class"));
        //获取@Bean注解的方法
        Set<MethodMetadata> beanMethods = metadataReader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());

        for (MethodMetadata beanMethod : beanMethods) {
           //获取到被@Bean注解的方法,接下来调用工厂方法生产bean
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
            builder.setFactoryMethodOnBean(beanMethod.getMethodName(),"config");
            //设置自动装配模式
            builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
            AbstractBeanDefinition definition = builder.getBeanDefinition();
            context.getDefaultListableBeanFactory()
                    .registerBeanDefinition(beanMethod.getMethodName(),definition);
        }

就是判断是否方法上有@Bean,如果有,就构造即可,

注意:仔细看Config类中SqlSessionFactoryBean中需要DataSource参数,这里我们需要设置自动装配模式

//设置自动装配模式
            builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);

然后我们来运行一下代码:


        context.refresh();


         //获取bean名称
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }


config
sqlSessionFactoryBean
dataSource

可以看到成功的定义了@Bean注解的bean

3.抽取方法

同样,我们也可以抽取为一个类方法,然后注册这个类

public class AtBeanBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
        //读取Config配置类
        MetadataReader metadataReader = null;
        try {
            metadataReader = factory.getMetadataReader(new ClassPathResource("com/jjh/configure/Config.class"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        //获取@Bean注解的方法
        Set<MethodMetadata> beanMethods = metadataReader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());

        for (MethodMetadata beanMethod : beanMethods) {
            //获取到被@Bean注解的方法,接下来调用工厂方法生产bean
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
            builder.setFactoryMethodOnBean(beanMethod.getMethodName(),"config");
            //设置自动装配模式
            builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
            AbstractBeanDefinition definition = builder.getBeanDefinition();
            //我们需要转型一下
            DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
            defaultListableBeanFactory.registerBeanDefinition(beanMethod.getMethodName(),definition);
        }

    }
}

注册这个类

  //添加@Bean 后处理器
        context.registerBean(AtBeanBeanFactoryPostProcessor.class);

运行,查看结果:

config
com.jjh.configure.AtBeanBeanFactoryPostProcessor
sqlSessionFactoryBean
dataSource

可以看到同样的效果

四.@Mapper实现

1.原理

我们知道这个注解是mybatis提供的用来创建dao层方法的,那么我们spring如何实例化他呢

因为他是一个接口,所以spirng实际上实现的是MapperFactoryBean,然后利用他的方法来创建mapper对象

2.验证

(1)创建两个mapper,在com.jjh.mapper包下:

@Mapper
public interface Mapper1 {
}

@Mapper
public interface Mapper2 {
}

(2)在配置类中来实现:

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

我们配置这个bean,然后利用我们上一步的@Bean的实现,直接运行即可:

查看结果:

config
com.jjh.configure.AtBeanBeanFactoryPostProcessor
sqlSessionFactoryBean
dataSource
mapper1

我们发现mapper1已经成功实现了!

3.抽取方法

下面我们直接抽取方法来实现:

package com.jjh.configure;

import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.*;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;

import java.io.IOException;

/**
 * @author JJH
 */
public class MapperBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {



    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {

        CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
         //创建bean名称生成器
        BeanNameGenerator generator = new AnnotationBeanNameGenerator();
        //获取类路径下的mapper文件
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource[] resources ;
        try {
            resources = resolver.getResources("classpath:com/jjh/mapper/**/*.class");
            for (Resource resource : resources) {
               //读取文件的原信息
                MetadataReader reader = factory.getMetadataReader(resource);
                //获取文件的类信息
                ClassMetadata classMetadata = reader.getClassMetadata();
                //判断该文件是否是接口
                if (classMetadata.isInterface()) {
                    //是接口,然后判断是否上面有@Mapper
                    if (reader.getAnnotationMetadata().hasAnnotation(Mapper.class.getName())) {
                        //有@Mapper注解,就需要构造方法了
                        BeanDefinitionBuilder builder = BeanDefinitionBuilder
                                .genericBeanDefinition(MapperFactoryBean.class);
                        //对它的set方法设置自动装配
                        builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
                        //对它的构造方法设置要传递的类型
                        builder.addConstructorArgValue(classMetadata.getClassName());
                        //构建beanDefinition
                        AbstractBeanDefinition bf1 = builder.getBeanDefinition();

                        //为了保持mapper的名称,我们需要在定义一个BeanDefinitionBuilder
                        BeanDefinition bf2 =BeanDefinitionBuilder
                                .genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();
                        //我们根据每个mapper生成bean名称
                        String name = generator.generateBeanName(bf2, beanFactory);
                        //我们将beanDefinition注入beanFactory
                        beanFactory.registerBeanDefinition(name,bf1);

                    }
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }


    }

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

    }
}

注册这个后处理类:

 //添加@Mapper 后处理器
        context.registerBean(MapperBeanFactoryPostProcessor.class);

运行代码,查看结果:

config
com.jjh.configure.AtBeanBeanFactoryPostProcessor
com.jjh.configure.MapperBeanFactoryPostProcessor
mapper1
mapper2
sqlSessionFactoryBean
dataSource

我们发现成功的将mapper1和mapper2定义到beanFactory中了!

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值