spring整合myabtis无非就是,把@Mapper修饰的接口扫描到容器中,然后spring中接口是不能呗扫描成为bean定义的,还有spring中connection与mybatis中connection的整合无非就是同一个线程spring生成的connection放到ThreadLocal中,mybatis用到时直接从中取保证了同一个业务方法中spring的连接与Mybatis时同一个。(此文只针对相应的@Mapper接口扫描beandefinition)

一、如何把@Mapper接口扫描到容器中

(1) spring中接口是不能扫描成bean定义的,而且bean定义的class肯定不能设置为接口类型,因接口不能实例化。spring提供了FactoryBean,因此我们可以在生成bean定义的时候指定beanClass为FactoryBean类型当getObject时根据当前接口生成代理对象
看下伪代码:

public class MybatisFactoryBean implements FactoryBean {private Class  classInterface;public MybatisFactoryBean(Class<?> classInterface) {this.classInterface = classInterface;}@Overridepublic Object getObject() throws Exception {return  生成相应代理对象;}@Overridepublic Class<?> getObjectType() {return classInterface;}
}///  扫描无非就是把没过bean定义的先把构造参数设置当前的接口的,再把beanClass设置FactoryBean类型因为需要实例化GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionHolder.getBeanDefinition();beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());beanDefinition.setBeanClassName(MybatisFactoryBean.class.getName());beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

生成代理对象mybatis中sqlSession.getMapper(classInterface);
所以FactoryBean需要有SqlSession属性

public class MybatisFactoryBean implements FactoryBean {private Class  classInterface;private DefaultSqlSession sqlSession;//设置by_type注入会扫描所有set方法进行注入不多做解释public void setSqlSession(SqlSessionFactory sqlSessionFactory) {this.sqlSession = (DefaultSqlSession)sqlSessionFactory.openSession();//此处必须把这个接口加入进来负责报错sqlSession.getConfiguration().addMapper(classInterface);}public MybatisFactoryBean(Class<?> classInterface) {this.classInterface = classInterface;}@Overridepublic Object getObject() throws Exception {return  sqlSession.getMapper(classInterface);}@Overridepublic Class<?> getObjectType() {return classInterface;}
}

因SqlSessionFactory 需要在容器中

(2)、SqlSessionFactory 需要交给spring容器

@ComponentScan("com.spring.demo.component")
@Configuration
@MapperScan("com.spring.demo.mybatis")
public class ApplicationConfig {@Beanpublic SqlSessionFactory sqlSessionFactory() throws IOException {InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);return sqlSessionFactory;}}

mybatis配置文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/test_db?useUnicode=true&amp;useJDBCCompliantTimezoneShift=true&amp;useLegacyDatetimeCode=false&amp;serverTimezone=UTC"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment></environments></configuration>

(3)、考虑如何把接口扫描成bean定义呢

先来了解下 spring中扫描bean定义:
1、org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan
2、org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#findCandidateComponents
3、org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {Set<BeanDefinition> candidates = new LinkedHashSet<>();try {String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +resolveBasePackage(basePackage) + '/' + this.resourcePattern;Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);boolean traceEnabled = logger.isTraceEnabled();boolean debugEnabled = logger.isDebugEnabled();for (Resource resource : resources) {if (traceEnabled) {logger.trace("Scanning " + resource);}try {MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);//1此处就是改造关键点if (isCandidateComponent(metadataReader)) {ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);sbd.setSource(resource);//2、此处会把接口类型过滤掉if (isCandidateComponent(sbd)) {if (debugEnabled) {logger.debug("Identified candidate component class: " + resource);}candidates.add(sbd);}}}return candidates;}protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {AnnotationMetadata metadata = beanDefinition.getMetadata();return (metadata.isIndependent() && (metadata.isConcrete() ||(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));}protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {for (TypeFilter tf : this.excludeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {return false;}}//此处扫描只会this.includeFilters.add(new AnnotationTypeFilter(Component.class));@Componet注解的beanfor (TypeFilter tf : this.includeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {return isConditionMatch(metadataReader);}}return false;}

因此我们自定义扫描器肯定需要把1、不是@Component注解修饰的也需要扫描进来,2、还要把接口类行不要过滤掉
为了方便自定义扫描器继承ClassPathBeanDefinitionScanner只需要把上面注释中的1、2两处重写即可

public class MybatisMapperScan extends ClassPathBeanDefinitionScanner {public MybatisMapperScan(BeanDefinitionRegistry registry) {super(registry);}@Overrideprotected Set<BeanDefinitionHolder> doScan(String... basePackages) {Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionHolder.getBeanDefinition();beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());beanDefinition.setBeanClassName(MybatisFactoryBean.class.getName());beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);}return beanDefinitionHolders;}@Override//把接口类型放开protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {AnnotationMetadata metadata = beanDefinition.getMetadata();return metadata.isInterface();}
}public class MybatisImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {Map<String,Object> mapScan=  importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName());String value= (String) mapScan.get("value");MybatisMapperScan mybatisMapperScan=new MybatisMapperScan(registry);//2、添加过滤条件此处把所有条件都放开,spring中此处之后把@Copmonet注解放开,此处后续可以改造只扫描@Mapper注解的放开mybatisMapperScan.addIncludeFilter(new TypeFilter(){@Overridepublic boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {return true;}});mybatisMapperScan.doScan(value);}
}

何时会触发这个扫描器呢?
ImportBeanDefinitionRegistrar接口Spring启动时候调用该接口的registerBeanDefinitions()因此我们只需在配置类上导入该类即可

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MybatisImportBeanDefinitionRegistrar.class)
public @interface MapperScan {String value() default "";
}@ComponentScan("com.spring.demo.component")
@Configuration
@MapperScan("com.spring.demo.mybatis")
public class ApplicationConfig {@Beanpublic SqlSessionFactory sqlSessionFactory() throws IOException {InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);return sqlSessionFactory;}}

一切准备就绪测试(注意我的TestMapper接口没有加@Mapper注解因为上面代码过滤中会把所有类都扫描到,此处可以扩展,即加上@Mapper,上面过滤条件只扫描带此注解的):

public interface TestMapper {@Select("select 'hello' from  dual ")String select();
}@Component
public class UserService {@Autowiredprivate TestMapper testMapper;public void hello(){System.out.println(testMapper.select());}}

二、代码demo路径
https://github.com/kangchangchang/SpringMybatis.git

手写Spring整合mybatis相关推荐

  1. spring整合mybatis(入门级简单教程1)--在spring中配置c3p0,并成功测试

    引子:spring整合mybatis.因为,我们看完(我就是这样的)spring和mybatis之后,本想自己写一个小小的项目,以便加深理解,但是我发现在spring中整合mybatis并不是一件容易 ...

  2. JAVA项目代码手写吗_一个老程序员是如何手写Spring MVC的

    见人爱的Spring已然不仅仅只是一个框架了.如今,Spring已然成为了一个生态.但深入了解Spring的却寥寥无几.这里,我带大家一起来看看,我是如何手写Spring的.我将结合对Spring十多 ...

  3. Spring整合Mybatis之注解方式,(注解整合Junit)

    Spring整合Mybatis之注解方式 我有一篇博客详细写了我自己使用xml的方法Spring整合MyBatis,现在我就把核心配置文件中的每个bean的配置使用注解的方式实现 注解整合MyBati ...

  4. Spring整合Mybatis之DAO层、Service层开发

    3. Spring整合Mybatis编程DAO层开发 1. 项目引入相关依赖spring mybatis mysql mybatis-spring druid2. 编写spring.xml整合:spr ...

  5. Spring——Spring整合MyBatis

    文章目录: 1.写在前面 2.实现步骤 2.1 项目的大体框架 2.2 使用Navicat在数据库中创建一张表student2 2.3 在pom.xml文件中加入maven依赖 2.4 编写实体类St ...

  6. Spring源码深度解析(郝佳)-学习-源码解析-Spring整合MyBatis

    了解了MyBatis的单独使用过程之后,我们再来看看它也Spring整合的使用方式,比对之前的示例来找出Spring究竟为我们做了什么操作,哪些操作简化了程序开发. 准备spring71.xml &l ...

  7. spring整合mybatis(实现数据的增删改查)

    一.专业术语解释 1.spring:是分层的Java SE/EE应用full - stack轻量级开源框架,以IoC(控制反转)和AOP(面向切面编程)为内核,提供展现层spring MVC 和 sp ...

  8. Spring整合MyBatis总结

    整合原理 MyBatis操作数据库,对数据库进行CRUD(增.删.改.查)操作时,实际原理是通过SqlSessionFactory对象---->产生SqlSession---->利用Sql ...

  9. 手写 Spring 事务、IOC、DI 和 MVC

    Spring AOP 原理 什么是 AOP? AOP 即面向切面编程,利用 AOP 可以对业务进行解耦,提高重用性,提高开发效率 应用场景:日志记录,性能统计,安全控制,事务处理,异常处理 AOP 底 ...

最新文章

  1. opencv reshape函数详解
  2. 网络安全等级保护测评高风险判定指引_等保知识|测评高风险项详解:安全管理中心...
  3. eclipse 在 Linux中常用命令,持续更新....
  4. Nacos 快速开始、版本选择、预备环境准备、下载源码或者安装包、从 Github 上下载源码方式、下载编译后压缩包方式、配置nacos、配置集群、启动服务器、服务注册发现和配置管理、关闭服务器
  5. 在IDEA中`New`没有`Mapper`文件选项(亲测)
  6. 悬崖边上的舞者,记7.2生产数据库灾难事件
  7. 漫游Kafka实战篇之搭建Kafka运行环境
  8. OpenCV+python调用本地摄像头并录制视频
  9. 无向图:查找最小环集合(最短路径回溯算法)
  10. PHP的学习--PHP的闭包
  11. sql内部连接_SQL内部联接的分步演练
  12. springboot和springcloud及常用注解积累
  13. spring-boot实现访问http跳转到https端口的方法
  14. Eclipse 汉化方法
  15. python里none什么意思_python none代表什么
  16. 【思想感悟】站在巨人的肩膀上
  17. 项目管理-4-运筹帷幄
  18. ireport分页后最后多一页空白页
  19. Qt5中使用lambda表达式
  20. 鸿蒙坐骑九彩祥云,封神演义五大神兽坐骑排名,第一名太出人意料了!

热门文章

  1. 生于70年代--我爱的NEW AGE音乐
  2. oracle中bulk,Oracle数据库之FORALL与BULK COLLECT语句
  3. 微信android发送图片,[求助]Xposed微信发送图片方法
  4. mysql数据库blob换行_mysql数据库blob类型
  5. centOS镜像 + Wmwar虚拟机+Xshell + git实现一键部署服务器(详细)
  6. java addall的用法_Java Collections addAll()用法及代码示例
  7. 带头节点的单链表逆置
  8. Picker——uniapp[uview]微信小程序兼容支付宝小程序
  9. Android知识点复习(持续更新中)
  10. python爬取微信公众号文章信息