mybatis扩展---Second

前言

劳歌一曲解行舟,红叶青山水急流。日暮酒醒人已远,满天风雨下西楼。— 许浑 《谢亭送别》


一、SSA

关于mybatis实现jdk动态代理,在接入spring的时候代码是如何处理的(下篇)


二、GTTP

1.看下基本类,模拟的mybatis扩展spring接口实现查询

  • 这里创建的CustomSqlsession customSqlsession = new CustomSqlsession();其实就是上一篇的创建动态代理的获取传入接口的代理对象的类
  • mapperInterface就是传入的接口,这里模拟的传入的就是IndexDao;
  • 这里提供了setMapperInterface是个set方法,意味着后面mybatis根据spring提供的接口规则将自己的代理对象注入spring的时候,spring通过这个set方法给mapperInterface设置值。
  • CustomFactoryBean这个类里也可以提供有参构造方法,spring根据有参构造填充mapperInterface这个属性。
package com.cusmybatis.example3.factorybean;

import com.cusmybatis.example3.sqlSession.CustomSqlsession;
import org.springframework.beans.factory.FactoryBean;

/**
 * @author xiansheng lv
 * @date 2021/6/15 11:20
 * 目的让mybati代理的这个对象注入spring.
 * 即此处 生成代理对象必须让这个CustomFactoryBean  的bean生效
 * 1, xml可以配置注册这个CustomFactoryBean类并且给他的属性指定需要代理的对象,
 * 但是这个做法很局限,代理的对象多个无法手动一一写出来
 * 2,注解的形式将需要被代理的对象注入CustomFactoryBean里
 * 这会导致CustomFactoryBean一并走spring实例化对象的周期,也就是出现代理两次,直接出错
 * 3,尝试添加这个代理对象的BeanDefintion  可行.
 *
 */
public class CustomFactoryBean implements FactoryBean {

	/*这个类是动态给的*/
	Class mapperInterface;

	@Override
	public Object getObject() {
		CustomSqlsession customSqlsession = new CustomSqlsession();
		/*代理类获取对象*/
		/*mabatis动态代理出来的类*/
		Object mapper = customSqlsession.getMapper(mapperInterface);
		/*此处实现FactoryBean返回的mapper对象会存到spring容器当中*/
		return mapper;
	}

	@Override
	public Class<?> getObjectType() {
		return mapperInterface;
	}

	/*提供一个set方法,属性注入的时候使用这个方法给
	* 相较于构造方法而写的这个
	* */
	public void setMapperInterface(Class mapperInterface) {
		this.mapperInterface = mapperInterface;
	}
}

  • 在spring实例化周期的过程中,这里实现了ImportBeanDefinitionRegistrar接口,来注入mybatis代理的对象
  • 很明显你要注入mybatis代理对象必须按照spring提供的规矩来。也就是上面的(implements FactoryBean)实现FactoryBean接口,用BeanDefinitionBuilder构建mybatis的beanDefinition模型,将构建好的模型注册(registry.registerBeanDefinition)进去。
  • 有一个大家很熟悉的后置处理器(implements BeanFactoryPostProcessor ),顾名思义,实现这个后置处理器可以根据实现类提供的beanFactory拿到你想要的beanDefinition,但这个是后置处理器,是已经走完注册BeanDefinition流程的接口,所以扩展这个接口只能获取不能在这里注册BeanDefinition;
  • beanDefinition也提供了相应的属性设置的方法可以是.getPropertyValues().add(“mapperInterface”, IndexDao.class)来设置
package com.cusmybatis.example3.bd;

import com.cusmybatis.example3.dao.IndexDao;
import com.cusmybatis.example3.factorybean.CustomFactoryBean;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.core.type.AnnotationMetadata;

/**
 * @author xiansheng lv
 * @date 2021/6/15 14:00
 * spring里这个注册beanDefinition的类生效不能是Component注解
 * 而是在配置类加上Import注解指定
 */
public class CustomImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
		MergedAnnotations annotations = importingClassMetadata.getAnnotations();
		/*
		 *真实的情况是根据这里的annotations注解扫描获取到所有的dao
		 * 循环遍历 给它做代理注册到spring里
		 * 这里只获取了一个IndexDao注册进去
		 * */
		/*构建传入class的beanDefinition信息*/
		BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(CustomFactoryBean.class);
		AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
		/*这里通过属性注入的方式 也可以是
		* beanDefinition.getConstructorArgumentValues().addArgumentValues();
		* 对应的CustomFactoryBean里也需要提供对应的有参构造
		* */
		beanDefinition.getPropertyValues().add("mapperInterface", IndexDao.class);

		/*注册beanDefinition*/
		registry.registerBeanDefinition("indexDao", beanDefinition);
	}
}

下面是模拟@MapperScan注解的自定义注解@CustomScan用到的所有的类。

在这里插入图片描述
可以看到成功从容器中获取到IndexDao,并用它的代理对象执行了数据库查询。

在这里插入图片描述

2.查看mybatis扩展spring源码

在这里插入图片描述
可以看到,用Mybatis扩展spring的注解MapperScan来查询数据库依然可以,且代理对象变成了org.apache.ibatis.binding.MapperProxy@7e5c856f。接下来就看下源代码中的处理过程是否和上面模拟的一样。


在这里插入图片描述

  • 可以看到@MapperScan(“com.cusmybatis.example3.dao”)中 @MapperScan注解上同样是@Import注解 导入了一个MapperScannerRegistrar.class类,这个类和笔者的@CustomScan注解导入的CustomImportBeanDefinitionRegistrar类一样实现了ImportBeanDefinitionRegistrar接口重写了registerBeanDefinitions方法。
    在这里插入图片描述
  • 可以看到这个MapperScannerRegistrar类的BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);这行逻辑代码第一行就构建了BeanDefinitionBuilder ,当然这里的MapperScannerConfigurer顾名思义扫描了所有的指定包下的dao;除了第一行下面的逻辑都是 做 builder.addPropertyValue(),这个和笔者的
    在这里插入图片描述
    这里的作用类似,是给BeanDefinition配置属性,这个属性是属于BeanDefinition的。最后一行 registry.registerBeanDefinition(beanName, builder.getBeanDefinition())将构建好的就是BeanDefinition注册到spring。接下来就重点看下MapperScannerConfigurer扫描干的事。

在这里插入图片描述

  • 可以看到在MapperScannerConfigurer类的扫描过程中,在指定的com.cusmybatis.example3.dao包下扫描出了IndexDao。理论上扫描出来后应该是给它的BeanDefinition填充属性,这个属性是代理类需要的属性。

在这里插入图片描述

  • 上图可以看到,扫描出来的beanDefinitions只有一个也就是MyBatis mapper 有一个IndexDao。但是在for (BeanDefinitionHolder holder : beanDefinitions) { 会循环遍历它,遍历的操作里有
    definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
    definition.setBeanClass(this.mapperFactoryBeanClass);
    两行代码,查看属性知道beanClassName的值就是com.cusmybatis.example3.dao.IndexDao,也就是definition将来要实例化出来的对象的Constructor的参数是com.cusmybatis.example3.dao.IndexDao,实例化出来的对象是mapperFactoryBeanClass,这个是mybaits的提供的类实现了implements FactoryBean接口
    在这里插入图片描述
    作用就和上图的意思一样。当然理解上面的过程前提是对spring的beanDefinition有一定的理解。

总结

总结就是:

  • mybaits自个儿单独使用是构建一个代理对象,需要查询数据库时将dao接口传入得到一个代理对象,这个代理对象的数据源事务等信息已经配置好了,可以直接调用dao接口里的方法查询得到数据,很方便。
  • 但是要接入spring,spring提供了扩展implements ImportBeanDefinitionRegistrar这个接口即可注册你自己的beanDefinition,重写方法registerBeanDefinitions,构建BeanDefinitionBuilder时传入的对象必须是实现了org.springframework.beans.factory.FactoryBean接口的,对mybatis而言传入的就是MapperFactoryBean。
  • 而这个对象有个有参构造,这个参就是用户传入发dao接口,MapperFactoryBean#getObject()方法返回对应的代理对象。
  • 构建出BeanDefinitionBuilder对象前必须扫描包,遍历有多少个MyBatis mapper (扫描出来的dao是通过ScannedGenericBeanDefinition#ScannedGenericBeanDefinition变成的ScannedGenericBeanDefinition–GenericBeanDefinition的子类具体是spring源码这里有兴趣可以看下具体的实现)。
  • 然后遍历每个beanDefinition,填充下未来根据beanDefinition代理出的bean对象的属性(BeanClassName,isAbstract,是否懒加载,是否单例,构造方法,构造方法的参数等),最后注册进去registry.registerBeanDefinition(beanName,beanDefinition)即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值