动态代理的解释本文不再赘述,在许许多多的框架代码中,我们都可以看到动态代理的应用,重要性可见一斑,理解动态代理,对于我们理解各种框架的原理具有重要意义。下文将从动态代理常见的几种方式来剖析。
jdk动态代理
使用jdk动态代理的步骤
1.通过实现InvocationHandler接口来自定义自己的InvocationHandler;
2.通过Proxy.newProxyInstance获得动态代理类
3.通过代理对象调用目标方法
下面以一个具体demo来演示:
1.设置
设置idea启动vm options: -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true(会保存生成的动态代理类到项目下)
2.测试代码
public interface User {
void sayHello();
}
public class Man implements User {
@Override
public void sayHello() {
System.out.println("hello,i am a man");
}
}
public class UserHandler implements InvocationHandler {
//代理的目标类
private Object target;
//通过构造方法注入目标类
public UserHandler(Object target) {
this.target = target;
}
@Override
/**
* proxy:代理类
* method:代理的方法
* args:方法参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("调用前...");
Object object = method.invoke(target, args);
System.out.println("调用后...");
return object;
}
}
public class ProxyTest {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
User user = new Man();
User userProxy = (User)Proxy.newProxyInstance(user.getClass().getClassLoader(),user.getClass().getInterfaces(),new UserHandler(user));
userProxy.sayHello();
}
}
3.控制台打印结果结果:
调用前...
hello,i am a man
调用后...
我们先看结果,找到生成的动态代理类反编译看下源码:
package com.sun.proxy;
import com.company.User;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements User {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void sayHello() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
m3 = Class.forName("com.company.User").getMethod("sayHello", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
从生成的动态代理类我们可以看出代理类之所以能实现代理功能,其实是实现了User接口,在实现方法中,通过super.h 也就是之前我们实现的 InvocationHandler 来调用被代理类的方法。InvocationHandler实际上就是一个中间的代理,我们在创建代理对象时,就把接口的实例通过构造方法传给了它,它拥有接口实例对象,就可以操作接口的方法了。这其实和装饰者模式是一个道理。你可以理解成我们生成的动态代理类对于具体的方法执行,其实是交给了它,由它来执行具体的方法。这里也理解了,我们经常说的 jdk的动态代理是基于接口的动态代理,是通过实现目标接口来实现代理。
下面看一下Proxy.newProxyInstance的源码:
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);
}
/*
* (核心): 生成代理类的Class对象
*/
Class<?> cl = getProxyClass0(loader, intfs);
try {
if (sm != null) {
//安全校验
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//反射获取构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
//判断构造方法权限修饰符.如果没有权限,强制访问
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//通过构造方法创建对象。h是InvocationHandler,代理对象的处理器
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
走读以上代码,可以看见,jdk的动态代理,其实就是首先通过动态生成字节码的方式,然后通过字节码拿到构造方法,最后实例化对象。
核心流程就是动态生成字节码的过程,有兴趣的同学可以沿着getProxyClass0 这个方法继续点下去,这里不再过多说明。这里推荐一篇源码分析的博客:深度解析jdk动态代理:https://blog.csdn.net/qq_26026985/article/details/118892358
问题1: 怎么样操作字节码?
通常方式有两种.第一种是ASM框架,第二种方式是javassist框架.
ASM框架是指令层面的框架,使用技术要求高,必须懂得JVM指令和字节码.运行速度更快,spring的aop默认使用的cglib代理就是基于ASM。
javassist框架是高度封装的框架,使用门槛低,不需懂JVM.但是据说运行速度偏慢(笔者没有验证过),dubbo中的动态代理默认就是使用javassist。
问题二:jdk中动态代理是如何实现的呢?
它既没有采用ASM框架,也没有采用javassist框架.它简单粗暴按照JVM字节码规范,强行用字节流进行写入.整个过程,非常原始和底层.没有进行任何过多的二次封装.
总结就是jdk的动态代理通过动态生成字节码和反射相结合来生成代理对象。
CGLIB动态代理
先来动手写一个demo
1.先引入jar包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
2.测试代码
public interface IUserService {
void sayHello();
}
public class UserServiceImpl implements IUserService{
@Override
public void sayHello() {
System.out.println("hello");
}
}
/**
* 拦截器,代理方法就是通过此拦截器实现调用目标对象的方法
* Object obj为目标对象
* Method method为目标方法
* Object[] params 为参数,
* MethodProxy proxy CGlib方法代理对象
*/
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("调用前");
Object result = proxy.invokeSuper(obj,args);
System.out.println(" 调用后"+result);
return result;
}
}
public static void main(String[] args) {
//保存生成的动态代理类
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/zhangxiaohu/workspace/openSourceProject/transactiondemo");
Enhancer enhancer = new Enhancer();
//设置代理的父类
enhancer.setSuperclass(UserServiceImpl.class);
/**
*定义一个拦截器。在调用目标方法时,CGLib会回调MethodInterceptor接口方法拦截,
* 来实现你自己的代理逻辑,类似于JDK中的InvocationHandler接口。
*/
enhancer.setCallback(new MethodIntercepter());
//生成动态代理类
UserServiceImpl proxy = (UserServiceImpl)enhancer.create();
proxy.sayHello();
}
3.结果
当我们跑起来此测试代码时,控制台打印:
CGLIB debugging enabled, writing to '/Users/zhangxiaohu/workspace/openSourceProject/transactiondemo'
调用前
hello
调用后
然后我们找到目标路径下的代理类,反编译看一下代理类:
package com.example.transactiondemo.service;
import com.example.transactiondemo.service.UserServiceImpl;
import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* 代理类继承了UserServiceImpl
*/
public class UserServiceImpl$$EnhancerByCGLIB$$2772de75 extends UserServiceImpl implements Factory {
private boolean CGLIB$BOUND;
public static Object CGLIB$FACTORY_DATA;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static Object CGLIB$CALLBACK_FILTER;
private static final Method CGLIB$sayHello$0$Method;
private static final MethodProxy CGLIB$sayHello$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$equals$1$Method;
private static final MethodProxy CGLIB$equals$1$Proxy;
private static final Method CGLIB$toString$2$Method;
private static final MethodProxy CGLIB$toString$2$Proxy;
private static final Method CGLIB$hashCode$3$Method;
private static final MethodProxy CGLIB$hashCode$3$Proxy;
private static final Method CGLIB$clone$4$Method;
private static final MethodProxy CGLIB$clone$4$Proxy;
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("com.example.transactiondemo.service.UserServiceImpl$$EnhancerByCGLIB$$2772de75");
Class var1;
Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$equals$1$Method = var10000[0];
CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
CGLIB$toString$2$Method = var10000[1];
CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
CGLIB$hashCode$3$Method = var10000[2];
CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");
CGLIB$clone$4$Method = var10000[3];
CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
CGLIB$sayHello$0$Method = ReflectUtils.findMethods(new String[]{"sayHello", "()V"}, (var1 = Class.forName("com.example.transactiondemo.service.UserServiceImpl")).getDeclaredMethods())[0];
CGLIB$sayHello$0$Proxy = MethodProxy.create(var1, var0, "()V", "sayHello", "CGLIB$sayHello$0");
}
final void CGLIB$sayHello$0() {
super.sayHello();
}
/**
* 重写了父类方法,并且定义成final类型(子类不可继承)
*/
public final void sayHello() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if(this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if(var10000 != null) {
var10000.intercept(this, CGLIB$sayHello$0$Method, CGLIB$emptyArgs, CGLIB$sayHello$0$Proxy);
} else {
super.sayHello();
}
}
final boolean CGLIB$equals$1(Object var1) {
return super.equals(var1);
}
public final boolean equals(Object var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if(this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if(var10000 != null) {
Object var2 = var10000.intercept(this, CGLIB$equals$1$Method, new Object[]{var1}, CGLIB$equals$1$Proxy);
return var2 == null?false:((Boolean)var2).booleanValue();
} else {
return super.equals(var1);
}
}
final String CGLIB$toString$2() {
return super.toString();
}
public final String toString() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if(this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null?(String)var10000.intercept(this, CGLIB$toString$2$Method, CGLIB$emptyArgs, CGLIB$toString$2$Proxy):super.toString();
}
final int CGLIB$hashCode$3() {
return super.hashCode();
}
public final int hashCode() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if(this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if(var10000 != null) {
Object var1 = var10000.intercept(this, CGLIB$hashCode$3$Method, CGLIB$emptyArgs, CGLIB$hashCode$3$Proxy);
return var1 == null?0:((Number)var1).intValue();
} else {
return super.hashCode();
}
}
final Object CGLIB$clone$4() throws CloneNotSupportedException {
return super.clone();
}
protected final Object clone() throws CloneNotSupportedException {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if(this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null?var10000.intercept(this, CGLIB$clone$4$Method, CGLIB$emptyArgs, CGLIB$clone$4$Proxy):super.clone();
}
public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
String var10000 = var0.toString();
switch(var10000.hashCode()) {
case -508378822:
if(var10000.equals("clone()Ljava/lang/Object;")) {
return CGLIB$clone$4$Proxy;
}
break;
case 1535311470:
if(var10000.equals("sayHello()V")) {
return CGLIB$sayHello$0$Proxy;
}
break;
case 1826985398:
if(var10000.equals("equals(Ljava/lang/Object;)Z")) {
return CGLIB$equals$1$Proxy;
}
break;
case 1913648695:
if(var10000.equals("toString()Ljava/lang/String;")) {
return CGLIB$toString$2$Proxy;
}
break;
case 1984935277:
if(var10000.equals("hashCode()I")) {
return CGLIB$hashCode$3$Proxy;
}
}
return null;
}
public UserServiceImpl$$EnhancerByCGLIB$$2772de75() {
CGLIB$BIND_CALLBACKS(this);
}
public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
CGLIB$THREAD_CALLBACKS.set(var0);
}
public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
CGLIB$STATIC_CALLBACKS = var0;
}
private static final void CGLIB$BIND_CALLBACKS(Object var0) {
UserServiceImpl$$EnhancerByCGLIB$$2772de75 var1 = (UserServiceImpl$$EnhancerByCGLIB$$2772de75)var0;
if(!var1.CGLIB$BOUND) {
var1.CGLIB$BOUND = true;
Object var10000 = CGLIB$THREAD_CALLBACKS.get();
if(var10000 == null) {
var10000 = CGLIB$STATIC_CALLBACKS;
if(CGLIB$STATIC_CALLBACKS == null) {
return;
}
}
var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
}
}
public Object newInstance(Callback[] var1) {
CGLIB$SET_THREAD_CALLBACKS(var1);
UserServiceImpl$$EnhancerByCGLIB$$2772de75 var10000 = new UserServiceImpl$$EnhancerByCGLIB$$2772de75();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
}
public Object newInstance(Callback var1) {
CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1});
UserServiceImpl$$EnhancerByCGLIB$$2772de75 var10000 = new UserServiceImpl$$EnhancerByCGLIB$$2772de75();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
}
public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
CGLIB$SET_THREAD_CALLBACKS(var3);
UserServiceImpl$$EnhancerByCGLIB$$2772de75 var10000 = new UserServiceImpl$$EnhancerByCGLIB$$2772de75;
switch(var1.length) {
case 0:
var10000.<init>();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
default:
throw new IllegalArgumentException("Constructor not found");
}
}
public Callback getCallback(int var1) {
CGLIB$BIND_CALLBACKS(this);
MethodInterceptor var10000;
switch(var1) {
case 0:
var10000 = this.CGLIB$CALLBACK_0;
break;
default:
var10000 = null;
}
return var10000;
}
public void setCallback(int var1, Callback var2) {
switch(var1) {
case 0:
this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
default:
}
}
public Callback[] getCallbacks() {
CGLIB$BIND_CALLBACKS(this);
return new Callback[]{this.CGLIB$CALLBACK_0};
}
public void setCallbacks(Callback[] var1) {
this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
}
static {
CGLIB$STATICHOOK1();
}
}
通过生成代理类的源码不难看出,代理类是继承了父类,并且对其中的方法重写。总结下cglib的大致原理:
动态生成一个要代理类的子类,子类继承父类并重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。底层使用字节码处理框架ASM,来转换字节码并生成新的类。需要注意的是,对于final方法(final定义的方法无法被子类重写)cglib是无法进行代理的。
顺便提一下,我们常说静态方法是无法进行代理的,这是问什么呢?
这个问题我们需要分为两个部分讨论,
一、对于jdk这种通过实现接口的动态代理来说,因为已经继承了Proxy这个类,而在java中是不能多重继承的,所以静态方法自然是不可能被代理。
二、对于cglib这种通过继承的方式来实现动态代理的方式来说,因为在 Java 编程规范中“静态方法是可继承但不可被重写的”。所以不能被代理。
举个例子:A类有静态方法 a(),B类继承A类,B类继承了A类的静态方法,所以B类可直接使用A类的静态方法。此时若在B类中尝试重写静态方法 a(),新的静态方法 a()将变成独属于B类的静态方法,而失去了原属于A类静态方法 a() 的继承关系。注意,静态方法是独属于当前类的,你若定义便失去了父类静态方法的继承关系,新的静态方法只与当前所属类挂钩。Spring AOP 的 CGLIB 代理在于对父类方法的重写,而对静态方法的重写,会使其失去与父类静态方法的继承关系,违背了代理的核心目的,因此 CGLIB 直接排除了静态方法。
扩展一下,所谓动态代理,最重要的其实就是动态生成一个对象而已。那么思考一个问题,在java中,生成一个对象,除了new出来一个,还有什么其他的方式呢?
1、反射当然可以动态生成一个类,这应该是我们最常用的一个手段了。
2、而除了通过反射可以动态获取类,我们还可以通过生成类的字符串再动态编译类生成一个类,在dubbo的SPI自适应特性中,就是使用了动态编译来得到真正的Class对象。在dubbo的官方书籍:深入理解Apache Dubbo与实战中有说道,相比于反射生成一个类,在性能上和直接编译好的Class会有一定差距(笔者暂未验证过)。
3、当然我们也可以通过动态字节码来进行操作生成类对象。常见的字节码操作类库有:
ASM:是一个轻量级java字节码操作框架,直接涉及到JVM底层的操作和指令
CGLIB:是一个强大的,高性能,高质量的code生成类库,基于ASM实现。spring中默认使用了cglib的动态代理方式。
Javassist:是一个开源的分析,编辑和创建字节码的类库,但是据说运行速度偏慢(笔者没有验证过),使用简单,dubbo中的动态代理即默认使用了此框架。