代理是一种模式,提供了对目标对象的间接访问方式,即通过代理访问目标对象。如此便于在目标实现的基础上增加额外的功能操作,前拦截,后拦截等,以满足自身的业务需求,同时代理模式便于扩展目标对象功能的特点也为多人所用。
JDK 本身提供的动态代理只能代理接口。java 中的动态代理位于java.lang.reflect 包下,主要涉及两个类:
1. Interface InvocationHandler
该接口中仅定义了一个方法
public object invoke(Object obj,Method method, Object[] args)。这个抽象方法在代理类中动态实现。
参数 | 功能 |
---|---|
Object obj | 被代理的对象 |
Method method | 是被代理的方法 |
Object[] args | 为该方法的参数数组 |
2.Proxy
该类即为动态代理类,其中主要包含以下内容:
protected Proxy(InvocationHandler h)
构造函数,用于给内部的h赋值。
static Class getProxyClass (ClassLoaderloader, Class[] interfaces)
获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。
static Object newProxyInstance(ClassLoad load,Class[] interfaces, InvocationHandler h)
返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)
实现步骤
1、通过实现 InvocationHandler 接口创建自己的调用处理器;
2、通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
3、通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
4、通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
public class Test{
interface Subject {
public void sayHi();
public void sayHello();
}
static class SubjectImpl implements Subject{
public void sayHi() {
System.out.println("hi");
}
public void sayHello() {
System.out.println("hello");
}
}
static class ProxyInvocationHandler implements InvocationHandler {
private Subject target;
public ProxyInvocationHandler(Subject target) {
this.target=target;
}
// @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.print("say:");
return method.invoke(target, args);
}
}
public static void main(String[] args) {
Subject subject=new SubjectImpl();
Subject subjectProxy=(Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), new ProxyInvocationHandler(subject));
subjectProxy.sayHi();
subjectProxy.sayHello();
/**
* 结果
* say:hi
* say:hello
*/
}
}
JDK 的代理类
/**
*
* JDK动态代理类
*
*
*/
public class JDKProxy implements InvocationHandler {
private Object targetObject;//需要代理的目标对象
public Object newProxy(Object targetObject) {//将目标对象传入进行代理
this.targetObject = targetObject;
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(), this);//返回代理对象
}
public Object invoke(Object proxy, Method method, Object[] args)//invoke方法
throws Throwable {
before();
Object ret = null; // 设置方法的返回值
ret = method.invoke(targetObject, args); //invoke调用需要代理的方法
after();
return ret;
}
private void before() {//方法执行前
System.out.println("方法执行前 !");
}
private void after() {//方法执行后
System.out.println("方法执行后");
}
}
newProxyInstance方法执行了以下几种操作。
public final class $Proxy0 extends Proxy
implements Test.Subject
{
private static Method m4;
private static Method m1;
private static Method m3;
private static Method m0;
private static Method m2;
static
{
try {
m4 = Class.forName("Test$Subject").getMethod("sayHello", new Class[0]);
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("Test$Subject").getMethod("sayHi", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public $Proxy0(InvocationHandler paramInvocationHandler)
{
super(paramInvocationHandler);
}
public final void sayHello()
{
try
{
this.h.invoke(this, m4, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final boolean equals(Object paramObject)
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void sayHi()
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
}
我们可以看到代理类内部实现比较简单,在调用每个代理类每个方法的时候,都用反射去调newProxyInstance方法中传来的h的invoke方法(也就是我们自定义的InvocationHandler的子类中重写的invoke方法),用参数传递了代理类实例、接口方法、调用参数列表,这样我们在重写的invoke方法中就可以实现对所有方法的统一包装了。
1.Mapper 是怎么添加进去的呢
DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory,dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(OrdersMapper1.class);// 添加Mapper接口SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
等价于
String resource = "mybatis-config.xml"; // xml内容就不贴了
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
我们后面的 sqlSessionFactory采用以上方式上面生成sqlSessionFactory 的过程中有这么一行 configuration.addMapper(OrderMapper.class); 添加了一个Mapper 接口
addMapper()方法的实现
Configuration类:
public void addMapper(Class type) {
mapperRegistry.addMapper(type);
}
从代码中我们可以看出mapper 实际是被添加进mapperRegistry 中去了,我们继续研究mapperRegistry 类。
final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
public void addMapper(Class type) {
if (type.isInterface()) {// 只添加接口
if (hasMapper(type)) {// 不允许重复添加
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory(type));// 注意这里
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
mapperRegistry里面有一个私有的HashMap 就是knownMappers,它的key 为当前的class 对象,value 为一个MapperProxyFactory()实例
总结:Mapper 接口被添加进了MapperReqister 里面的一个HashMap 中去了,并且以Mapper接口的class 对象作为key,以一个携带mepper 接口作为属性的MapperProxyFactory 作为value,MapperProxyFactory 从名字来看好像是一个工厂,用来创建mapper proxy 的工厂。
2.mapper 是怎样获取的呢
从源码来看依然是调用configuration 的getMapper()方法
DefaultSqlsession 类:
public T getMapper(Class type) {
return configuration.getMapper(type, this);
}
configuration 类里面存放了所有关于xml 文件对的配置信息,从参数上看它要我们传入一个class 类型,这已经能看到后面一定要用到反射机制和动态代理
Configuration类:
public T getMapper(Class type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
MapperRegister 类
private final Map, MapperProxyFactory>> knownMappers = new HashMap, MapperProxyFactory>>();
public T getMapper(Class type, SqlSession sqlSession) {
final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession); //重点看这里
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
我们调用的session.getMapper(order.class)最终会到这里,这个方法根据order的class对象,以它为key在knowMappers 中找到了对应的value——MapperProxyFactory(BlogMapper) 对象,然后调用这个对象的newInstance()方法。根据这个名字,我们就能猜到这个方法是创建了一个对象
MapperProxyFactory()
public class MapperProxyFactory {
//映射器代理工厂
private final Class mapperInterface;
private final Map methodCache = new ConcurrentHashMap(); //删除不必要代码
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy mapperProxy) {
//使用了JDK自带的动态代理生成映射器代理类的对象
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
最终是通过Proxy.newProxyInstance产生了一个OrdersMapper1的代理对象。Mybatis 为了完成 Mapper 接口的实现,运用了代理模式。具体是使用了JDK动态代理,这个Proxy.newProxyInstance方法生成代理类的三个要素是:
代理模式中,代理类(MapperProxy)中才真正的完成了方法调用的逻辑。我们贴出MapperProxy的代码
public class MapperProxy implements InvocationHandler, Serializable {
//实现了InvocationHandler接口
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
//代理以后,所有Mapper的方法调用时,都会调用这个invoke方法
try {
return method.invoke(this, args);// 注意1
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
final MapperMethod mapperMethod = cachedMapperMethod(method); // 使用了缓存
//执行CURD
return mapperMethod.execute(sqlSession, args);// 注意2
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
}
Blog blog =mapper.getOrdersById(3); 实际上最后是会调用这个MapperProxy的invoke方法。这段代码中,if 语句先判断,我们想要调用的方法是否来自Object类,这里的意思就是,如果我们调用toString()方法,那么是不需要做代理增强的,直接还调用原来的method.invoke()就行了。只有调用getOrdersById之类的方法的时候,才执行增强的调用——即mapperMethod.execute(sqlSession, args);
而mapperMethod.execute(sqlSession, args);这句最终就会执行增删改查了
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
}
// 删除部分代码
}
// 删除部分代码
return result;
}
我们分析了 Mapper接口是如何注册的,Mapper接口是如何产生动态代理对象的,Maper接口方法最终是如何执行的。总结起来主要就是这几个点: