MyBatis框架的代理模式
通过前面的了解,我们大致知道了MyBatis框架的基本使用方式。但是,我们可能会有一个疑问,为什么Mapper接口没有实现类却能被正常调用呢?其实这是因为MyBatis在Mapper接口上使用了动态代理的一种非常规的用法。在了解这种用法之前,我们先来看看原生方法的使用。
原生方法的调用方式
-
以查询操作为例,原生方法的调用会直接通过
sqlSession
会话调用相应的方法来达到查询的目的。代码如下: -
接口文件:
public interface StudentMapper {
/**
* 模糊查询
*/
public List<Student> getStudentByName(String str);
}
- XML文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tulun.dao.StudentMapper">
<!--直接在参数上拼接通配符,进行模糊查询-->
<select id="getStudentByName" resultType="com.tulun.pojo.Student">
select * from Student where SName like #{
SName}
</select>
mapper>
- 测试文件:
public class StudentMapper3Test {
private SqlSessionFactory sqlSessionFactory;
@Before
public void before() {
//mybatis配置文件
String resource = "mybatis-config.xml";
//通过mybatis提供的Resources类来得到配置文件流
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
//创建会话工厂,传输mybatis配置文件信息
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void getStudentByName(){
//通过工厂得到SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//原生方法调用形式
//通过方法接口进行调用
List<Object> objects = sqlSession.selectList("com.tulun.dao.StudentMapper3.getStudentByName", "%L%");
System.out.println(objects);
}
}
-
注意测试方式的不同:
- 返回多个结果时,使用
selectList
方法。 - 返回的结果不管是单个还是多个在resultType属性都是返回的Java对象全路径。
- 返回单个结果对象使用
selectOne
。 - 对于增删改操作,同样可以使用此操作,其
sqlSession
对象也同样提供了其对应的方法。
- 返回多个结果时,使用
-
执行结果:
而在MyBatis的使用过程中,我们经常使用的是通过getMapper
方法获取代理对象,形如:StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
方式,从而进行对接口的动态代理,接下来,我们看看什么是动态代理。
动态代理
动态代理其实是代理模式的一种,而代理模式其实是Java当中的一种设计模式。其结构如图所示:
这种模式就相当于一个公司生产了一个产品,但并不是由其开发人员去销售,而是由销售人员去销售。
代理模式的特点:
- 代理模式中代理类和委托类是具有相同的接口。
- 代理类的主要职责就是为委托类预处理消息,过滤消息等功能的实现。
- 代理类的对象本身并不是真正的实现服务,而是通过委托类的对象的相关方法,来提供特定的一些服务。
- 代理类和委托类之间存在关联关系,一个代理类的对象和一个委托类的对象相关联。
- 访问实际对象,是通过代理对象来访问的。
代理模式的分类: 静态代理和动态代理。
- 静态代理是在程序编译阶段就确定了代理对象。
- 而动态代理是在程序运行阶段才确定代理对象。
- 而在这里,我们仅仅来分析一下动态代理:
其中动态代理是在运行时根据Java代码指示动态生成的,相比较静态代理,优势在于方便对代理类的函数进行统一的处理,而不用修改每个代理类的方法。
Java中提供的动态代理方式有两种:JDK自带的动态代理
和CGLib实现的代理
。下面来看看这两种方式的区别。
JDK自带的动态代理
JDK自带的代理方式是需要实现invocationHandler接口,并且实现invoke的方法来进行的。下面我们来自己实现一下这种代理方式:
- 先提供一个共有的接口和委托类的实现。
- 接口文件:
/**
* 接口类
* 定义委托类和代理类共工的方法
*/
public interface IUser {
void talk();
}
- 委托实现类:
/**
* 委托类
* 实现了接口Iuser中的talk方法
*/
public class User implements IUser {
@Override
public void talk() {
System.out.println("doing User.talk");
}
}
- 要实现动态代理,需要首先创建一个实现了InvocationHandler接口的辅助类。
/**
* 代理的辅助类
*/
public class UserProxy implements InvocationHandler {
private Object object;