Spring中那些BeanFactoryPostProcessors详解(一)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: Spring中那些BeanFactoryPostProcessors详解(一)

关联博文:

Spring中那些BeanFactoryPostProcessors详解(一)

Spring中那些BeanFactoryPostProcessors详解(二)


本文是对Spring中refresh分析之invokeBeanFactoryPostProcessors方法详解的一个补充,我们详细说明一下那些BeanFactoryPostProcessor在invokeBeanFactoryPostProcessors方法中的作用。


【1】CachingMetadataReaderFactoryPostProcessor


CachingMetadataReaderFactoryPostProcessor是SharedMetadataReaderFactoryContextInitializer的静态内部类实现了BeanDefinitionRegistryPostProcessor 和 PriorityOrdered接口。


主要用来注册CachingMetadataReaderFactory这个BeanDefinition并配置ConfigurationClassPostProcessor。如下所示其postProcessBeanFactory方法为空,我们主要看其postProcessBeanDefinitionRegistry方法。register方法会注册SharedMetadataReaderFactoryBean这个一个BeanDefinition,beanName如下所示:

org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory

79212540ebdd42a39af5cbb87b38e7f4.png

ConfigurationWarningsPostProcessor是ConfigurationWarningsApplicationContextInitializer的静态内部类,实现了PriorityOrdered 和 BeanDefinitionRegistryPostProcessor接口主要用来打印一些警示信息,我们对这个不用过多关注。

【2】ConfigurationClassPostProcessor


这个重量级的处理器我们在SpringBoot自动配置原理解析系列分析中详细描述过其postProcessBeanDefinitionRegistry方法。而其postProcessBeanFactory呢则是对候选配置类做了增强并注册了ImportAwareBeanPostProcessor这样一个BeanPostProcessor用来对ImportAware类型做支持。


关于ConfigurationClassPostProcessor本文我们这里不做进一步分析,梳理其作用如下:


对于候选配置类使用CGLIB Enhancer增强

解析处理@PropertySource 注解

解析@ComponentScan注解,扫描@Configuration、@Service、@Controller、@Repository和@Component注解并注册BeanDefinition

解析@Import注解,然后进行实例化,并执行ImportBeanDefinitionRegistrar的registerBeanDefinitions逻辑,或者ImportSelector的selectImports逻辑或者将候选类作为配置类触发配置类解析过程

解析@ImportResource注解,并加载相关配置信息

解析方法级别@Bean注解并将返回值注册成BeanDefinition

注册ImportAwareBeanPostProcessor到容器中,用于处理ImportAware

【3】MapperScannerConfigurer


是的,这个我们在SSM年代很熟悉的扫描配置器也是一个BeanDefinitionRegistryPostProcessor。其是在什么时候注册BeanDefinition呢?是在ConfigurationClassPostProcessor的parse方法中。


如下所示@MapperScan注解导入了MapperScannerRegistrar。

a5b079fd73da4a609a34084a535118c6.png

这个MapperScannerRegistrar会做什么呢?其会注册一个MapperScannerConfigurer这样的BeanDefinition到BeanFactory中。如下图所示:


我们继续看MapperScannerConfigurer。其postProcessBeanFactory方法是个空方法,我们主要看其postProcessBeanDefinitionRegistry方法。简单来讲其会实例化一个扫描器ClassPathMapperScanner并触发对basePackage的扫描。

@Override
 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
   if (this.processPropertyPlaceHolders) {
     processPropertyPlaceHolders();
   }
  //实例化并进行配置
   ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
   scanner.setAddToConfig(this.addToConfig);
   scanner.setAnnotationClass(this.annotationClass);
   scanner.setMarkerInterface(this.markerInterface);
   scanner.setSqlSessionFactory(this.sqlSessionFactory);
   scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
   scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
   scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
   scanner.setResourceLoader(this.applicationContext);
   scanner.setBeanNameGenerator(this.nameGenerator);
   scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
   if (StringUtils.hasText(lazyInitialization)) {
     scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
   }
   //注册过滤器
   scanner.registerFilters();
   //触发基础包的扫描
   scanner.scan(
       StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
 }

ClassPathMapperScanner 继承自ClassPathBeanDefinitionScanner,其scan方法如下所示。

// ClassPathBeanDefinitionScanner
public int scan(String... basePackages) {
  int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
//实际扫描方法,触发子类ClassPathMapperScanner 的方法
  doScan(basePackages);
  // Register annotation config processors, if necessary.
//默认为true,在实例化AnnotatedBeanDefinitionReader时我们触发过这个registerAnnotationConfigProcessors
//这里可以理解为一个补偿机制,本文这个方法没有任何作用
  if (this.includeAnnotationConfig) {
    AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
  }
//返回本次扫描注册的数量
  return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}

子类ClassPathMapperScanner 的doScan方法又调用了父类ClassPathMapperScanner 的doScan方法然后对beanDefinitions 做了后置处理包装为MapperFactoryBean。

 @Override
 public Set<BeanDefinitionHolder> doScan(String... basePackages) {
   Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
   if (beanDefinitions.isEmpty()) {
     LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
         + "' package. Please check your configuration.");
   } else {
   //这里会包装为MapperFactoryBean
     processBeanDefinitions(beanDefinitions);
   }
   return beanDefinitions;
 }

父类ClassPathMapperScanner 的doScan方法我们在SpringBoot自动配置原理解析(五)中分析@ComponentScan注解解析过程中详细说明过,这里不再赘述。我们接下来看一下processBeanDefinitions方法。

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
   GenericBeanDefinition definition;
   //遍历beanDefinitions
   for (BeanDefinitionHolder holder : beanDefinitions) {
     definition = (GenericBeanDefinition) holder.getBeanDefinition();
     //比如com.recommend.mapper.SysAdviceMapper
     String beanClassName = definition.getBeanClassName();
     LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
         + "' mapperInterface");
     // the mapper interface is the original class of the bean
     // but, the actual class of the bean is MapperFactoryBean
     //作为MapperFactoryBean的Class<T> mapperInterface成员
     definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
     //设置BeanClass为MapperFactoryBean,其是一个FactoryBean哦
     definition.setBeanClass(this.mapperFactoryBeanClass);
  //添加属性-值
     definition.getPropertyValues().add("addToConfig", this.addToConfig);
     boolean explicitFactoryUsed = false;
     //尝试处理sqlSessionFactory属性值,本文这里sqlSessionFactoryBeanName为null
     if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
       definition.getPropertyValues().add("sqlSessionFactory",
           new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
       explicitFactoryUsed = true;
     } else if (this.sqlSessionFactory != null) {
       definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
       explicitFactoryUsed = true;
     }
 //尝试处理sqlSessionTemplate属性值,本文这里sqlSessionTemplateBeanName为null
     if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
       if (explicitFactoryUsed) {
         LOGGER.warn(
             () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
       }
       definition.getPropertyValues().add("sqlSessionTemplate",
           new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
       explicitFactoryUsed = true;
     } else if (this.sqlSessionTemplate != null) {
       if (explicitFactoryUsed) {
         LOGGER.warn(
             () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
       }
       definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
       explicitFactoryUsed = true;
     }
//设置自动装配类型  AUTOWIRE_BY_TYPE=2
     if (!explicitFactoryUsed) {
       LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
       definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
     }
     // lazyInitialization默认为false
     definition.setLazyInit(lazyInitialization);
   }
 }


可以看到在这里为BeanDefinition设置了BeanClass为MapperFactoryBean,其是一个MapperFactoryBean,我们后面再实例化我们自己的XXXMapper时就会触发其getObject方法。而我们自己的原始接口类型比如com.recommend.mapper.SysAdviceMapper则作为MapperFactoryBean构造方法的参数。


// MapperFactoryBean
public MapperFactoryBean(Class<T> mapperInterface) {
   this.mapperInterface = mapperInterface;
 }
@Override
public T getObject() throws Exception {
  return getSqlSession().getMapper(this.mapperInterface);
}


【4】PropertySourceOrderingPostProcessor

其是ConfigFileApplicationListener的静态内部类,实现了BeanFactoryPostProcessor,和Ordered接口。其postProcessBeanFactory方法主要用来将我们的defaultProperties-PropertySource排到最后面,如果存在的话。

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
  reorderSources(this.context.getEnvironment());
}
private void reorderSources(ConfigurableEnvironment environment) {
  PropertySource<?> defaultProperties = environment.getPropertySources().remove(DEFAULT_PROPERTIES);
  // 本文这里为null
  if (defaultProperties != null) {
    environment.getPropertySources().addLast(defaultProperties);
  }
}


目录
相关文章
|
6月前
|
Java Spring
Spring中那些BeanFactoryPostProcessors详解(二)
Spring中那些BeanFactoryPostProcessors详解(二)
42 0
|
2月前
|
SQL 监控 druid
springboot-druid数据源的配置方式及配置后台监控-自定义和导入stater(推荐-简单方便使用)两种方式配置druid数据源
这篇文章介绍了如何在Spring Boot项目中配置和监控Druid数据源,包括自定义配置和使用Spring Boot Starter两种方法。
|
1月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
163 2
|
3月前
|
缓存 Java Maven
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
|
10天前
|
缓存 IDE Java
SpringBoot入门(7)- 配置热部署devtools工具
SpringBoot入门(7)- 配置热部署devtools工具
22 2
 SpringBoot入门(7)- 配置热部署devtools工具
|
6天前
|
存储 运维 安全
Spring运维之boot项目多环境(yaml 多文件 proerties)及分组管理与开发控制
通过以上措施,可以保证Spring Boot项目的配置管理在专业水准上,并且易于维护和管理,符合搜索引擎收录标准。
17 2
|
4月前
|
Java 测试技术 数据库
Spring Boot中的项目属性配置
本节课主要讲解了 Spring Boot 中如何在业务代码中读取相关配置,包括单一配置和多个配置项,在微服务中,这种情况非常常见,往往会有很多其他微服务需要调用,所以封装一个配置类来接收这些配置是个很好的处理方式。除此之外,例如数据库相关的连接参数等等,也可以放到一个配置类中,其他遇到类似的场景,都可以这么处理。最后介绍了开发环境和生产环境配置的快速切换方式,省去了项目部署时,诸多配置信息的修改。
|
1月前
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
54 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
|
1月前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
168 2
|
1月前
|
数据采集 监控 Java
SpringBoot日志全方位超详细手把手教程,零基础可学习 日志如何配置及SLF4J的使用......
本文是关于SpringBoot日志的详细教程,涵盖日志的定义、用途、SLF4J框架的使用、日志级别、持久化、文件分割及格式配置等内容。
132 0
SpringBoot日志全方位超详细手把手教程,零基础可学习 日志如何配置及SLF4J的使用......