简述使用spring整合mybatis的基本步骤,spring springmvc mybatis整合
00-1010分析MyBatis的基本工作原理及需要解决的问题;春季菜豆的发生过程;问题解决汇总在介绍Spring整合Mybatis原理之前,我们得先来稍微介绍Mybatis的工作原理。
00-1010在Mybatis中,我们可以使用一个接口来定义要执行的sql。简化后的代码如下:定义一个接口,@Select表示执行查询sql语句。
公共接口User mapper { @ Select( Select * from User where id=# { id } )User Select byid(整数id);}执行代码:
InputStream InputStream=resources . getresourceasstream( my batis . XML );SqlSessionFactory SqlSessionFactory=new SqlSessionFactory builder()。构建(inputStream);SQL session SQL session=sqlsessionfactory . open session();//以下是我们需要注意的重点user mapper=SQL session . get mapper(user mapper . class);整数id=1;user user=mapper . selectbyid(id);它使Mybatis的目的是:程序员能够通过调用方法来执行指定的sql,并封装了执行sql的底层逻辑。在这里,我将重点关注下面的映射器对象。调用SqlSession的getMapper方法时,会为传入接口生成一个代理对象,这个代理对象就是程序真正需要的。在调用代理对象方法时,Mybatis会取出该方法对应的sql语句,然后用JDBC执行sql语句,最后得到结果。
目录
Spring和Mybatis,我们应该关注这个代理对象。因为集成的目的是把一个Mapper的代理对象作为bean放入Spring容器,这样就可以像普通bean一样使用,比如可以通过@Autowire自动注入。例如,当Spring和Mybatis集成时,我们可以使用下面的代码来使用Mybatis中的代理对象:@ Component public class UserService { @ auto wired private user mapper user mapper;公共用户getUserById(整数id){ return User mapper . select byid(id);} } UserService中的userMapper属性将作为Mybatis中的代理对象自动注入。如果基于集成项目进行调试,会发现userMapper的类型是:org . Apache . ibatis . binding . mapper proxy @ 41 a0aa 7d。它被证明是Mybatis中的代理对象。好了,那么现在我们要解决的问题是:如何将Mybatis的代理对象作为bean放入Spring容器?要解决这个问题,我们需要对Spring的bean生成过程有一个了解。
Mybatis的基本工作原理
Spring Spring的启动,bean大致会通过以下步骤生成扫描指定包路径下的类文件,根据类信息生成相应的BeanDefinition。在这里,程序员可以使用一些机制来修改BeanDefinition,根据BeanDefinition生成bean实例,并将生成的bean实例放入Spring容器以假设有一个A类,假设有如下代码:一个类。
@Component公共类A {}是一个B类,没有@Component注释。
公共B类{ }执行如下代码:
注释ConfigApplicationContext c
ontext = 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内容请搜索盛行IT以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。