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知识库 -> 第5讲 - BeanFactory后处理器 -> 正文阅读

[Java知识库]第5讲 - BeanFactory后处理器

BeanFactory后处理器的作用

先来看看没有BeanFactory后处理器时有哪些常见的注解不能解析

public class A05Application {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);

        // 初始化容器
        context.refresh();
        // 打印容器中所有的bean
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }

        context.close();
    }
}

@Configuration
@ComponentScan("com.shuttle.spring5")
public class Config {

    @Bean
    public Bean01 bean01() {
        return new Bean01();
    }

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

    // Druid连接池
    @Bean(initMethod = "init")
    public DruidDataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        return dataSource;
    }

}

public class Bean01 {

    public Bean01() {
    }

}

执行结果如下:

config?

可以看到就只有手工注册的config对象,标注了@ComponentScan、@Bean注解等都没有生效。现在我们给启动类中加入BeanFactory后处理器。

context.registerBean(ConfigurationClassPostProcessor.class);

再次执行结果如下:

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

可以发现注解已经生效。

ConfigurationClassPostProcessor后处理器其实并不只能解析以上注解,还包括@Import、@ImportResource注解等等,有兴趣的大佬可以课后了解下。

再来看看实现@MapperScan注解的后处理器 - MapperScannerConfigurer

创建两个Mapper接口并给类上添加@Mapper注解

@Mapper
public interface Mapper01 {
}

@Mapper
public interface Mapper02 {
}

在不添加额外后处理器的情况下执行结果并没有看到有接口bean对象,也就是说明@Mapper注解需要额外的后处理器来解析,现在我们给容器中添加?MapperScannerConfigurer 后处理器并按照如下配置【basePackage:扫描的包范围】

    context.registerBean(MapperScannerConfigurer.class, beanDefinition -> {
        beanDefinition.getPropertyValues().add("basePackage", "com.shuttle.spring5");
    });

可以发现已经有了mapper接口bean对象的出现

模拟实现常见后处理器

模拟实现解析Component注解

@Component
public class Bean01 {
    public Bean01() {
    }
}

@Component
public class Bean02 {
    public Bean02() {
    }
}

@Controller
public class Bean03 {
    public Bean03() {
    }
}

现在来模拟下解析@Component注解及其派生注解,之后将其注册进容器中。

        // 利用Spring提供的工具方法找到指定类上有没有标注指定注解
        ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
        // 若不为空则进行解析
        if (componentScan != null) {
            // 获取扫描包范围
            for (String path : componentScan.basePackages()) {
                // 将解析出来的路径转换为可以获取的资源路径
                // path -> com.shuttle.spring5 -> classpath*:com/shuttle/spring5/**/*.class
                String classPath = "classpath*:" + path.replace(".", "/") + "/**/*.class";
                // 帮助获取类信息
                CachingMetadataReaderFactory readerFactory = new CachingMetadataReaderFactory();
                // 帮助生成beanName
                AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
                // 获取资源
                Resource[] resources = context.getResources(classPath);
                for (Resource resource : resources) {
                    // 获取类元信息
                    MetadataReader reader = readerFactory.getMetadataReader(resource);
                    // 检查是否含有Component注解【只会解析Component,不会解析Component派生注解,例如Controller】
                    boolean hasComponent = reader.getAnnotationMetadata().hasAnnotation(Component.class.getName());
                    // 解析只会解析Component派生注解,例如Controller
                    boolean hasMetaComponent = reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName());
                    // 如果包含Component或者其派生注解
                    if (hasComponent || hasMetaComponent) {
                        // 根据类元信息生成BeanDefinition信息
                        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                                .genericBeanDefinition(reader.getClassMetadata().getClassName())
                                .getBeanDefinition();
                        // 获取BeanFactory
                        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
                        // 生成beanName
                        String name = generator.generateBeanName(beanDefinition, beanFactory);
                        // 注册beanDefinition
                        beanFactory.registerBeanDefinition(name, beanDefinition);
                    }
                }
            }
        }

将?context.registerBean(ConfigurationClassPostProcessor.class); 注释后打印结果如下:

可以看到手动写的方法生效了,但现在离成功还有一步之遥,那就是将其抽取成自定义的BeanFactory后处理器。

public class ComponentScanPostProcessor implements BeanDefinitionRegistryPostProcessor {

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

    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
        try {
            // 利用Spring提供的工具方法找到指定类上有没有标注指定注解
            ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
            // 若不为空则进行解析
            if (componentScan != null) {
                // 获取扫描包范围
                for (String path : componentScan.basePackages()) {
                    // 将解析出来的路径转换为可以获取的资源路径
                    // path -> com.shuttle.spring5 -> classpath*:com/shuttle/spring5/**/*.class
                    String classPath = "classpath*:" + path.replace(".", "/") + "/**/*.class";
                    // 帮助获取类信息
                    CachingMetadataReaderFactory readerFactory = new CachingMetadataReaderFactory();
                    // 帮助生成beanName
                    AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
                    // 获取资源
                    Resource[] resources = new PathMatchingResourcePatternResolver().getResources(classPath);
                    for (Resource resource : resources) {
                        // 获取类元信息
                        MetadataReader reader = readerFactory.getMetadataReader(resource);
                        // 检查是否含有Component注解【只会解析Component,不会解析Component派生注解,例如Controller】
                        boolean hasComponent = reader.getAnnotationMetadata().hasAnnotation(Component.class.getName());
                        // 解析只会解析Component派生注解,例如Controller
                        boolean hasMetaComponent = reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName());
                        // 如果包含Component或者其派生注解
                        if (hasComponent || hasMetaComponent) {
                            // 根据类元信息生成BeanDefinition信息
                            AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                                    .genericBeanDefinition(reader.getClassMetadata().getClassName())
                                    .getBeanDefinition();
                            // 生成beanName
                            String name = generator.generateBeanName(beanDefinition, beanFactory);
                            // 注册beanDefinition
                            beanFactory.registerBeanDefinition(name, beanDefinition);
                        }
                    }
                }
            }
        } catch (Exception e) {
            System.out.println("error...");
        }
    }
}

将上述后处理器注册进容器中

context.registerBean(ComponentScanPostProcessor.class);

?达到了和原来一样的效果~?

模拟实现解析@Bean注解

public class MyBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @SneakyThrows
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }

    @SneakyThrows
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
        // 读取Config类元数据信息
        CachingMetadataReaderFactory readerFactory = new CachingMetadataReaderFactory();
        MetadataReader reader = readerFactory.getMetadataReader(new ClassPathResource("com/shuttle/spring5/Config.class"));
        // 找到标有@Bean注解的方法
        Set<MethodMetadata> methodMetadata = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
        for (MethodMetadata method : methodMetadata) {
            // 获取Bean属性 initMethod
            String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
            builder.setFactoryMethodOnBean(method.getMethodName(), "config");
            // 对于构造方法的参数以及工厂方法的参数自动注入模式统一设置
            builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
            // 如果initMethod不为空则设置
            if (initMethod.length() > 0) {
                builder.setInitMethodName(initMethod);
            }
            AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
            // 注册bean
            beanFactory.registerBeanDefinition(method.getMethodName(), beanDefinition);
        }
    }
}

模拟解析Mapper接口?

public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @SneakyThrows
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
        // 查找路径资源
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource[] resources = resolver.getResources("classpath:com/shuttle/spring5/mapper/**/*.class");
        AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
        CachingMetadataReaderFactory readerFactory = new CachingMetadataReaderFactory();
        for (Resource resource : resources) {
            // 获取类元信息
            MetadataReader reader = readerFactory.getMetadataReader(resource);
            ClassMetadata classMetadata = reader.getClassMetadata();
            // 若类为接口
            if (classMetadata.isInterface()) {
                // 构建beanDefinition
                AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class)
                        // 添加构造器参数值
                        .addConstructorArgValue(classMetadata.getClassName())
                        // 设置自动注入模式,按照类型注入
                        .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
                        .getBeanDefinition();
                // 为了生成bean的名称,默认为mapperFactoryBean,会造成覆盖
                AbstractBeanDefinition beanDefinitionForName = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();
                String name = generator.generateBeanName(beanDefinitionForName, beanFactory);
                beanFactory.registerBeanDefinition(name, beanDefinition);
            }
        }
    }

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

    }
}

将上述后处理器全部注册到容器中可以达到和Spring一样的效果。?

context.registerBean(ComponentScanPostProcessor.class);
context.registerBean(MyBeanPostProcessor.class);
context.registerBean(MapperPostProcessor.class);

config
com.shuttle.spring5.processor.ComponentScanPostProcessor
com.shuttle.spring5.processor.MyBeanPostProcessor
com.shuttle.spring5.processor.MapperPostProcessor
bean01
bean02
bean03
sqlSessionFactoryBean
dataSource
mapper01
mapper02

到这里就算是大功告成了~

?

?

写在最后

学到这里我真的想给满老师点个大大的赞,不同于其它Spring源码课程,满老师采用的是模拟实现的方式一步步引领我们体会后处理器的作用,对新手小白也十分友好,有兴趣的朋友可以课后手动实现下对于其它常见注解的解析。

下次见咯~

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-10-31 11:40:47  更:2022-10-31 11:45:45 
 
开发: 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年11日历 -2024/11/23 4:31:13-

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