IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Sping高级(源码)--- 1.5BeanFactory后处理器 -> 正文阅读

[Java知识库]Sping高级(源码)--- 1.5BeanFactory后处理器

1.5.1BeanFactory后处理器的作用:为BeanFactory提供扩展

  1. ConfigurationClassPostProcessor和MapperScannerConfigurer基本作用:扫描所有注解的包
  • Bean1
@Slf4j
public class Bean1 {
    public Bean1() {
        log.debug("我被 Spring 管理啦");
    }
}
  • Config
@Configuration
@ComponentScan("com.spring.demo.demo05.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/javatest");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        return dataSource;
    }
}
  • Mapper1、Mapper2、Mapper3
@Mapper
public interface Mapper1 {
}

项目结构

---demo05
------component
---------Bean2
---------Bean3
---------Bean4
------mapper
---------Mapper2
---------Mapper2
---------Mapper3
------A05Application
------Bean1
------Config
  • A05Application
@Slf4j
public class A05Application {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config",Config.class);

        //解析@ComponentScan、@Bean、@Import等等
        context.registerBean(ConfigurationClassPostProcessor.class);

        //扫描mybatis的注解,比如:@MapperScan
        context.registerBean(MapperScannerConfigurer.class,db->{
            //指定扫描某个包,类似@MapperScan
            db.getPropertyValues().add("basePackage","com.spring.demo.demo05.mapper");
        });

        //初始化
        context.refresh();

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

        context.close();
    }
}

运行结果:

config
org.springframework.context.annotation.ConfigurationClassPostProcessor
org.mybatis.spring.mapper.MapperScannerConfigurer
bean2
bean3
bean1
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

如果不添加后置处理器的解析,运行结果:

config

1.5.2常见的BeanFactory后处理器

  1. 组件扫描ComponentScan:根据组件扫描找到对应的包,获取到该包下的所有资源
@Slf4j
public class A05Application {
    public static void main(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config",Config.class);

        //解析@ComponentScan、@Bean、@Import等等
//        context.registerBean(ConfigurationClassPostProcessor.class);
//
//        //扫描mybatis的注解,比如:@MapperScan
//        context.registerBean(MapperScannerConfigurer.class,db->{
//            //指定扫描某个包,类似@MapperScan
//            db.getPropertyValues().add("basePackage","com.spring.demo.demo05.mapper");
//        });

        //找到config类的@ComponentScan注解
        ComponentScan annotation = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);

        if(annotation != null){
            for (String s : annotation.basePackages()) {
                System.out.println("组件扫描到的包:"+s);
                //获取资源路径,所有子包下的文件
                //com.spring.demo.demo05.component  -> classpath*:com/spring/demo/demo05/component/**/*.class
                String path = "classpath*:"+s.replace(".", "/")+"/**/*.class";
                System.out.println("资源路径:"+path);
                //获取路径下的所有资源
                Resource[] resource = context.getResources(path);
                for (Resource resource1 : resource) {
                    System.out.println(resource1);
                }
            }
        }

        //初始化
        context.refresh();

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

        context.close();
    }
}

运行结果:

组件扫描到的包:com.spring.demo.demo05.component
资源路径:classpath*:com/spring/demo/demo05/component/**/*.class
file [E:\Demo\JavaDemo\SpringSourceCodeDemo\target\classes\com\spring\demo\demo05\component\Bean2.class]
file [E:\Demo\JavaDemo\SpringSourceCodeDemo\target\classes\com\spring\demo\demo05\component\Bean3.class]
file [E:\Demo\JavaDemo\SpringSourceCodeDemo\target\classes\com\spring\demo\demo05\component\Bean4.class]
  • 对上面的方法修改一下,可以判断类上是否加了某些注解
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();

                //获取路径下的所有资源
                Resource[] resource = context.getResources(path);
                for (Resource resource1 : resource) {
//                    System.out.println(resource1);
                    //读取每一个资源
                    MetadataReader metadataReader = factory.getMetadataReader(resource1);
                    //
                    System.out.println("类名:"+metadataReader.getClassMetadata().getClassName());
                    System.out.println("是否加了@Compoent:"+metadataReader.getAnnotationMetadata().hasAnnotation(Component.class.getName()));
                    //是否间接加了Component注解,比如@Controller源码内使用了Component注解,可以被扫描到
                    System.out.println("是否间接加了@Compoent:"+metadataReader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName()));
                    System.out.println("--------");
                }

运行结果:

类名:com.spring.demo.demo05.component.Bean2
是否加了@Compoent:true
是否间接加了@Compoent:false
--------
类名:com.spring.demo.demo05.component.Bean3
是否加了@Compoent:false
是否间接加了@Compoent:true
--------
类名:com.spring.demo.demo05.component.Bean4
是否加了@Compoent:false
是否间接加了@Compoent:false
--------
  1. 将加了或间接加了@Compoent注解的类注册为Bean

修改2中的代码

//1.获取路径下的所有资源
Resource[] resource = context.getResources(path);
//获取bean名称的工具
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
for (Resource resource1 : resource) {
    //读取每一个资源
    MetadataReader metadataReader = factory.getMetadataReader(resource1);
    AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();

    //3.如果直接加了或间接加了,那么我们将他们注册为bean
    if(annotationMetadata.hasAnnotation(Component.class.getName())
       || annotationMetadata.hasMetaAnnotation(Component.class.getName())){
        //获取类名,获取bean定义
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
            .genericBeanDefinition(metadataReader.getClassMetadata().getClassName())
            .getBeanDefinition();

        //获取bean工厂
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();

        //获取名字
        String name = generator.generateBeanName(beanDefinition, beanFactory);

        //bean注册
        beanFactory.registerBeanDefinition(name,beanDefinition);
    }
}
//初始化
context.refresh();

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

context.close();

运行结果:

bean:config
bean:bean2
bean:bean3

我们可以将该方法抽取出来:

  • 新建ComponentScanPostProcessor类,抽取代码
public class ComponentScanPostProcessor implements BeanDefinitionRegistryPostProcessor {
    //context.refresh会回调该代码
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory){
        try {
            //找到config类的@ComponentScan注解
            ComponentScan annotation = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
            if (annotation != null) {
                for (String s : annotation.basePackages()) {
                    //获取资源路径,所有子包下的文件
                    //com.spring.demo.demo05.component  -> classpath*:com/spring/demo/demo05/component/**/*.class
                    String path = "classpath*:" + s.replace(".", "/") + "/**/*.class";
                    System.out.println(path);

                    CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();

                    //1.获取路径下的所有资源
                    Resource[] resource = new PathMatchingResourcePatternResolver().getResources(path);

                    //获取bean名称的工具
                    AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
                    for (Resource resource1 : resource) {
                        //读取每一个资源
                        MetadataReader metadataReader = factory.getMetadataReader(resource1);
                        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();

                        //3.如果直接加了或间接加了,那么我们将他们注册为bean
                        if (annotationMetadata.hasAnnotation(Component.class.getName())
                                || annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
                            //获取类名,获取bean定义
                            AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                                    .genericBeanDefinition(metadataReader.getClassMetadata().getClassName())
                                    .getBeanDefinition();


                            String name = generator.generateBeanName(beanDefinition, beanFactory);

                            //bean注册
                            beanFactory.registerBeanDefinition(name, beanDefinition);

                        }
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

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

    }
}
  • A05Application
/**
 * @author 我见青山多妩媚
 * @date Create on 2022/10/4 8:48
 */
@Slf4j
public class A05Application {
    public static void main(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config",Config.class);
        
        context.registerBean(ComponentScanPostProcessor.class);

        //初始化
        context.refresh();

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

        context.close();
    }
}

运行结果:

bean:config
bean:com.spring.demo.demo05.ComponentScanPostProcessor
bean:bean2
bean:bean3
  1. @Bean注解的解析
  • AtBeanPostProcessor类
/**
 * @author 我见青山多妩媚
 * @date Create on 2022/10/6 9:15
 */
public class AtBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
        try{
            //@Bean注解的解析
            CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
            //指定@Bean所在类
            MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/spring/demo/demo05/Config.class"));

            //读取到bean
            Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());

            //遍历bean
            for (MethodMetadata method : methods) {
                System.out.println("bean解析--->"+method);

                String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();


                BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
                //给builder设置bean
                builder.setFactoryMethodOnBean(method.getMethodName(), "config");

                //如果不开启,sqlSessionFactoryBean读取bean时会报错,因为他有个参数(DataSource dataSource),所以此处开启自动装配
                //开启自动装配,对于构造方法的参数就是用这个
                builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);

                //如果该注解上有initMethod,
                if(initMethod != null){
                    //配置注解上的参数
                    builder.setInitMethodName(initMethod);
                }

                //得到beanDefinition
                AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();

                //添加到bean工厂内(bean名称,beanDefinition)
                beanFactory.registerBeanDefinition(method.getMethodName(),beanDefinition);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
  • A05Application
//注册
context.registerBean(AtBeanPostProcessor.class);

运行结果:

bean解析--->org.springframework.core.type.classreading.SimpleMethodMetadata@3ffcd140
bean解析--->org.springframework.core.type.classreading.SimpleMethodMetadata@23bb8443
bean解析--->org.springframework.core.type.classreading.SimpleMethodMetadata@1176dcec

//日志信息

bean:config
bean:com.spring.demo.demo05.AtBeanPostProcessor
bean:bean1
bean:sqlSessionFactoryBean
bean:dataSource
  1. @Mapper解析

最简单的解析:

  • config
    @Bean
    public MapperFactoryBean<Mapper1> mapper1(SqlSessionFactory sqlSessionFactory){
        //mapper1注册到spring容器内
        MapperFactoryBean<Mapper1> factory = new MapperFactoryBean<>(Mapper1.class);
        factory.setSqlSessionFactory(sqlSessionFactory);
        return factory;
    }

    @Bean
    public MapperFactoryBean<Mapper2> mapper2(SqlSessionFactory sqlSessionFactory){
        //mapper1注册到spring容器内
        MapperFactoryBean<Mapper2> factory = new MapperFactoryBean<>(Mapper2.class);
        factory.setSqlSessionFactory(sqlSessionFactory);
        return factory;
    }

但是这样的比较死板,不易扩展,虽然spring内部 也是这样实现的,但是spring也有更好的一种实现,我们模拟一下:

  • MapperPostProcessor类
/**
 * @author 我见青山多妩媚
 * @date Create on 2022/10/6 11:01
 */
public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
        try {
            PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            //查找mapper资源
            Resource[] resources = resolver.getResources("classpath:com/spring/demo/demo05/mapper/**/*.class");

            CachingMetadataReaderFactory factory = new  CachingMetadataReaderFactory();

            //名字生成
            AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
            for (Resource resource : resources) {
                //获取资源原信息
                MetadataReader reader = factory.getMetadataReader(resource);
                ClassMetadata classMetadata = reader.getClassMetadata();
                //判断是否是接口
                if (classMetadata.isInterface()) {
                    //管理的类型为mapper,添加到容器内的
                    AbstractBeanDefinition bd1 = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class)
                            //给构造方法设置参数,获取接口类名
                            .addConstructorArgValue(classMetadata.getClassName())
                            //设置自动装配,根据类型自动装配,因为config有自己定义的sqlSessionFactory
                            .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
                            .getBeanDefinition();
                    //用来区分名字,根据接口生成BeanDefinition
                    AbstractBeanDefinition bd2 = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();

                    //此处不能用使用bd1,因为会覆盖,此处生成的名字就是mapper1、mapper2
                    String name = generator.generateBeanName(bd2, beanFactory);
                    //注册bean工厂
                    beanFactory.registerBeanDefinition(name,bd1);
                    //spring内部实现方法也是这样
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

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

    }
}
  • A05Application
context.registerBean(MapperPostProcessor.class);

运行结果:

bean:config
bean:com.spring.demo.demo05.AtBeanPostProcessor
bean:com.spring.demo.demo05.MapperPostProcessor
bean:bean1
bean:sqlSessionFactoryBean
bean:dataSource
bean:mapper1
bean:mapper2

mapper1和mapper已经被解析到了

最后的目录结构:

---demo05
------component
---------Bean2
---------Bean3
---------Bean4
------mapper
---------Mapper2
---------Mapper2
---------Mapper3
------A05Application
------Bean1
------Config
------AtBeanPostProcessor
------ComponentScanPostProcessor
------MapperPostProcessor
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-10-31 11:40:46  更:2022-10-31 11:43:11 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 11:41:59-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码