mybatis框架的使用非常广泛,使用者只需要开发很简单的代码就能实现很多业务需求,真的非常方便,简化开发的同时还规范了开发,也因此成为在持久映射框架中最为流行的框架之一。
使用mybatis开发的同学都应该接触过这样一种操作,我们只需要定义dao的接口,并没有具体的接口实现,却能够正常调用通过dao来执行sql,这是怎么做到的呢?
最近研究了一下mybatis的源码,通过理解源码得出是通过动态代理来实现的,这里做了一个简单的例子以方便我们理解,示例代码如下:
1.新建两个dao接口
package com.wisea.demoh2.mbatis.proxy;
public interface UserDao {
@Insert
int add(String str);
@Update
int update(String str);
}
package com.wisea.demoh2.mbatis.proxy;
public interface OrderDao {
@Insert
int add(String str);
@Update
int update(String str);
}
2.创建代理工厂类
package com.wisea.demoh2.mbatis.proxy;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
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 mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}
protected T newInstance(MapperProxy<T> mapperProxy){
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
}
public T newInstance(){
final MapperProxy<T> mapperProxy = new MapperProxy<>(mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
3.创建代理类实现InvocationHandler接口
package com.wisea.demoh2.mbatis.proxy;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
public class MapperProxy<T> implements InvocationHandler, Serializable {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
}
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(args);
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(method, this);
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
}
4.创建代理处理的方法类
package com.wisea.demoh2.mbatis.proxy;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
public class MapperMethod {
private final Method method;
private final MapperProxy mapperProxy;
public MapperMethod(Method method, MapperProxy mapperProxy) {
this.method = method;
this.mapperProxy = mapperProxy;
}
public Object execute(Object[] args){
System.out.println("MapperProxy: " + mapperProxy);
System.out.println("execute method {" + method.getName() + "}" );
// 读取mapper.xml中的语句块,并将其作为SqlStatement执行,
// 方法名获取sql语句,参数通过代理的Invocation等获得
// 有注解则处理注解中的参数
// 此处只是简单的模拟一下
final Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
Class<? extends Annotation> aClass = annotation.annotationType();
if (aClass.equals(Insert.class)) {
System.out.println("execute insert {" + args[0] + "} completed");
}
if (aClass.equals(Update.class)) {
System.out.println("execute update {" + args[0] + "} completed");
}
}
return 1;
}
}
5.编写测试类
package com.wisea.demoh2.mbatis.proxy;
public class ProxyMain {
public static void main(String[] args) {
System.out.println("实例化UserDao的代理工厂");
MapperProxyFactory<UserDao> userDaoProxyFactory = new MapperProxyFactory<>(UserDao.class);
UserDao target = userDaoProxyFactory.newInstance();
System.out.println(target);
target.add("chinoukin");
target.update("chenyingqin");
System.out.println("\n实例化OrderDao的代理工厂");
MapperProxyFactory<OrderDao> OrderDaoProxyFactory = new MapperProxyFactory<>(OrderDao.class);
OrderDao target1 = OrderDaoProxyFactory.newInstance();
System.out.println(target1);
target1.add("订单1");
target1.update("订单xxxx");
}
}
执行结果如下:
实例化UserDao的代理工厂
com.wisea.demoh2.mbatis.proxy.MapperProxy@b4c966a
MapperProxy: com.wisea.demoh2.mbatis.proxy.MapperProxy@b4c966a
execute method {add}
execute insert {chinoukin} completed
MapperProxy: com.wisea.demoh2.mbatis.proxy.MapperProxy@b4c966a
execute method {update}
execute update {chenyingqin} completed
实例化OrderDao的代理工厂
com.wisea.demoh2.mbatis.proxy.MapperProxy@7530d0a
MapperProxy: com.wisea.demoh2.mbatis.proxy.MapperProxy@7530d0a
execute method {add}
execute insert {订单1} completed
MapperProxy: com.wisea.demoh2.mbatis.proxy.MapperProxy@7530d0a
execute method {update}
execute update {订单xxxx} completed
Process finished with exit code 0
总结:
1.先为每一个DAO接口创建一个代理工厂
2.再通过代理工厂去创建代理对象,如上面的"MapperProxy: com.wisea.demoh2.mbatis.proxy.MapperProxy@b4c966a"
3.通过代理对象去调用实际的业务逻辑