spring与mybatis如何无缝连接的

2023-10-02 15 0

1.Mybatis只提供了mapper接口,在spring中直接@Autowired这个mapper接口就可以用sqlsession方法了,本文就揭开面纱,并且自己实现。

2.详解实现

这个是mybatis的标准配置。MybatisMapperScanner是我自己模仿mybatis写的,自己实现有助于理解。sqlSessionFactory没有实现,也不是本文重点。

	<bean class="com.hadluo.photo.MybatisMapperScanner"><!-- 指定mapper接口包路径 --><property name="basePackage" value="com.hadluo.photo.mybatismapper" /><!-- 指定sessionFactory<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />--></bean>

主要是MybatisMapperScanner 起作用,下面看下它的实现

public class MybatisMapperScanner implements BeanDefinitionRegistryPostProcessor {private String basePackage;public String getBasePackage() {return basePackage;}public void setBasePackage(String basePackage) {this.basePackage = basePackage;}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {// TODO Auto-generated method stubMyBatisScanner scanner = new MyBatisScanner(registry);scanner.registerFilters();scanner.scan(basePackage);}
}

BeanDefinitionRegistryPostProcessor
这个是spring里面的类,专门用于动态注册Bean,提供了postProcessBeanDefinitionRegistry方法,用于注册bean信息。

spring加载我们配置的MybatisMapperScanner bean时会自动回调postProcessBeanDefinitionRegistry方法,下面主要看这个方法:
1.构建了MyBatisScanner类
继承了ClassPathBeanDefinitionScanner ,这个也是spring原生类,用于扫描某个包路径下的类,并注入bean。

public class MyBatisScanner extends ClassPathBeanDefinitionScanner {public MyBatisScanner(BeanDefinitionRegistry registry) {super(registry, false);}@Overrideprotected Set<BeanDefinitionHolder> doScan(String... basePackages) {// 借助spring原生扫描,得到 bean defineSet<BeanDefinitionHolder> mappers = super.doScan(basePackages);GenericBeanDefinition definition;for (BeanDefinitionHolder mapper : mappers) {//每个definition = (GenericBeanDefinition) mapper.getBeanDefinition();definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue// mapper接口的classClass<?> mapperClass = null;try {mapperClass = Class.forName(definition.getBeanClassName());} catch (ClassNotFoundException e) {e.printStackTrace();}// 这里很重要,偷偷转换了bean为我们的 一个FactoryBeandefinition.setBeanClass(MybatisFactoryBean.class);// 设置MybatisFactoryBean 的 类的属性mapperInterface为 mapperClassdefinition.getPropertyValues().add("mapperInterface", mapperClass);definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);}return mappers;}/**** 不重写这个方法   上面的super.doScan就不会成功。*/@Overrideprotected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {// 这个方法是 欺骗spring 能通过 扫描mapper接口产生bean definereturn true;}/**** 不重写这个方法   上面的super.doScan就不会成功。* 这个方法也是 欺骗spring 能通过 扫描mapper接口产生bean define* * @author HadLuo  2019年10月18日 新建*/public void registerFilters() {boolean acceptAllInterfaces = true;if (acceptAllInterfaces) {// default include filter that accepts all classesaddIncludeFilter(new TypeFilter() {@Overridepublic boolean match(MetadataReader metadataReader,MetadataReaderFactory metadataReaderFactory) throws IOException {return true;}});}// exclude package-info.javaaddExcludeFilter(new TypeFilter() {@Overridepublic boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)throws IOException {String className = metadataReader.getClassMetadata().getClassName();if (className.endsWith("package-info")) {return true;}return metadataReader.getAnnotationMetadata().hasAnnotation("tk.mybatis.mapper.annotation.RegisterMapper");}});}
}

上面代码注意以下几点:
1.registerFilters必须要在scan前调用。
2.isCandidateComponent必须重写,返回true,不然spring看如果你的mapper路径下类全是接口,不会通过注册bean。
3.在调用super.doScan后,是抱spring大腿,扫描得到bean define,由于我们的beanClassName还是一个接口,必须将其移花接木转成,一个FactoryBean。用于调用mybatis的代理对象。

下面重点落在了MybatisFactoryBean类这个 FactoryBean上。

public class MybatisFactoryBean<T> implements FactoryBean<T> {// 模拟mybatis 的 代理对象池static final Map<Class<?>, Object> mapperProxys = new HashMap<Class<?>, Object>();static {//模拟 动态生成类mapperProxys.put(MyDytt2019Mapper.class, new MyDytt2019MapperProxy());}//模拟的 动态类public static class MyDytt2019MapperProxy implements MyDytt2019Mapper {@Overridepublic void select() {System.err.println("调用了Mybatis代理对象的select方法");}}//真正的 mapper接口 classprivate Class<T> mapperInterface;public MybatisFactoryBean(Class<T> mapperInterface) {this.mapperInterface = mapperInterface;}@SuppressWarnings("unchecked")@Overridepublic T getObject() throws Exception {// spring @Autowired 时,实际 会回调这个方法,返回真正的实例return (T) mapperProxys.get(mapperInterface);}@Overridepublic Class<?> getObjectType() {// 上面 getObject 的 class 类型return mapperInterface;}@Overridepublic boolean isSingleton() {// 是单利return true;}public void setMapperInterface(Class<T> mapperInterface) {this.mapperInterface = mapperInterface;}public Class<T> getMapperInterface() {return mapperInterface;}
}

以上代码要注意以下几点:
1.上面没有动态生成类的代码,只是模拟。
2.spring FactoryBean 的知识,用于spring 注册了FactoryBean 后,实际 @Autowired 用的时候,会回调getObject 来获取真正实例。
3.通过这个MybatisFactoryBean ,我们就可以调用 动态代理对象 的方法来操作sqlSession了。

现在已经写完了,我们来测试下。
扫描包路径下 建立一个mapper接口,
在这里插入图片描述
在这里插入图片描述
测试类
在这里插入图片描述
启动执行
会调用我们模拟的 MybatisFactoryBean类 内部 类 MyDytt2019MapperProxy 的select方法。
在这里插入图片描述

好了就到这里了,以后就可以方方便便的利用spring写框架了。

老生常谈:深圳有爱好音乐的会打鼓(吉他,键盘,贝斯等)的程序员和其它职业可以一起交流加入我们乐队一起嗨。我的QQ:657455400 表演视频实例https://v.qq.com/x/page/f0517awx0x4.html

代码编程
赞赏

相关文章

数商云食品行业解决方案:新技术加持食品行业,为企业快速发展提供有力支撑
星期零斩获2021新消费领域“投资界50强企业”和“中国食品工业创新品牌”奖项
2022-2028年中国食品产业园区行业市场运行格局及发展策略分析报告
工业互联网业务知识
劲牌公司荣获2020年度“中国食品工业协会科学技术奖”特等奖
面试3_:不修改数组找出重复的数字