动态代理设计模式
Mybatis中实现
代理模式可以认为是Mybatis的核心使用的模式,正是由于这个模式,我们只需要编写Mapper.java接 口,不需要实现,由Mybati s后台帮我们完成具体SQL的执行。 当我们使用Configuration的getMapper方法时,会调用mapperRegistry.getMapper方法,而该方法又 会调用mapperProxyFactory.newInstance(sqlSession)来生成一个具体的代理:
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return this.mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return this.methodCache;
}
//通过JDK动态代理创建出Dao接口的代理对象
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
}
在这里,先通过T newInstance(SqlSession sqlSession)方法会得到一个MapperProxy对象,然后调用T newInstance(MapperProxy mapperProxy)生成代理对象然后返回。而查看MapperProxy的代码,可 以看到如下内容:
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
//调用dao接口的方法最终都会执行该方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
if (this.isDefaultMethod(method)) {
return this.invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
MapperMethod mapperMethod = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
}
非常典型的,该MapperProxy类实现了InvocationHandler接口,并且实现了该接口的invoke方法。通 过这种方式,我们只需要编写Mapper.java接口类,当真正执行一个Mapper接口的时候,就会转发给 MapperProxy.invoke方法,而该方法则会调用后续的sqlSession.cud>executor.execute>prepareStatement 等一系列方法,完成 SQL 的执行和返回
举例
1.创建一个抽象类,Person接口,使其拥有一个没有返回值的doSomething方法。
public interface Person {
public void doSomething();
}
2.创建一个名为Bob的Person接口的实现类,使其实现doSomething方法
public class Bob implements Person {
@Override
public void doSomething() {
System.out.println("Bob doing Something");
}
}
3.创建JDK动态代理类,使其实现InvocationHandler接口。拥有一个名为target的变量,并创建 getTa rget获取代理对象方法。
public class JDKDynamicProxy implements InvocationHandler {
// 声明被代理的对象
private Person person;
//构造函数
public JDKDynamicProxy(Person person) {
this.person = person;
}
//获取代理对象
public Object getTarget(){
return Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), this);
}
/**
* @param proxy 代理对象,一般这个参数没怎么用
* @param method 被代理的方法
* @param args 被代理方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("对原方法进行了前置增强");
//原方法执行
Object invoke = method.invoke(person, args);
System.out.println("对原方法进行了后置增强");
return invoke;
}
}
4.创建JDK动态代理测试类J DKDynamicTest
public class proxyTest {
public static void main(String[] args) {
System.out.println("不使用代理类,调用doSomething");
Person person = new Bob();
person.doSomething();
System.out.println("--------------------------");
System.out.println("使用代理类,调用doSomething");
Person proxy = (Person) new JDKDynamicProxy(new Bob()).getTarget();
proxy.doSomething();
}
}
适配器设计模式
MyBatis中实现
StatementHandler接口如下:
public interface StatementHandler {
//创建数据库操作对象Statement对象
Statement prepare(Connection connection, Integer transactionTimeout)
throws SQLException;
void parameterize(Statement statement)
throws SQLException;
void batch(Statement statement)
throws SQLException;
int update(Statement statement)
throws SQLException;
<E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException;
<E> Cursor<E> queryCursor(Statement statement)
throws SQLException;
BoundSql getBoundSql();
ParameterHandler getParameterHandler();
}
StatementHandler接口的继承结构如下
从上面可知StatementHandler接口中的prepare方法是用来创建数据库操作对象Statement或者PreparedStatement或者CallableStatement的,并且StatementHandler接口的实现类BaseStatementHandler中只是简单的实现,并没有真正创建数据库操作对象,如下:
由以上可知创建数据库操作对象的instantiateStatement方法在BaseStatementHandler类中是一个抽象方法,其具体的实现在BaseStatementHandler的三个子类中实现,即SimpleStatementHandler,PreparedStatementHandler和CallableStatementHandler
查看他们的instantiateStatement方法如下:
举例
注意:以下命名不规范,单纯只是为了能讲清楚适配器设计模式。
1.有一个学生守则接口,如下:
public interface 学生守则接口 {
//不受实现类欢迎方法
public void 考试后家长签字();
//受到实现类欢迎方法
public void 每周免费看一次电影();
}
2.对于小学生而言,他只想看电影,不想考试后家长签字,怎么办呢?这时候他找到了校门口附近的零食小商贩帮他在试卷上签字,如下:
public abstract class 零食小商贩 implements 学生守则接口{
@Override
public void 考试后家长签字() {
System.out.println("家长阅");
}
}
3.接着小学生很开心地认了小商贩作为他地干爹,自己每周都可以免费看电影,而不用考试后找家长签字了,如下:
public class 小学生 extends 零食小商贩 {
@Override
public void 每周免费看一次电影() {
System.out.println("看电影");
}
}
4.测试类如下:
public class TestMain {
public static void main(String[] args) {
学生守则接口 mike = new 小学生();
//mike.考试后家长签字();
mike.每周免费看一次电影();
}
}