实现动态代理的步骤
- 通过实现 InvocationHandler 接口创建自己的调用处理器;
- 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
- 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
关于代理的知识参考https://www.ibm.com/developerworks/cn/java/j-lo-proxy1/
MapperProxy:映射器代理(调用处理器)
这个类继承了InvocationHandler接口,我们主要看这个类中的两个方法。
一是java7的执行的invoke方法
另一个就是实现InvocationHandler接口的invoke。
映射器代理类是MapperProxyFactory对应的目标类:
package org.apache.ibatis.binding;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
import org.apache.ibatis.reflection.ExceptionUtil;
import org.apache.ibatis.session.SqlSession;
/**
* 映射器代理,代理模式
*
*/
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;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//代理以后,所有Mapper的方法调用时,都会调用这个invoke方法
//并不是任何一个方法都需要执行调用代理对象进行执行,如果这个方法是Object中通用的方法(toString、hashCode等)无需执行
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
//这里优化了,去缓存中找MapperMethod
final MapperMethod mapperMethod = cachedMapperMethod(method);
//执行
return mapperMethod.execute(sqlSession, args);
}
//去缓存中找MapperMethod
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
//找不到才去new
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
@UsesJava7
private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
throws Throwable {
final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
.getDeclaredConstructor(Class.class, int.class);
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
final Class<?> declaringClass = method.getDeclaringClass();
return constructor
.newInstance(declaringClass,
MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
| MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
.unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
}
/**
* Backport of java.lang.reflect.Method#isDefault()
*/
private boolean isDefaultMethod(Method method) {
return ((method.getModifiers()
& (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC)
&& method.getDeclaringClass().isInterface();
}
}
invokeDefaultMethod
当使用的是java7即以上的jdk编译的时候执行此方法
invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {//如果方法定义所在的类和代理的对象相同的话调,就执行原来的方法
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//将方法放到缓存中
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
至于这个地方为什么要这样做,也不是很清楚
动态代理其实就是Java.lang.reflect.Proxy类动态的根据您指定的所有接口生成一个class byte,该class会继承Proxy类,并实现所有你指定的接口(您在参数中传入的接口数组);然后再利用您指定的classloader将 class byte加载进系统,最后生成这样一个类的对象,并初始化该对象的一些值,如invocationHandler,以即所有的接口对应的Method成员。
该类中的三个参数:
1. sqlSession:session会话
2. mapperInterface:映射器接口
3. methodCache:方法缓存
以上三个参数需要在构造器中进行赋值,首先session会话用于指明操作的来源,映射器接口指明操作的目标,方法缓存则用于保存具体的操作方法实例。在每个映射器代理中都存在以上三个参数,也就是说我们一旦我们使用过某个操作,那么这个操作过程中产生的代理实例将会一直存在,且具体操作方法会保存在这个代理实例的方法缓存中备用。
MapperProxy是使用JDK动态代理实现的代理功能,其重点就在invoke()方法中,首先过滤掉Object类的方法,然后从先从缓存中获取指定的方法,如果缓存中不存在则新建一个MapperMethod实例并将其保存在缓存中,如果缓存中存在这个指定的方法实例,则直接获取执行。
MapperProxyFactory:映射器代理工厂
package org.apache.ibatis.binding;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.ibatis.session.SqlSession;
/**
* @author Lasse Voss
*/
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
这个类中最重要的方法就是newInstance方法,这个方法构成了重载。
最终调用的是
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
此方法是通过传入一个自己的调用处理器,生成对应的代理对象
MapperRegistry
首先在注册器中会定义一个集合用于保存注册内容,这里是有HashMap:
java
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
上面的集合中键值的类型分别为Class类型与MapperProxyFactory类型,MapperProxyFactory是映射器代理工厂,通过这个工厂类可以获取到对应的映射器代理类MapperProxy,这里只需要保存一个映射器的代理工厂,根据工厂就可以获取到对应的映射器。
相应的,注册器中必然定义了添加映射器和获取映射器的方法来对外提供服务(供外部调取)。这里就是addMapper()方法与getMapper()方法,在该注册器中还有一种根据包名来注册映射器的方法addMappers()方法。因为该方法最后会调用addMapper()方法来完成具体的注册功能,所以我们直接从addMappers()说起。
这个类主要用来mapper的注册,我们就首先来看addMapper函数:
这个类会在Configuration类作为一个属性存在,在Configuration类初始化时进行初始化:
protected MapperRegistry mapperRegistry = new MapperRegistry(this);
addMapper
public void addMapper(Class<?> type) {
//因为Java的动态代理只能实现接口,因而在注册mapper时也只能注册接口
if (type.isInterface()) {
//如果已经注册过了,则抛出异常,而不是覆盖
if (knownMappers.contains(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
//先将这个mapper添加到mybatis中,如果加载过程中出现异常需要再将这个mapper从mybatis中删除
knownMappers.add(type);
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
//下面两行代码其实做了很多的事,由于涉及的东西较多,在此不做过多的描述,留待以后专门进行介绍
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
- 验证要添加的映射器的类型是否是接口,如果不是接口则结束添加,如果是接口则执行下一步
- 验证注册器集合中是否已存在该注册器(即重复注册验证),如果已存在则抛出绑定异常,否则执行下一步
- 定义一个boolean值,默认为false
- 执行HashMap集合的put方法,将该映射器注册到注册器中:以该接口类型为键,以以接口类型为参数调用MapperProxyFactory的构造器创建的映射器代理工厂为值
- 然后对使用注解方式实现的映射器进行注册(一般不使用)
- 设置第三步的boolean值为true,表示注册完成
- 在finally语句块中对注册失败的类型进行清除
getMapper
向外提供生成代理对象的函数:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//如果不存在这个mapper,则直接抛出异常
if (!knownMappers.contains(type))
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
try {
//返回代理类
return MapperProxy.newMapperProxy(type, sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
从集合中获取指定接口类型的映射器代理工厂,然后使用这个代理工厂创建映射器代理实例并返回,那么我们就可以获取到映射器的代理实例。
小结
- 在Mybatis提供的编程接口中,开发人员只需要定义好Mapper接口(如:UserDao),开发人员无需去实现。Mybatis会利用JDK的动态代理实现 Mapper接口。
- 在Mybatis中,每个Mapper接口都会对应一个MapperProxyFactory对象实例,这个对应关系在Configuration.mapperRegistry.knownMappers中。
- 当getMapper()方法被调用时,Mybatis会找到相对应的MapperProxyFactory对象实例,利用这个工厂来创建一个jdk动态代理对象,是这个Mapper接口的实现类,当Mapper定义的方法被调用时,会调用MapperProxy来处理。
- MapperProxy会根据方法找到对应的MapperMethod对象来实现这次调用。
- MapperMethod对应会读取方法中的注解,从Configuration中找到相对应的MappedStatement对象,再执行
映射方法单独定义,是因为这里并不存在一个真正的类和方法供调用,只是通过反射和代理的原理来实现的假的调用,映射方法是调用的最小单位(独立个体),将映射方法定义之后,它就成为一个实实在在的存在,我们可以将调用过的方法保存到对应的映射器的缓存中,以供下次调用,避免每次调用相同的方法的时候都需要重新进行方法的生成。很明显,方法的生成比较复杂,会消耗一定的时间,将其保存在缓存集合中备用,可以极大的解决这种时耗问题。
即使是在一般的项目中也会存在很多的映射器,这些映射器都要注册到注册器中,注册器集合中的每个映射器中都保存着一个独有的映射器代理工厂实例,而不是映射器实例,映射器实例只在需要的时候使用代理工厂进行创建,所以我们可以这么来看,MapperProxyFactory会存在多个实例,针对每个映射器有一个实例,这个实例就作为值保存在注册器中,而下一节中的MapperProxy被MapperProxyFactory调用来生成代理实例,同样也是与映射器接口一一对应的存在(即存在多个实例,只不过这个实例只会在需要的时候进行创建,不需要的时候是不存在的)。