动态代理的作用
我们都知道,spring的面向切面编程默认由jdk动态代理和cglib动态代理实现,使用动态代理我们可以无侵入的实现切面编程,比如日志管理、权限管理、事务管理等。jdk动态代理是面向接口的,cglib是面向普通类。弄明白了这两种动态代理实现原理也就懂了spring的aop编程。
jdk动态代理说明
jdk动态代理是面向接口的,只能对接口生成代理类。我曾经以为这个接口必须有实现类,其实这是错误的。接口实现是否存在是根据业务需求而来的。
jdk动态代理实现方法
1、目标接口、目标接口实现类
2、自定义的InvocationHandler
public interface UserService {
public void methodA();
}
public class UserServiceImpl implements UserService {
public void methodA() {
System.out.println("UserServiceImpl methodA()...");
}
}
public class MyInvocationHandler implements InvocationHandler {
public MyInvocationHandler() {
super();
}
public MyInvocationHandler(Object obj) {
super();
this.obj = obj;
}
private Object obj;
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("执行前....");
Object value = method.invoke(obj, args);
System.out.println("执行后");
return value;
}
import java.lang.reflect.Proxy;
import com.interceptor.MyInvocationHandler;
import com.jun.UserService;
import com.jun.impl.UserServiceImpl;
public class TestJDKDynamicProxy {
public static void main(String[] args) {
MyInvocationHandler h = new MyInvocationHandler(new UserServiceImpl());
Class<?>[] interfaces ={UserService.class};
UserService service = (UserService) Proxy.newProxyInstance(MyInvocationHandler.class.getClassLoader(), interfaces, h);
service.methodA();
System.out.println(service.getClass());
}
}
输出结果
执行前....
UserServiceImpl methodA()...
执行后
class com.sun.proxy.$Proxy0
jdk动态代理原理
切面编程的效果出来了,在执行方法前和执行方法后,执行了逻辑操作,这里,我们可以做事务开启、关闭,日志的记录等。但背后的原理是什么?
通过对目标接口生成代理类,代理类会继承Proxy并实现目标接口,代理类的所有方法都会调用定制的invocationhandler的invoke方法,在定制InvocationHandler中调用目标类方法, 从而实现切面编程。
我们注意到打印出来的UserService类是com.sun.Proxy.$Proxy0,而不是UserService。通过追踪Proxy.newProxyInstance方法,我们发现有个getProxyClass0方法
Class<?> cl = getProxyClass0(loader, interfaces);
这个方法生成了代理类信息,进入方法发现,是这一行代码生成字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces);
由于jdk是不开源的,所以我们需要下载openjdk查看源代码
/**
* Generate a proxy class given a name and a list of proxy interfaces.
*/
public static byte[] generateProxyClass(final String name,
Class[] interfaces)
{
ProxyGenerator gen = new ProxyGenerator(name, interfaces);
final byte[] classFile = gen.generateClassFile();
if (saveGeneratedFiles) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
try {
FileOutputStream file =
new FileOutputStream(dotToSlash(name) + ".class");
file.write(classFile);
file.close();
return null;
} catch (IOException e) {
throw new InternalError(
"I/O exception saving generated file: " + e);
}
}
});
}
return classFile;
}
我们发现saveGeneratedFiles控制是否把代理类保存到磁盘上
private final static boolean saveGeneratedFiles =
java.security.AccessController.doPrivileged(
new GetBooleanAction(
"sun.misc.ProxyGenerator.saveGeneratedFiles")).booleanValue();
现在已经确定是ProxyGenerator.generateProxyClass生成代理类,既然已经找到"真凶",我们就生成代理类看看吧。
import sun.misc.ProxyGenerator;
public class TestJDKDynamicProxy {
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
Class<?>[] interfaces ={UserService.class};
ProxyGenerator.generateProxyClass(
"C:\\Users\\DELL\\Desktop\\$Proxy1", interfaces);
}
}
注意,传入的属性值是字符串"true",而不是布尔值true,传入布尔值true不会保存到磁盘
通过java反编译工具jad(java decomplier)反编译$Proxy1生成jad文件,打开后如下所示
import java.lang.reflect.*;
public final class C_3A__5C_Users_5C_DELL_5C_Desktop_5C_$Proxy1 extends Proxy
implements UserService
{
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
Class.forName("java.lang.Object")
});
m4 = Class.forName("com.jun.UserService").getMethod("methodB", new Class[0]);
m3 = Class.forName("com.jun.UserService").getMethod("methodA", 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(NoSuchMethodException nosuchmethodexception)
{
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
}
catch(ClassNotFoundException classnotfoundexception)
{
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
private static Method m3;
public C_3A__5C_Users_5C_DELL_5C_Desktop_5C_$Proxy1(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
public final void methodA()
{
try
{
super.h.invoke(this, m3, null);
return;
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
}
我们可以看到代理类$Proxy1,实现了指定接口UserService并继承了Proxy,执行methodA时,会调用父类的invocationHandler,而这个InvocationHandler就是在创建代理类对象时,传入的MyInvocationHandler
Class<?> cl = getProxyClass(loader, interfaces);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
Constructor cons = cl.getConstructor(constructorParams);
return cons.newInstance(new Object[] { <strong><span style="font-size:18px;color:#ff0000;">h</span></strong> });
我们再回过头来回顾一下事情的经过,首先执行代理的methodA方法,然后调用MyInvocationHandler.invoke方法,首先进行方法前逻辑处理,然后执行UserServiceImpl的methodA方法,然后执行方法后逻辑处理。
注意:
1、目标接口不是必须有实现类的,比如Mybatis的mapper,mapper都是接口,但是没有实现类。Mybatis的InvocationHandler就是MapperProxy,在invoke方法中方法里执行sql。