java 动态代理代理以及mybatis是如何用动态代理执行没有实现的接口解析
java中的东岱代理机制是动态生成代理接口的实例类,在代理类的invoke方法中可以在实际方法执行时做用户自定义的一些操作,spring中的aop就是用这个原理来执行的。
先看看普通的动态代理是怎么使用的。
1、先声明一个接口
package testmybatis; public interface IFruit { void eat(); void shape(); }
2、生成实现类
package testmybatis; public class Apple implements IFruit { public void eat() { // TODO Auto-generated method stub System.out.println("吃了一个苹果"); } public void shape() { // TODO Auto-generated method stub } }
3、编写动态代理类
package testmybatis; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import javax.swing.plaf.basic.BasicComboPopup.InvocationKeyHandler; public class MyProxy implements InvocationHandler { Object target; public MyProxy(Object target){ this.target=target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 此处为方法实际执行的地方,当然你也可以根本就不执行此方法,mybatis就是根本不管这里的方法,他需要的只是方法名,用方法名来找到对应的sql语句,参数,返回值的信息 Object result=method.invoke(target, args); return result; } }
4、测试动态代理
public void testApp() { // MyProxy proxy=new MyProxy(new Apple()); IFruit app=(IFruit)Proxy.newProxyInstance(IFruit.class.getClassLoader(), new Class[] {IFruit.class}, proxy); app.eat(); }
这样app就是通过动态代理生成的IFruit的实现类了,这个类就是经过代理有的apple。现在实现类执行方法的时候其实就是在调用
代理的Object result=method.invoke(target, args)这段代码,其中target是我们自己实现的一个实现类,如果要在这里确实执行方法,那么就必须传入一个实现类。但是mybatis根本就不需要执行接口的方法,所以这里可以不用传入类的实例。
接下来看看mybatis的实现方式吧,我们只看他核心的生成部分代码
mybatis中的org.apache.ibatis.binding.MapperRegistry类里有一个getMapper方法,代码如下:
@SuppressWarnings("unchecked") public <T> T getMapper(Class<T> type, SqlSession sqlSession) { final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) 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); } }
这里从MapperProxyFactory中得到了代理后类。点击mapperProxyFactory.newInstance(sqlSession),看他是如何执行的。
@SuppressWarnings("unchecked") protected T newInstance(MapperProxy<T> mapperProxy) {
//用动态代理生成实现类,mapperProxy就是代理类的实例
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) {
//实例化proxy类 final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }
先调了 public T newInstance(SqlSession sqlSession) 这个方法,这个方面又调用了上面的 protected T newInstance(MapperProxy<T> mapperProxy),这里就是真正生实现类的地方了。
(T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy)
这句话生成了实现类,其中 mapperInterface是mapper接口,mapperproxy是动态代理类,我们所有的sql查询都是通过他来完成。我们看看他的代码。
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; /** * @author Clinton Begin * @author Eduardo Macarron */ 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; } 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) { throw ExceptionUtil.unwrapThrowable(t); } } final MapperMethod mapperMethod = cachedMapperMethod(method);
//执行真正的查询 return mapperMethod.execute(sqlSession, args); } 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; } }
代码不多也不复杂,看invoke方法。
//如果传进来的 object对象就执行相应的发布方法
if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } }
//真正执行的sql查询的地方,sqlsession封装了数据库已经需要执行的语句等信息,args包含了执行所需要的参数 final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args);
以上就是mybatis如果通过接口实现查询的一个过程,当然,动态代理生成实现类并不是在每次请求时,而是在一开始初始化时就已经生成并缓存了起来,核心的流程其实就是通过动态代理来完成查询动作的。
当然里面还有很多配置文件解析xml解析缓存等工作需要做,不过了解了整个流程以后就可以慢慢一步一步的去看他是怎么完成的了