我们都知道mybatis操作数据库的都是通过调用DAO接口中的方法,但是这是一个空方法,具体实现是怎么的,然后就想仔细看一下,发现使用了java动态代理来实现的。
先看一下mybatis中调用DAO中方式的大概过程,这里不详细介绍mybatis的内部细节,主要介绍和代理相关的部分。
首先从demo开始,这是一个简单的mybatis示例
ini
public static void main(String[] args) throws IOException {
// 获取配置文件
String resource = "mybatis/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 构建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
//User user = sqlSession.selectOne("com.ais.vaaa.test.mybatis.UserDao.selectUser", 1);
UserDao userDao = sqlSession.getMapper(UserDao.class);
User user = userDao.selectUser(1L);
System.out.println(user);
} finally {
sqlSession.close();
}
}
首先获取DAO
ini
UserDao userDao = sqlSession.getMapper(UserDao.class);
MapperRegistry.java#getMapper
ini
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
... ... ...
return mapperProxyFactory.newInstance(sqlSession);
获取MapperProxyFactory,MapperProxyFactory是mybatis初始化的时候为每个DAO(mapper)生成的一个工厂对象,通过工厂类获取DAO
MapperProxyFactory.java
typescript
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
这里会发现使用了jdk动态代理Proxy.newProxyInstance生成代理类,它的InvocationHandler实现类是MapperProxy.java
添加图片注释,不超过 140 字(可选)
这里看到userDao是一个对象并且名称中带有$Proxy
添加图片注释,不超过 140 字(可选)
这是生成的代理类,实现了UserDao接口,并实现了selectUser方法,具体selectUser方式是怎么处理,这里就不深入了,主要是在MapperProxy的invoke中来处理。
以上就是mybatis中使用到动态代理的一种场景,下面我们再说下jdk中的的动态代理是如何做的。
通过简单的示例来了解动态代理,示例中定义了一个接口(IHello.java),接口的实现(HelloImpl.java),一个代理类(HelloProxy.java)和入口类(Main.java)。通过代理类在sayHello方法调用前后加上日志
输出结果
erlang
before call method...
Hello hello world
after call method...
IHello.java
arduino
public interface IHello {
void sayHello(String name);
}
HelloImpl.java
typescript
public class HelloImpl implements IHello {
@Override
public void sayHello(String name) {
System.out.println("Hello " + name);
}
}
HelloProxy.java
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class HelloProxy implements InvocationHandler {
private Object delegate;
public Object bind(Object delegate) {
this.delegate = delegate;
Object obj = Proxy.newProxyInstance(
this.delegate.getClass().getClassLoader(), this.delegate
.getClass().getInterfaces(), this);
// 也可以直接使用接口,不用实现类,mybatis就是这样
// Object obj = Proxy.newProxyInstance(
// cls.getClassLoader(), new Class[] { cls }, this);
return obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result = null;
try {
System.out.println("before call method...");
result = method.invoke(this.delegate, args);
System.out.println("after call method...");
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
Main.java
java
public class Main {
static public void main(String[] arg) {
// jdk proxy 生成的代理类
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
// cglib 生成的代理类
//System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\class");
HelloProxy helloproxy = new HelloProxy();
HelloImpl hello = new HelloImpl();
IHello ihello = (IHello) helloproxy.bind(hello);
ihello.sayHello("hello world");
}
}
动态代理如何实现
通过Proxy.newProxyInstance重新生成一个新的代理类
csharp
public Object bind(Object delegate) {
this.delegate = delegate;
Object obj = Proxy.newProxyInstance(
this.delegate.getClass().getClassLoader(), this.delegate
.getClass().getInterfaces(), this);
return obj;
}
我们进入Proxy.newProxyInstance方法查看
scss
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
... ...
继续跟来到 java.lang.reflect.Proxy$ProxyClassFactory#apply方法,通过此方法生成了一个动态类
ini
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
生成的动态类可以通过设置System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");生成到本地,如下:
typescript
public final class $Proxy0 extends Proxy implements IHello {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler paramInvocationHandler) {
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject) {
try {
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
} catch (Error|RuntimeException error) {
throw null;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void sayHello(String paramString) {
try {
this.h.invoke(this, m3, new Object[] { paramString });
return;
} catch (Error|RuntimeException error) {
throw null;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString() {
try {
return (String)this.h.invoke(this, m2, null);
} catch (Error|RuntimeException error) {
throw null;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode() {
try {
return ((Integer)this.h.invoke(this, m0, null)).intValue();
} catch (Error|RuntimeException error) {
throw null;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("com.ais.vaaa.test.invocationhandler.IHello").getMethod("sayHello", new Class[] { Class.forName("java.lang.String") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
} catch (NoSuchMethodException noSuchMethodException) {
throw new NoSuchMethodError(noSuchMethodException.getMessage());
} catch (ClassNotFoundException classNotFoundException) {
throw new NoClassDefFoundError(classNotFoundException.getMessage());
}
}
}
最终调用流程:
添加图片注释,不超过 140 字(可选)