Spring整合Mybatis实操分享

目录
  • Mybatis的基本工作原理
  • 分析需要解决的问题
  • Spring中Bean的产生过程
  • 解决问题
  • 总结

在介绍Spring整合Mybatis原理之前,我们得先来稍微介绍Mybatis的工作原理。

Mybatis的基本工作原理

Mybatis中,我们可以使用一个接口去定义要执行sql,简化代码如下: 定义一个接口,@Select表示要执行查询sql语句。

 public interface UserMapper {
 @Select("select * from user where id = #{id}")
 User selectById(Integer id);
 }

以下为执行sql代码:

 InputStream inputStream = Resources.getResourceAsStream("mybatis.xml" );
 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder(). build(inputStream);
 SqlSession sqlSession = sqlSessionFactory.openSession();
 // 以下使我们需要关注的重点
 UserMapper mapper = sqlSession.getMapper(UserMapper.class);
 Integer id = 1;
 User user = mapper.selectById(id);

Mybatis的目的是:使得程序员能够以调用方法的方式执行某个指定的sql,将执行sql的底层逻辑进行了封装。 这里重点思考以下mapper这个对象,当调用SqlSession的getMapper方法时,会对传入的接口生成一个 代理对象,而程序要真正用到的就是这个代理对象,在调用代理对象的方法时,Mybatis会取出该方法所对应的sql语句,然后利用JDBC去执行sql语句,最终得到结果。

分析需要解决的问题

Spring和Mybatis时,我们重点要关注的就是这个代理对象。因为整合的目的就是:把某个Mapper的代理 对象作为一个bean放入Spring容器中,使得能够像使用一个普通bean一样去使用这个代理对象,比如能 被@Autowire自动注入。 比如当Spring和Mybatis整合之后,我们就可以使用如下的代码来使用Mybatis中的代理对象了:

 @Component
 public class UserService {
 @Autowired
 private UserMapper userMapper; 

 public User getUserById(Integer id) {
 return userMapper.selectById(id);
 }
 }

UserService中的userMapper属性就会被自动注入为Mybatis中的代理对象。如果你基于一个已经完成整合的项目去调试即可发现,userMapper的类型为: org.apache.ibatis.binding.MapperProxy@41a0aa7d。证明确实是Mybatis中的代理对象。 好,那么现在我们要解决的问题的就是:如何能够把Mybatis的代理对象作为一个bean放入Spring容器中?要解决这个,我们需要对Spring的bean生成过程有一个了解。

Spring中Bean的产生过程

Spring启动过程中,大致会经过如下步骤去生成bean

  • 扫描指定的包路径下的class文件
  • 根据class信息生成对应的BeanDefinition
  • 在此处,程序员可以利用某些机制去修改BeanDefinition
  • 根据BeanDefinition生成bean实例
  • 把生成的bean实例放入Spring容器中

假设有一个A类,假设有如下代码: 一个A类

@Component
 public class A {
 }

一个B类,不存在@Component注解

 public class B {
 }

执行如下代码:

AnnotationConfigApplicationContext context = new AnnotationConfigAppl icationContext(AppConfig.class);
 System.out.println(context.getBean("a"));

输出结果为:com.luban.util.A@6acdbdf5 A类对应的bean对象类型仍然为A类。但是这个结论是不确定的,我们可以利用BeanFactory后置处理器来 修改BeanDefinition,我们添加一个BeanFactory后置处理器:

 @Component
 public class LubanBeanFactoryPostProcessor implements BeanFactoryPost Processor {
 @Override
public void postProcessBeanFactory(ConfigurableListableBeanFactor y beanFactory)
 throws BeansException {
 BeanDefinition beanDefinition = beanFactory.getBeanDefinition
("a");
beanDefinition.setBeanClassName(B.class.getName());
}
 }

这样就会导致,原本的A类对应的BeanDefiniton被修改了,被修改成了B类,那么后续正常生成的bean对 象的类型就是B类。此时,调用如下代码会报错:

 context.getBean(A.class);

但是调用如下代码不会报错,尽管B类上没有@Component注解:

 context.getBean(B.class);

并且,下面代码返回的结果是:

com.luban.util.B@4b1c1ea0

 AnnotationConfigApplicationContext context = new AnnotationConfigAppl icationContext(AppConfig.class);
 System.out.println(context.getBean("a"));

之所以讲这个问题,是想说明⼀个问题:在Spring中,bean对象跟class没有直接关系,跟 BeanDefinition才有直接关系。 那么回到我们要解决的问题:如何能够把Mybatis的代理对象作为一个bean放入Spring容器中? 在Spring中,如果你想生成一个bean,那么得先生成一个BeanDefinition,就像你想new一个对象实 例,得先有一个class。

解决问题

继续回到我们的问题,我们现在想自己生成一个bean,那么得先生成一个BeanDefinition,只要有了 BeanDefinition,通过在BeanDefinition中设置bean对象的类型,然后把BeanDefinition添加给 Spring,Spring就会根据BeanDefinition⾃动帮我们⽣成⼀个类型对应的bean对象。

所以,现在我们要解决两个问题:

  • Mybatis的代理对象的类型是什么?因为我们要设置给BeanDefinition
  • 我们怎么把BeanDefinition添加给Spring容器?

注意:上文中我们使用的BeanFactory后置处理器,他只能修改BeanDefinition,并不能新增一个 BeanDefinition。我们应该使用Import技术来添加一个BeanDefinition。后面再详细介绍如果使用Import 技术来添加一个BeanDefinition,可以先看一下伪代码实现思路。

假设:我们有一个UserMapper接口,他的代理对象的类型为UserMapperProxy。 那么我们的思路就是这样的,

伪代码如下:

 BeanDefinitoin bd = new BeanDefinitoin();
 bd.setBeanClassName(UserMapperProxy.class.getName());
 SpringContainer.addBd(bd);

但是,这里有一个严重的问题,就是上文中的UserMapperProxy是我们假设的,他表示一个代理类的类 型,然而Mybatis中的代理对象是利用的JDK的动态代理技术实现的,也就是代理对象的代理类是动态生成的,我们根本方法确定代理对象的代理类到底是什么。 所以回到我们的问题:Mybatis的代理对象的类型是什么? 本来可以有两个答案: 1. 代理对象对应的代理类 2. 代理对象对应的接口 那么答案1就相当于没有了,因为是代理类是动态生成的,那么我们来看答案2:代理对象对应的接口如果我们采用答案2,那么我们的思路就是:

BeanDefinition bd = new BeanDefinitoin();
 // 注意这⾥,设置的是UserMapper
 bd.setBeanClassName(UserMapper.class.getName());
 SpringContainer.addBd(bd);

但是,实际上给BeanDefinition对应的类型设置为一个接口是行不通的,因为Spring没有办法根据这个 BeanDefinition去new出对应类型的实例,接口是没法直接new出实例的。 那么现在问题来了,我要解决的问题:Mybatis的代理对象的类型是什么? 两个答案都被我们否定了,所以这个问题是无解的,所以我们不能再沿着这个思路去思考了,只能回到最 开始的问题:如何能够把Mybatis的代理对象作为一个bean放入Spring容器中?

总结上文的推理:我们想通过设置BeanDefinition的class类型,然后由Spring自动的帮助我们去生成对应的bean,但是这条路是行不通的。 终极解决方案 那么我们还有没有其他办法,可以去生成bean呢?并且生成bean的逻辑不能由Spring来帮我们做了,得 由我们自己来做。 FactoryBean 有,那就是Spring中的FactoryBean。我们可以利用FactoryBean去自定义我们要生成的bean对象,比如

 @Component
 public class LubanFactoryBean implements FactoryBean {
 @Override
public Object getObject() throws Exception {
 Object proxyInstance = Proxy.newProxyInstance(LubanFactoryBe an.class.
getClassLoader(), new Class[]{UserMapper.class}, new Invoca tionHandler() {
 @Override
 public Object invoke(Object proxy, Method method, Object [] args) throws Throwable {
 if (Object.class.equals(method.getDeclaringClass())) {
 return method.invoke(this, args);
 } else {
 // 执⾏代理逻辑
 return null;
 }
 }
 });
 return proxyInstance;
 }
 @Override
 public Class<?> getObjectType() {
return UserMapper.class;
 }
 }

我们定义了一个LubanFactoryBean,它实现了FactoryBean,getObject方法就是用来自定义生成bean 对象逻辑的。 执行如下代码:

 public class Test {
 public static void main(String[] args) {
 AnnotationConfigApplicationContext
context = new AnnotationCo nfigApplicationContext(AppConfig.class);
 System.out.println("lubanFactoryBean: " + context.getBean
("lu banFactoryBean"));
 System.out.println("&lubanFactoryBean: " + context.getBean
("& lubanFactoryBean"));
 System.out.println("lubanFactoryBean-class: " +
context.getBe an("lubanFactoryBean").getClass());
 }
}

将打印: lubanFactoryBean: com.luban.util.LubanFactoryBean1@4d41cee &lubanFactoryBean: com.luban.util.LubanFactoryBean@3712b94 lubanFactoryBean-class: class com.sun.proxy.Proxy20 从结果我们可以看到,从Spring容器中拿名字为"lubanFactoryBean"的bean对象,就是我们所自定义的 jdk动态代理所生成的代理对象。

所以,我们可以通过FactoryBean来向Spring容器中添加一个自定义的bean对象。上文中所定义的 LubanFactoryBean对应的就是UserMapper,表示我们定义了一个LubanFactoryBean,相当于把 UserMapper对应的代理对象作为一个bean放入到了容器中。 但是作为程序员,我们不可能每定义了一个Mapper,还得去定义一个LubanFactoryBean,这是很麻烦的 事情,我们改造一下LubanFactoryBean,让他变得更通用,

比如:

 @Component
public class LubanFactoryBean implements FactoryBean {
// 注意这里
 private Class mapperInterface;
 public LubanFactoryBean(Class mapperInterface) {
 this.mapperInterface = mapperInterface;
 }
 @Override
 public Object getObject() throws Exception {
 Object proxyInstance = Proxy.newProxyInstance(LubanFactoryBe an.class.getClassLoader(),
new Class[]{mapperInterface}, new Invocat ionHandler() {
 @Override
 public Object invoke(Object proxy, Method method, Object [] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
 } else {
 // 执行代理逻辑
 return null;
 }
 }
 }); 

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

改造LubanFactoryBean之后,LubanFactoryBean变得灵活了,可以在构造LubanFactoryBean时,通 过构造传入不同的Mapper接口。 实际上LubanFactoryBean也是一个Bean,我们也可以通过生成一个BeanDefinition来生成一个 LubanFactoryBean,并给构造方法的参数设置不同的值,比如伪代码如下:

BeanDefinition bd = new BeanDefinitoin();
 // 注意一:设置的是LubanFactoryBean
 bd.setBeanClassName(LubanFactoryBean.class.getName());
 // 注意二:表示当前BeanDefinition在生成bean对象时,会通过调用LubanFactoryBean
 的构造方法来生成,并传入UserMapper
bd.getConstructorArgumentValues().addGenericArgumentValue(UserMapper. class.getName())
 SpringContainer.addBd(bd)

特别说一下注意二,表示表示当前BeanDefinition在生成bean对象时,会通过调用LubanFactoryBean的 构造方法来生成,并传入UserMapper的Class对象。那么在生成LubanFactoryBean时就会生成一个 UserMapper接口对应的代理对象作为bean了。 到此为止,其实就完成了我们要解决的问题:把Mybatis中的代理对象作为一个bean放入Spring容器中。

只是我们这是用简单的JDK代理对象模拟的Mybatis中的代理对象,如果有时间,我们完全可以调⽤ Mybatis中提供的方法区生成一个代理对象。这里就不花时间去介绍了。 Import 到这里,我们还有一个事情没有做,就是怎么真正的定义一个BeanDefinition,并把它添加到Spring中, 上文说到我们要利用Import技术,比如可以这么实现:

定义如下类:

public class LubanImportBeanDefinitionRegistrar implements ImportBea nDefinitionRegistrar {
 @Override
 public void registerBeanDefinitions(AnnotationMetadata importing
 ClassMetadata, BeanDefinitionRegistry registry) {
 BeanDefinitionBuilder builder = BeanDefinitionBuilder.generi cBeanDefinition();
 AbstractBeanDefinition beanDefinition = builder.getBeanDefin ition();
 beanDefinition.setBeanClass(LubanFactoryBean.class);
 beanDefinition.getConstructorArgumentValues().addGenericArgu mentValue(UserMapper.class);
 // 添加beanDefinition
 registry.registerBeanDefinition("luban"+UserMapper.class.get SimpleName(),
 beanDefinition);
 }
 }

并且在AppConfig上添加@Import注解:

 @Import(LubanImportBeanDefinitionRegistrar.class)
 public class AppConfig {

这样在启动Spring时就会新增一个BeanDefinition,该BeanDefinition会生成一个LubanFactoryBean对 象,并且在生成LubanFactoryBean对象时会传入UserMapper.class对象,通过LubanFactoryBean内部 的逻辑,相当于会自动生产一个UserMapper接口的代理对象作为一个bean。

总结

总结一下,通过我们的分析,我们要整合Spring和Mybatis,需要我们做的事情如下:

  • 定义一个LubanFactoryBean
  • 定义一个LubanImportBeanDefinitionRegistrar
  • 在AppConfig上添加一个注解@Import(LubanImportBeanDefinitionRegistrar.class)

到此这篇关于Spring整合Mybatis实操分享的文章就介绍到这了,更多相关Spring整合Mybatis内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBoot+Mybatis plus实现多数据源整合的实践

    SpringBoot 版本为1.5.10.RELEASE,Mybatis plus 版本为2.1.8. 第一步:填写配置信息: spring: aop: proxy-target-class: true auto: true datasource: druid: # 数据库 1 db1: url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zer

  • SpringBoot整合Mybatis LocalDateTime 映射失效的解决

    目录 SpringBoot整合Mybatis LocalDateTime映射失效 一.概述 二.具体原因 三.解决办法 四.小结一下 使用LocalDateTime报错问题 解决方法 SpringBoot整合Mybatis LocalDateTime映射失效 一.概述 最近在开发一个项目,在使用SpringBoot继承Mybatis时,做单元测试时,由于需要根据参数(类型LocaDateTime)去更新数据,发现更新记录为0. 刚开始以为是没有提交事务(Mybatis默认没有开启自动提交),后来

  • springboot整合springsecurity与mybatis-plus的简单实现

    1.概述 Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架. 它是用于保护基于Spring的应用程序的实际标准. Spring Security是一个框架,致力于为Java应用程序提供身份验证和授权. 与所有Spring项目一样,Spring Security的真正强大之处在于可以轻松扩展以满足自定义要求 springboot对于springSecurity提供了自动化配置方案,可以使用更少的配置来使用springsecurity 而在项目开发中,主要用于对用户的

  • springboot多模块化整合mybatis,mapper自动注入失败问题及解决

    目录 springboot多模块化整合mybatis,mapper自动注入失败 问题 解决 springboot mapper注入失败的一种原因 具体情况是 解决办法 springboot多模块化整合mybatis,mapper自动注入失败 问题 启动类添加@MapperScan或@ComponentScan,mapper类添加@Mapper或@Repository ==> Consider defining a bean of type 'com.ten.mapper.UserMapper'

  • springboot整合mybatisplus的方法详解

    目录 POM: application.yaml: POJO: mapper接口: 包扫描: 测试: 总结 POM: <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.1</version> </dependency> <dependenc

  • Spring整合Mybatis实操分享

    目录 Mybatis的基本工作原理 分析需要解决的问题 Spring中Bean的产生过程 解决问题 总结 在介绍Spring整合Mybatis原理之前,我们得先来稍微介绍Mybatis的工作原理. Mybatis的基本工作原理 在Mybatis中,我们可以使用一个接口去定义要执行sql,简化代码如下: 定义一个接口,@Select表示要执行查询sql语句. public interface UserMapper { @Select("select * from user where id = #

  • Spring集成MyBatis完整实例(分享)

    为了梳理前面学习的<Spring整合MyBatis(Maven+MySQL)一>与<Spring整合MyBatis(Maven+MySQL)二>中的内容,准备做一个完整的示例完成一个简单的图书管理功能,主要使用到的技术包含Spring.MyBatis.Maven与MySQL等.最后的运行效果如下: 项目结构如下: 一.新建一个基于Maven的Web项目 1.1.创建一个简单的Maven项目,项目信息如下: 1.2.修改层面信息,在项目上右键选择属性,再选择"Project

  • spring 整合mybatis后用不上session缓存的原因分析

    因为一直用spring整合了mybatis,所以很少用到mybatis的session缓存. 习惯是本地缓存自己用map写或者引入第三方的本地缓存框架ehcache,Guava 所以提出来纠结下 实验下(spring整合mybatis略,网上一堆),先看看mybatis级别的session的缓存 放出打印sql语句 configuration.xml 加入 <settings> <!-- 打印查询语句 --> <setting name="logImpl"

  • Spring整合MyBatis(Maven+MySQL)图文教程详解

    一. 使用Maven创建一个Web项目 为了完成Spring4.x与MyBatis3.X的整合更加顺利,先回顾在Maven环境下创建Web项目并使用MyBatis3.X,第一.二点内容多数是回顾过去的内容 . 1.2.点击"File"->"New"->"Other"->输入"Maven",新建一个"Maven Project",如下图所示: 1.2.请勾选"Create a si

  • Spring整合Mybatis使用<context:property-placeholder>时的坑

    背景 最近项目要上线,需要开发一个数据迁移程序.程序的主要功能就是将一个数据库里的数据,查询出来经过一系列处理后导入另一个数据库.考虑到开发的方便快捷.自然想到用spring和mybatis整合一下.甚至用mybatis的自动代码生成,可以省下大量dao层的开发. 整合的坑 之前的项目:以前也有过这种类似的程序,就把spring和mybatis整合的配置直接拿来修改下用.之前的整合配置是这样子的: 1.考虑到数据库url.用户名密码的可配置性,将这些信息放入properties文件.在sprin

  • spring 整合 mybatis 中数据源的几种配置方式(总结篇)

    因为spring 整合mybatis的过程中, 有好几种整合方式,尤其是数据源那块,经常看到不一样的配置方式,总感觉有点乱,所以今天有空总结下. 一.采用org.mybatis.spring.mapper.MapperScannerConfigurer 其实逆向工程也是这种方式 1.数据源配配置文件 2.DAO文件 package com.jdd.mapper; import com.jdd.pojo.Employee; import java.util.List; public interfa

  • Spring整合MyBatis图示过程解析

    这篇文章主要介绍了Spring整合MyBatis图示过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.导入所需要的jar依赖 !--MyBatis和Spring的整合包 由MyBatis提供--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <vers

  • Spring SpringMVC,Spring整合MyBatis 事务配置的详细流程

    整合思路 (1)SSM是什么? Spring,SpringMVC,Mybastis (2)思路 搭建整合的环境,初始化环境 搭建Spring环境,配置完成并测试 (service层) 再使用Spring整合MyBatis框架,并测试(Dao层) 最后使用Spring整合SpringMVC框架,并测试(web层) SSM搭建环境 (1)数据库创建ssm (2)创建maven工程 (3)git (创建.gitignore来过滤不用提交的文件) (4)依赖框架 (5)log4j.properties

  • ssm整合之Spring整合MyBatis框架配置事务的详细教程

    ssm整合之Spring整合MyBatis框架配置事务 1.在applicationContext.xml修改代码如下: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance

  • Spring整合MyBatis的三种方式

    1.整合之前的环境准备 导入相关的jar包 Junit测试 <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> MyBatis <dependency> <groupId

随机推荐