Mybatis-Spring源码分析图解
Mybatis-Spring
当我们使用mybatis和spring整合后为什么下面的代码可以运行?
文章图片
文章图片
一个问题:
我就写了个mapper接口为什么能用?
首先来看,在spring的配置xml中有一段
这段xml的作用是将一个类添加到spring容器中,点进这个类看看
文章图片
它实现了一个
BeanDefinitionRegistryPostProcessor
接口,关于这个接口的作用和执行时机上篇博客写过了,这里就不再赘述那么它必然实现
postProcessBeanDefinitionRegistry
方法,点击这个方法查看@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {if (this.processPropertyPlaceHolders) {processPropertyPlaceHolders(); }ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); ..........scanner.registerFilters(); scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS)); }
其中将接口注册到spring容器中在最后一行,先来看
ClassPathMapperScanner
这个类,它继承了ClassPathBeanDefinitionScanner
这个扫描器文章图片
scan的具体代码
public int scan(String... basePackages) {int beanCountAtScanStart = this.registry.getBeanDefinitionCount(); doScan(basePackages); // Register annotation config processors, if necessary.if (this.includeAnnotationConfig) {AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); }return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart); }
这个是spring内部的扫描方法,当它走到doScan的时候,因为ClassPathMapperScanner这个类重写了doScan方法,所以会调用子类重写的方法
@Overridepublic SetdoScan(String... basePackages) {Set beanDefinitions = super.doScan(basePackages); if (beanDefinitions.isEmpty()) {LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)+ "' package. Please check your configuration."); } else {processBeanDefinitions(beanDefinitions); }return beanDefinitions; }
通过包名获取BeanDefinitionHolder,现在它获取到了User接口的BeanDefinitionHolder,然后判断如果BeanDefinitionHolder的集合为空,也就是没有找到mapper的情况则不做任何处理,而现在有一个UserMapper的,进入else
private void processBeanDefinitions(SetbeanDefinitions) {AbstractBeanDefinition definition; BeanDefinitionRegistry registry = getRegistry(); for (BeanDefinitionHolder holder : beanDefinitions) {definition = (AbstractBeanDefinition) holder.getBeanDefinition(); .........//主要看这行definition.setBeanClass(this.mapperFactoryBeanClass); .........if (!definition.isSingleton()) {BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true); if (registry.containsBeanDefinition(proxyHolder.getBeanName())) {registry.removeBeanDefinition(proxyHolder.getBeanName()); }registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition()); }}}
将MapperFactoryBean类设置为了UserMapperBeanDefinition的class
文章图片
spring在创建这个userMapper这个Bean的时候会使用这个有参构造将当前这个UserMapper类型设置到mapperInterface属性上(为啥使用有参构造而不是无参来初始化对象我也不知道.....这和spring推断构造方法有关,以后学会了在来写)
这个MapperFactoryBean实现了一个
FactoryBean
接口,这个接口可以让我们自定义获取bean的操作回到spring的代码,例如当我们使用context.getBean(xxx.class)的时候
文章图片
spring将xxx.class类型解析为bean名称,通过名称去获取
protectedT doGetBean(String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly)throws BeansException {//获取对应的beanNameString beanName = transformedBeanName(name); Object bean; Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) {bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } .......// Create bean instance.if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {try {//真正创建对象的地方return createBean(beanName, mbd, args); }catch (BeansException ex) {// Explicitly remove instance from singleton cache: It might have been put there// eagerly by the creation process, to allow for circular reference resolution.// Also remove any beans that received a temporary reference to the bean.destroySingleton(beanName); throw ex; }}); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }}
首先是调用
getSingleton
方法,尝试获取存在缓存中的bean(其实就是三个Map,key为bean名称,value是对象),那现在是首次获取map中没有然后执行到下面的createBean,当创建完这个bean后spring需要判断这个bean是一个普通bean还是一个FactoryBean,程序员是想要获取普通bean还是FactoryBean,还是FactoryBean的getObject方法返回的从工厂生成的对象
咱们一段一段看
protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {if (BeanFactoryUtils.isFactoryDereference(name)) {if (beanInstance instanceof NullBean) {return beanInstance; }if (!(beanInstance instanceof FactoryBean)) {throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass()); }} .....}
BeanFactoryUtils.isFactoryDereference(name)
的作用是一个字符串判断,当返回传入名称是否为工厂,如果name不为空,并且以&开头返回true这个方法在下面的判断也使用到了,记一下它的作用即可
来看例子
文章图片
文章图片
在我们使用FactoryBean通过context.getBean("工厂Bean名称")的时候获取的是FactoryBean的getObject生成的对象,如果我们想获取FactoryBean的引用则需要在名称前面加一个
&
符号文章图片
回来看代码,如果这个bean的引用是一个NullBean类型则直接返回引用,下面有做了一个判断
if (!(beanInstance instanceof FactoryBean))
再次判断这个bean是不是一个FactoryBean,如果为true则抛出异常,这个好理解,因为我们在getBean的时候完全可以将一个普通的bean名称前面加上&符号主要的判断在下面的这个if
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {return beanInstance; }
现在有3中情况
1.当前的bean是一个普通的bean
第一个条件false 取反 true 第二个条件false 结果true,直接返回bean实例
2.当前是一个FactoryBean,想通过工厂获取Bean
第一个条件 true 取反false 第二个条件false 结果false,进行下面的操作
3.当前是一个FactoryBean,想获取工厂的引用
第一个条件 true 取反 false 第二个条件 true 结果 true 直接返回factoryBean实例
当前我们是想通过FactoryBean获取对象,那么不进if,继续下面的代码
Object object = null; // 如果beanDefinition为null,则尝试从缓存中获取给定的FactoryBean公开的对象if (mbd == null) {//尝试从缓存中加载beanobject = getCachedObjectForFactoryBean(beanName); }// 未能从缓存中获得FactoryBean公开的对象,则说明该bean是一个新创建的beanif (object == null) {FactoryBean> factory = (FactoryBean>) beanInstance; if (mbd == null && containsBeanDefinition(beanName)) {mbd = getMergedLocalBeanDefinition(beanName); }boolean synthetic = (mbd != null && mbd.isSynthetic()); // 从给定的FactoryBean中获取指定的beanName对象object = getObjectFromFactoryBean(factory, beanName, !synthetic); }return object;
主要来看
getObjectFromFactoryBean
protected Object getObjectFromFactoryBean(FactoryBean> factory, String beanName, boolean shouldPostProcess) {if (factory.isSingleton() && containsSingleton(beanName)) {synchronized (getSingletonMutex()) {Object object = this.factoryBeanObjectCache.get(beanName); if (object == null) {//调用factoryBean的getObject方法object = doGetObjectFromFactoryBean(factory, beanName); Object alreadyThere = this.factoryBeanObjectCache.get(beanName); if (alreadyThere != null) {object = alreadyThere; }}..........}}}
doGetObjectFromFactoryBean
方法private Object doGetObjectFromFactoryBean(FactoryBean> factory, String beanName) throws BeanCreationException {Object object; try {if (System.getSecurityManager() != null) {AccessControlContext acc = getAccessControlContext(); try {object = AccessController.doPrivileged((PrivilegedExceptionAction
也就是说当我们getBean("userMapper")的时候其实是调用FactoryBean的getObject方法,代码回到mybatis-spring项目的MapperFactoryBean类中的getObject方法
@Overridepublic T getObject() throws Exception {return getSqlSession().getMapper(this.mapperInterface); }@OverridepublicT getMapper(Class type) {return configuration.getMapper(type, this); }public T getMapper(Class type, SqlSession sqlSession) {final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory ) knownMappers.get(type); if (mapperProxyFactory == null) {throw new BindingException("Type " + type + " is not known to the MapperRegistry."); }try {return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) {throw new BindingException("Error getting mapper instance. Cause: " + e, e); }}public T newInstance(SqlSession sqlSession) {final MapperProxy mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }protected T newInstance(MapperProxy mapperProxy) {return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); }
到最后发现是通过jdk的动态代理来生成的对象,那么回答开始的问题
我就写了个接口为什么能用?
因为mybatis在spring加载bean之前修改了beanDefinition,通过MapperScannerConfigurer类实现的BeanDefinitionRegistryPostProcessor接口中将我们定义的一些mapper接口的BeanDefinition的BeanClass属性修改为了MapperFactoryBean,而这个类实现了FactoryBean,我们获取接口实际上是通过FactoryBean的getObject方法
【Mybatis-Spring源码分析图解】到此这篇关于Mybatis-Spring源码分析的文章就介绍到这了,更多相关Mybatis-Spring源码分析内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
推荐阅读
- web|[RoarCTF 2019]Easy Calc_WP
- 号称国内著名的免费精品源码分享社区莎莎源码论坛疑似倒闭了
- 分享源码学习技术|亲测-分享最新微信付费进群收费进群系统源码-附带搭建教
- 代码审计|[代码审计]ThinkPHP 5.0.x 变量覆盖导致的RCE分析
- 代码审计|[代码审计]yii2 反序列化漏洞分析
- big|2022-2028年中国二氯甲烷行业市场竞争状况及发展趋向分析报告
- 分析服务实时概览数据助力开发者数据运营
- 中间件|2021年中国软件产业发展现状分析(软件业务收入达94994亿元,同比增长16.43%[图])
- JDK|阅读 JDK 源码(HashMap 扩容总结及图解)
- 内存溢出+CPU占用过高:问题排查+解决方案+复盘(超详细分析教程)