内容简介:优质文章,第一时间送达
点击上方 蓝色字体 ,选择“设置星标”
优质文章,第一时间送达
小匠自己P的图怎么样?
前言
因为 MyBatis
的易上手性和可控性,使得它成为了 ORM
框架中的首选。近日新起了一个项目,所以重新搭建了一下 Spring-mybatis
, 下面是搭建笔记和从 Spring-mybatis
源码分析其如何使用 Java动态代理
,希望对大家有帮助。
Spring 集成 Mybatis
Spring
集成 Mybatis
的方式有很多种,大家耳熟能详的 xml
配置方式或者本文的采用的方式:
首先需要添加 MyBatis
的和 MyBatis-Spring
的依赖,本文使用的 Spring-mybatis
版本是1.3.1。在 mvnrepository
里面我们可以找到当前 Spring-mybatis
依赖的 spring
和 mybatis
版本,最好是选择匹配的版本以避免处理不必要的兼容性问题。因为 MyBatis-Spring
中对 mybatis
的依赖选择了 provided
模式,所以我们不得不额外添加 mybatis
依赖,依赖配置如下。
接下来会我们要创建工厂bean,放置下面的代码在 Spring 的 XML 配置文件中:
这个工厂需要一个 DataSource
,就是我们熟知的数据源了。这里我们选择了阿里的 Druid
,同样我们需要引入两个配置
添加 Spring
配置如下
接下来我们要编写数据库访问对象,大多数人会把它叫做 DAO
或者 Repository
,在这里其被称为
Mapper
,也是因为它的实现方式所决定。要注意的是所指定的映射器类必须是一个接口,而不是具体的实现类。这便因为 Mybatis
的内部实现使用的是 Java动态代理
,而 Java动态代理
只支持接口,关于 动态代理
我们下文有更详细的描述。
接下来可以使用 MapperFactoryBean
,像下面这样来把接口加入到 Spring
中,这样就把 UserMapper
和 SessionFactory
关联到一起了,原来使用 xml
配置的时候还需要Dao继承 SqlSessionDaoSupport
才能注入 SessionFactory
,这种方式直接通过 Java动态代理
把 SqlSessionFactory
代理给了 UserMapper
,使得我们直接使用 UserMapper
即可。配置如下。
这样我们已经完成了90%,就差调用了,前提是你 Spring
环境是OK的。调用 MyBatis
数据方法现在只需一行代码:
那么问题又来了,每次写一个DAO都需要为其写一个 Bean
配置,那不是累死?于是我们又寻找另一种方案,代替手动声明 *Mapper
。 MapperScannerConfigurer
的出现解决了这个问题, 它会根据你配置的包路径自动的扫描类文件并自动将它们创建成 MapperFactoryBean
,可以在 Spring 的配置中添加如下代码:
basePackage
属性是让你为映射器接口文件设置基本的包路径。你可以使用分号或逗号作为分隔符设置多于一个的包路径。这个时候如果想自定义 sqlSessionFactory
可以添加如下配置:
这样以后还有一点点小瑕疵,如果我们数据的 column
名字是 _
连接的,那么它不会那么聪明自动转换为驼峰的变量,所以我们需要对 SqlSessionFactoryBean
做如下配置,但是在1.3.0以后才可以通过xml配置,如果用早起版本的需要注意了。
至此关于 SpringMyBatis
的配置已经全部结束,后面我们会简单说下 SpringMyBatis
中的动态代理。
浅析 Java 动态代理
JDK
自带的动态代理需要了解InvocationHandler接口和Proxy类,他们都是在java.lang.reflect包下。
InvocationHandler
是代理实例的调用处理程序实现的接口。每个代理实例都具有一个关联的 InvocationHandler
。对代理实例调用方法时,这个方法会调用 InvocationHandler
的 invoke
方法。 Proxy
提供静态方法用于创建动态代理类和实例,同时后面自动生成的代理类都是 Proxy
对象。下面我们直接通过代码来分析 Java动态代理
: InvocationInterceptor
实现 InvocationHandler
接口,用于处理具体的代理逻辑。
User
和 UserImpl
是被代理对象的接口和类
DynamicProxyTest
是测试类,用于创建 InvocationInterceptor
和 Proxy
类以便测试。
输入结果如下:
很明显,我们通过proxyInstance这个代理类进行方法调用的时候,会在方法调用前后进行输出打印,这样就简单的实现了一个 Java动态代理
例子。动态代理不仅仅是打印输出这么简单,我们可以通过它打印日志,打开关闭事务, 权限检查了等等。当然它更是许多框架的钟爱,就如下文我们要说的 MyBatis
中 Java动态代理
的实现。再多说一句 Spring
的 AOP
也是使用动态代理实现的,当然它同时使用了 Java动态代理
和 CGLib
两种方式。不过 CGLIB
不是本文要讨论的范围。
注意观察的同学看到上面代码的时候可能发现 invoke
方法的 proxy
参数并没有被使用,笔者查阅了一些相关文档也没有找到合理的说法,只能在源码中看看究竟喽,笔者当前的JDK版本是1.8。我们从入口开始, Proxy.newProxyInstance
:
如上代码由此可见,它调用了 getProxyClass0
来获取 ProxyClass
,那我们继续往下看。
其实上面写的已经很简单了,如果存在就在 proxyClassCache
里面获取到,如果不存在就使用 ProxyClassFactory
创建一个。当然我们如果看一下 proxyClassCache
变量的话其也是 ProxyClassFactory
对象。
那么我们直接就去查看 ProxyClassFactory
的实现问题不就解决了吗?
由上代码便一目了然了,为什么我们 Debug
的时候 Proxy
对象是 $Proxy0
,是因为他通过 $Proxy
和 AtomicLong
拼起来的类名,其实这不是重点。重点是 ProxyGenerator.generateProxyClass(proxyName,interfaces,accessFlags)
。这就是生成 class
的地方,它把所有的条件组合好,生成 class
文件,然后再加载到内存里面以供使用。有兴趣的同学可以继续往深处查看。而我们需要做的是获取到他生成的字节码,看一下里面到底是什么?当 saveGeneratedFiles
为 true
的时候会保存 class
文件,所以我们在 DynamicProxyTest
的 main
函数添加一行即可:
通过 Debug
我们可以发现,它存储 class
文件的路径是 com/sun/proxy/$Proxy0.class
,所以直接在我们项目的目录下面就能找到它,然后通过 Idea
打开便得到如下代码:
这样好多问题就迎刃而解。
为什么 Java动态代理
必须是接口,因为生成的类要去实现这个接口。
invoke
方法的 proxy
是干嘛的,通过 super.h.invoke(this,m3,(Object[])null);
我们可以发现传递给 invoke
方法的就是 Proxy
本身。
同时 Proxy
类也通过反射实现了 toString
, equals
,和 hashcode
等方法。
自此关于 Java动态代理
的讲解已经告段落,下面让我们简单看一下 Spring-mybatis
中关于 Java动态代理
的使用。
Java动态代理在Spring-mybatis中的实现
关于 Spring-mybatis
的实现我们得从 MapperScannerConfigurer
说起,首先 MapperScannerConfigurer
实现了 BeanDefinitionRegistryPostProcessor
接口。而 BeanDefinitionRegistryPostProcessor
依赖于 Spring
框架,简单的说 BeanDefinitionRegistryPostProcessor
使得我们可以将 BeanDefinition
添加到 BeanDefinitionRegistry
中,而 BeanDefinition
描述了一个Bean实例所拥有的实例、结构参数和参数值,简单点说拥有它就可以实例化 Bean
了。 BeanDefinitionRegistryPostProcessor
的 postProcessBeanDefinitionRegistry
方法在 Bean
被定义但还没被创建的时候执行,所以 Spring-mybatis
也是借助了这一点。需要想需要更深入的了解可以查看 Spring
的生命周期。
由上代码我们可以看到在 postProcessBeanDefinitionRegistry
里面得到 registry
然后使用 ClassPathMapperScanner
开始扫描包路径得到的 Bean
并且注册到 registry
里面。我们接着往里面看。
ClassPathMapperScanner
继承了 Spring
的 ClassPathBeanDefinitionScanner
所以调用父类的 doScan
方法就可以加载 Bean
然后再通过 processBeanDefinitions
方法加工成 MyBatis
需要的 Bean
。
如上代码循环了所有由 Spring
容器解析出来的 beanDefinitions
然后把他们的 BeanClass
修改为 mapperFactoryBean
,这就进入了行文的重点。我们翻看到 MapperFactoryBean
:
其调用了 Configuration
的 addMapper
方法,这样就把 Bean
交给 MyBatis
管理了。那么 checkDaoConfig
是什么时候调用的呢?我们翻看其父类 DaoSupport
可以看到:
因为 DaoSupport
实现了 InitializingBean
并重写 afterPropertiesSet
方法,了解 Spring
生命周期的同学知道 afterPropertiesSet
方法会在资源加载完以后,初始化bean之前执行。我们继续查看 addMapper
方法。
addMapper
方法最终创建了 MapperProxyFactory
对象,在 MapperProxyFactory
里面我们两眼泪汪汪地发现了似曾相识的代码:
而 MapperProxy
实现了 InvocationHandler
方法,最终实现对 Bean
的代理,同时获取到上下文的 sqlSession
以供使用。具体生成过程我们不再累述,直接通过其源码结束本篇文章:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Usability for the Web
Tom Brinck、Darren Gergle、Scott D. Wood / Morgan Kaufmann / 2001-10-15 / USD 65.95
Every stage in the design of a new web site is an opportunity to meet or miss deadlines and budgetary goals. Every stage is an opportunity to boost or undercut the site's usability. Thi......一起来看看 《Usability for the Web》 这本书的介绍吧!