最近在看 Mybatis 时,对mapper怎么映射到dao层联想到前公司内部的.net ORM框架,研究过.net ORM框架
在来看看 java 里面的mybatis是大致怎么样的一个实现原理,来简答解释一下java里面的动态代理机制
Mapper的XML文件和Mapper的接口,在使用过程中,我们往往只需要实例化一个Mapper对象,然后调用对象里面的接口,即可执行XML里面的sql,这中间Mapper肯定是有一个代理类,去帮助我们找到XML文件里面的SQL。
JDK动态代理
是什么?
动态代理可以将一个类里面的不同的方法统一集中到一个方法里面去执行,做一些其他的事情,对于调用者是感受不到,主要是使用了反射获取到一个接口类里面的所有公有或私有方法,来针对这些方法去创建动态代理对象。
JDK动态代理在java.lang.reflect.*这个包下,实现JDK动态代理主要涉及到两个类
1.InvocationHandler:这个接口中只有一个方法,invoke这个方法第一个参数proxy是代理类的实例,method是需要代理的方法,args是该方法的参数,
2.Proxy:动态代理类,代理类需要实现InvocationHandler这个类,必须实现invoke这个方法
动态代理实现Mybatis简单功能
主方法
public static void main(String[] args) { MySqlsession sqlsession=new MySqlsession(); /*** * 调用getMapper的时候,就会返回一个UserMapper的动态代理对象 * 当需要调用UserMapper类里面的任何方法的时候,都会被代理到MyMapperProxy的invoke方法里面 * */ UserMapper mapper = sqlsession.getMapper(UserMapper.class); User user = mapper.getUserById("2"); System.out.println(user); }
在去看看getMapper方法里面
/*** * 动态代理调用 * @param clas * @param <T> * @return */ @SuppressWarnings("unchecked") public <T> T getMapper(Class<T> clas){ /*** * clas 一个classLoader对象,定义由哪个ClassLoader对象生成的代理类进行加载 * 一个interfaces的接口数组,提供newProxyInstance,代理类就会实现这个接口的所有方法 * InvocationHandler对象,当动态代理调用到接口里面的方法时,会关联到哪个invocaionHandler对象,由里面的invoke去处理 */ return (T)Proxy.newProxyInstance(clas.getClassLoader(),new Class[]{clas}, new MyMapperProxy(myConfiguration,this)); }
通过Proxy.newProxyInstance去新建了一个动态代理类
实例了该动态代理类的接口,在调用的过程中,会首先进入到invoke方法里面,去由invoke统一执行
/***
* 实现InvocationHandler需要重写invoke
*
* 这里面通过反射会去获取到UserMapper.xml里面的所有查询,放到MapperBean里面
* 根据外层需要调用的方法名去遍历MapperBean里面的名字,在去根据不同的方法执行不同的SQL
* @param proxy 代理对象
* @param method 代理对象执行的方法
* @param args 代理对象执行方法的参数
* @return
* @throws Throwable
*/@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("进入invoke");
MapperBean readMapper = myConfiguration.readMapper("UserMapper.xml");
//是否是xml文件对应的接口
if(!method.getDeclaringClass().getName().equals(readMapper.getInterfaceName())){
return null;
}
List< function > list = readMapper.getList();
if(null != list || 0 != list.size()){
for (Function function : list) {
//id是否和接口方法名一样
if(method.getName().equals(function.getFuncName())){
return mySqlsession.selectOne(function.getSql(), String.valueOf(args[0]));
}
}
}
return null;
}
能做什么?
一般在业务上面很少会使用到动态代理,但是理解动态代理之后很容易帮助我们理解一些Spring内部的一些机制,包括Mybatis的部分模块
AOP
AOP面向切面编程 即在一个增强的方法前后加上一些需要特殊处理的事情,使用动态代理,在执行方法的前后加上需要增强的事情
RPC
RPC远程过程调用,在调用远程方法时,就像调用平常的方法一样,只是调用者不用关心调用的方法在哪,和具体的实现,这中间的事由一个处理中心去处理,在由处理中心去调用具体的实现方法
举一个WCF的例子,在web端调用服务端的方法,只用配置服务的路径,以及服务使用的协议,通过注册中心去分配任务