上篇笔记主要记录了BeanDefinitionRegistryPostProcessor扩展机制的实现原理,这篇笔记注意记录下,自己在学习源码的过程中,看到的该扩展机制使用的地方

其实我上篇笔记有说过,这个扩展机制,太靠前,如果我们通过@Component注解去注入到spring容器中,那我们自定义的实现类是在所有的业务bean还没有放到spring容器中的时候,就执行了,此时一般也很少有业务需求是在这个时候对beanDefinition进行一些修改,但是,在底层框架中,有可能会用到这个扩展点,我看到的是mybatis底层有用到这个扩展点

mybatis是干什么的

其实mybatis就是一个持久层框架,在和spring整合的时候,简单来说,我觉得就做了一件事情,将我们程序员自己写的mapper接口交给spring去管理
但是需要想一下,一个接口,怎么交给spring去管理?这里面的一个设计思想,我觉得是值得学习的:对于spring来说,不会为了去整合mybatis而去修改自己的代码,那原生的mybatis会修改吗?当然也不会,因为mybatis如果只是为了整合spring,修改了自己的代码,那mybatis就不是mybatis了,那怎么办呢?两者想要结合,总要有一个中间粘合剂,那这时候,spring就说了,你既然想和我进行整合,那你自己去扩展一个jar包,专门负责和我spring整合用,你可以利用我暴露给你的扩展点,总之,我不管你怎么做,你只要把你的接口放到我的beanDefinitionMap中,就可以了,我负责去初始化,然后放到容器中,至于你怎么把mapper接口放到我的beanDefinitionMap,我不管
那这时候,mybatis就自己去实现类一个jar包:mybatis-spring-1.3.2.jar
也就是在这个jar包中,mybatis利用了spring的BeanDefinitionRegistryPostProcessor这个扩展点,去把我们程序员提供的mapper接口,放入到了beanDefinitionMap中

mybatis整合spring原理

我们在使用mybatis的时候,需要加一个注解,@MapperScan,在该注解中,指定要扫描的路径,这个路径是给谁用的?就是给我们这篇笔记要学习的BeanDefinitionRegistryPostProcessor的实现类MapperScannerConfigurer

@MapperScan

在该注解中,通过@Import引入了一个类MapperScannerRegistrar,

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware

这个类是ImportBeanDefinitionRegistrar的实现类,这也是spring扩展点之一,我们这里不做深入说明,我们可以简单理解为,spring在初始化的过程中,会调用该接口实现类的方法
在mybatis-spring-1.3.x.jar包中,直接在MapperScannerRegistrar 的registerBeanDefinitions()方法中,进行了扫描,去扫描我们自己声明的mapper接口
但是在mybatis-spring-2.x.jar包中,在MapperScannerRegistrar 的registerBeanDefinitions()方法中,注册到beanDefinitionMap中一个BeanDefinitionRegistryPostProcessor的实现类:MapperScannerConfigurer

所以,在1.x版本中,进行扫描的时候,使用的是ImportBeanDefinitionRegistrar这个扩展点,但是在2.x的包中,使用的是BeanDefinitionRegistryPostProcessor的扩展点

我们来看下2.X版本中是如何进行mapper扫描的

MapperScannerConfigurer

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {if (this.processPropertyPlaceHolders) {this.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.registerFilters();scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));}

可以看到,在其方法中,会自己声明一个scanner,然后去调用scan方法扫描,截止到这里,我们可以结束了,已经看到了,在mybatis-spring-2.x.jar中,就是利用了BeanDefinitionRegistryPostProcessor的机制,去进行mapper的扫描

扫描之后的后续处理

由于这里的ClassPathMapperScanner 是mybatis自己的,所以在调用scan方法的时候,会调用到自己的scan方法

public Set<BeanDefinitionHolder> doScan(String... basePackages) {Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);if (beanDefinitions.isEmpty()) {this.logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");} else {this.processBeanDefinitions(beanDefinitions);}return beanDefinitions;}

这里的扫描方法,其实和spring自己的扫描bean是几乎上一致的,只是在扫描出来包下对应的class文件之后,判断条件不一样,mybatis会判断class是否是接口等等

在扫描了之后,有一个很重要的操作:processBeanDefinitions()

修改beanDefinition的属性

我们需要考虑下,为什么在将接口扫描出来之后,要修改对应的beanDefinition属性?
首选我们来说processBeanDefinitions()方法中,做了什么修改

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {GenericBeanDefinition definition;for (BeanDefinitionHolder holder : beanDefinitions) {definition = (GenericBeanDefinition) holder.getBeanDefinition();if (logger.isDebugEnabled()) {logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + definition.getBeanClassName() + "' mapperInterface");}// 1.添加一个构造函数// 2.将beanDefinition的beanClass设置为mapperFactoryBeandefinition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59definition.setBeanClass(this.mapperFactoryBean.getClass());definition.getPropertyValues().add("addToConfig", this.addToConfig);boolean explicitFactoryUsed = false;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;}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;}if (!explicitFactoryUsed) {if (logger.isDebugEnabled()) {logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");}// 3.将beanDefinition的自动注入属性设置为byTypedefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);}}
}

这里我们可以认为有两个关键点:
1.将beanDefinition的beanClass设置为mapperFactoryBean
2.将beanDefinition的autowireMode设置为byType

我们考虑第一点,为什么将beanClass设置为mapperFactoryBean?
可以想下,一个接口,我们怎么将其注入到service?接口是没有业务逻辑的,所以只能是注入一个动态代理对象,所以这里的mapperFactoryBean是为了在会将mapper接口注入到service的时候,生成一个mapper接口的代理对象,注入到service,这里和mapperFactoryBean的具体逻辑有关系,不做深入说明

接着考虑第二点:为什么将自动注入模型设置为byType,需要注意,这里的byType和@Autowired的byType是完全不同的两个概念,我之前博客中有说明过,如果自动注入模型是byType,那我们在注入的时候,就不需要在属性上添加任何注解,只需要提供对应的set方法,并且set方法入参是我们要注入的类型即可
这也就是为什么要设置为byType的原因,我们想下,如果不设置为byType,那在mapper接口中注入sqlSessionFactory或者是sqlSessionTemplate的时候,还需要提供@Autowired注解,那这不就又和spring强耦合到一起了吗?所以将autoWireMode设置为byType,就不需要和spring强耦合

总结

所以,整个笔记写下来,我们也知道了,spring和mybatis在整合的时候,不仅仅用到了factoryBean、自动注入模型这个扩展点,在进行mapper接口扫描的时候,也用到了BeanDefinitionRegistryPostProcessor这个扩展机制

spring扩展点一:BeanDefinitionRegistryPostProcessor在框架中的应用相关推荐

  1. 非常有必要了解的Springboot启动扩展点

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 来源:r6d.cn/r4P7 背景 Spring的核心思想 ...

  2. SSM框架中MVC各层的作用以及运行流程

    这篇博文主要介绍的是SSM(Spring.SpringMVC.Mybatis)框架中,MVC各层的作用以及各层之间的交互和框架整体运行流程. 一.MVC各层级间的作用及关系 表现层(springMVC ...

  3. Struts2工作原理和框架扩展点

    http://www.cnblogs.com/winstonyan/archive/2011/11/13/struts2_flow_and_extends.html 框架主要涉及技术:Spring + ...

  4. 三万字盘点Spring/Boot的那些常用扩展点

    Spring对于每个Java后端程序员来说肯定不陌生,日常开发和面试必备的.本文就来盘点Spring/SpringBoot常见的扩展点,同时也来看看常见的开源框架是如何基于这些扩展点跟Spring/S ...

  5. 盘点Spring/Boot的那些常用扩展点

    Spring对于每个Java后端程序员来说肯定不陌生,日常开发和面试必备的.本文就来盘点Spring/SpringBoot常见的扩展点,同时也来看看常见的开源框架是如何基于这些扩展点跟Spring/S ...

  6. ASP.NET MVC中你必须知道的13个扩展点

         ScottGu在其最新的博文中推荐了Simone Chiaretta的文章13 ASP.NET MVC extensibility points you have to know,该文章为我 ...

  7. SpringBoot 框架中 使用Spring Aop 、创建注解、创建枚举类 使用过程记录

    1.开始 在Springboot框架中引入AOP <dependency><groupId>org.springframework.boot</groupId>&l ...

  8. Spring框架中的设计模式(一)

    设计模式有助于遵循良好的编程实践.作为最流行的Web框架之一的Spring框架也使用其中的一些. 本文将介绍Spring Framework中使用的设计模式.这是5篇专题文章的第一部分.这次我们将发现 ...

  9. SSM框架中使用Spring的@Transactional注解进行事务管理

    一 介绍 在企业级应用中,保护数据的完整性是非常重要的一件事.因此不管应用的性能是多么的高.界面是多么的好看,如果在转账的过程中出现了意外导致用户的账号金额发生错误,那么这样的应用程序也是不可接受的 ...

  10. 在Spring 框架中如何更有效的使用JDBC?

    使用Spring JDBC 框架,资源管理以及错误处理的代价都会减轻.开发人员只需通过statements 和queries 语句从数据库中存取数据.Spring 框架中通过使用模板类能更有效的使用J ...

最新文章

  1. SLAM学习--帝国理工学院机器人课程slam部分讲义(附下载链接)
  2. 变频器端子阻抗3k_PLC与变频器连接问题分析
  3. 6.Vue Class 与 Style 绑定
  4. 机器学习基石HOW部分(2)
  5. 计算机刚过国家线能调剂到哪些学校,2020考研:刚过国家线好不好调剂?这4个调剂策略!考生要知道...
  6. 学习Python技术,怎么才能更好找到工作
  7. MATLAB字符数组和空数组
  8. 不小心运行了一个***程序~word 2003 打开2007格式文件的转换器.rar 里的
  9. NeurIPS、COLING顶会亮点有哪些 | 一周学术精选
  10. Qt 3D Opengl学习
  11. 工业大数据漫谈5:工业大数据案例(上)
  12. sql2000安装失败的解决方法
  13. vue2.0模板的三种写法
  14. uni-app - 设置最外层容器高度为100%
  15. 2-灵魂存在与否的论证(1)(耶鲁大学公开课-哲学-死亡)
  16. 台大机器学习基石上_lesson 2
  17. 4110:圣诞老人的礼物-Santa Clau’s Gifts(java)
  18. Python调试器-Pdb的简介及调试命令
  19. shiro整合SSM使用ehcache出现的net.sf.ehcache.CacheException异常解决办法
  20. R语言时间序列数据的合并(merge time series):使用merge函数合并时间序列数据、使用zoo包中的na.spline函数使用三次样条方法(cubic spline)填充时间序列缺失值

热门文章

  1. 贝叶斯概率推断(一):贝叶斯思维
  2. 寻找两个正序数组中的中位数 数组
  3. 410.分割数组的最大值
  4. 随机森林进行特征选择
  5. 决策树C4.5算法的不足
  6. 把数字翻译成字符串的方法数
  7. 继承、关联、聚合、组合的代码表示
  8. “按字典序输出方案” 解决方法
  9. sys.argv学习,通过运行py脚本,手动传入参数
  10. 官服好吗_【知否的唯美手绘图】看到齐衡身穿“官服”时,网友:好帅啊!