动态代理说白了还是为了最终生成新的代理类,把原先的老类包裹一层,生成新的类。是字节码编程一个特殊使用分支,即代理对象的增强。
一.CGLAB动态代理
cglib的原理是这样,它生成一个继承B的类型C(代理类),这个代理类持有一个MethodInterceptor,我们setCallback时传入的。 C重写所有B中的方法(方法名一致),然后在C中,构建名叫“CGLIB”+“$父类方法名$”的方法(下面叫cglib方法,所有非private的方法都会被构建),方法体里只有一句话super.方法名(),可以简单的认为保持了对父类方法的一个引用,方便调用。
这样的话,C中就有了重写方法、cglib方法、父类方法(不可见),还有一个统一的拦截方法(增强方法intercept)。其中重写方法和cglib方法肯定是有映射关系的。
C的重写方法是外界调用的入口(LSP原则),它调用MethodInterceptor的intercept方法,调用时会传递四个参数,第一个参数传递的是this,代表代理类本身,第二个参数标示拦截的方法,第三个参数是入参,第四个参数是cglib方法,intercept方法完成增强后,我们调用cglib方法间接调用父类方法完成整个方法链的调用。
CGLAB代理类核心,hadle类
package proxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibProxy implements MethodInterceptor{ // 根据一个类型产生代理类,此方法不要求一定放在MethodInterceptor中
public Object CreatProxyedObj(Class<?> clazz)
{
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable
{ // 这里增强
System.out.println("收钱");
return arg3.invokeSuper(arg0, arg2);
}
}
CGLAB动态代理代码使用案例
CglibProxy proxy2 = new CglibProxy();
Star star2 = (Star)proxy2.CreatProxyedObj(LiuDeHua.class);
star2.sing("ss");
star2.dance("ss");
CGLAB最终生成的代理类
public class CGlibTestClass$$EnhancerByCGLIB$$95b1c7fd extends CGlibTestClass implements Factory {
略
}
二.JDK动态代理
使用Proxy和InvocationHandler创建动态代理
动态代理就是要生成一个包装类对象,由于代理的对象是动态的,所以叫动态代理。由于我们需要增强,这个增强是需要留给开发人员开发代码的,因此代理类不能直接包含被代理对象,而是一个InvocationHandler,该InvocationHandler包含被代理对象,并负责分发请求给被代理对象,分发前后均可以做增强。从原理可以看出,JDK动态代理是“对象”的代理。
jdk核心Handle类
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class StarProxy implements InvocationHandler
{ // 目标类,也就是被代理对象
private Object target;
public void setTarget(Object target)
{
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
// 这里可以做增强
System.out.println("收钱");
Object result = method.invoke(target, args);
return result;
}
// 生成代理类
public Object CreatProxyedObj()
{
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
}
JDK最终生成的代理类
final class $Proxy0 extends Proxy implements IProx {
....
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
....
public final String hello(String var1) throws {
try {
return (String)super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
}
JDK动态代理使用案例
int times = 1000000;
Star ldh = new LiuDeHua();
StarProxy proxy = new StarProxy();
proxy.setTarget(ldh);
Star star = (Star)proxy.CreatProxyedObj();
star.sing("ss");
star.dance("ss");
注意:
-
jdk是对对象的代理(需要new tagert对象,新new的对象里会调用tagert),而cglib是对类的代理(不需要new target对象,直接用class文件,生成新的class文件,new对象)
-
jdk,cglab二者都需要调用反射方法(invoke),性能不理想
三.Javassist字节码之动态代理(dubbo的动态代理策略)
生成代理类方法
private static CountService createJavassistBytecodeDynamicProxy(CountService delegate) throws Exception {
ClassPool mPool = new ClassPool(true);
CtClass mCtc = mPool.makeClass(CountService.class.getName() + "JavaassistProxy");
mCtc.addInterface(mPool.get(CountService.class.getName()));
mCtc.addConstructor(CtNewConstructor.defaultConstructor(mCtc));
mCtc.addField(CtField.make("public " + CountService.class.getName() + " delegate;", mCtc));
mCtc.addMethod(CtNewMethod.make("public int count() { return delegate.count(); }", mCtc));
Class pc = mCtc.toClass();
CountService bytecodeProxy = (CountService) pc.newInstance();
Field filed = bytecodeProxy.getClass().getField("delegate");
filed.set(bytecodeProxy, delegate);
return bytecodeProxy;
}
使用方法
CountService javassistBytecodeProxy = createJavassistBytecodeDynamicProxy(delegate);
javassistBytecodeProxy.count();
四.ASM字节码之动态代理
生成代理类方法
private static CountService createAsmBytecodeDynamicProxy(CountService delegate) throws Exception {
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
String className = CountService.class.getName() + "AsmProxy";
String classPath = className.replace('.', '/');
String interfacePath = CountService.class.getName().replace('.', '/');
classWriter.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC, classPath, null, "java/lang/Object", new String[] {interfacePath});
MethodVisitor initVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
initVisitor.visitCode();
initVisitor.visitVarInsn(Opcodes.ALOAD, 0);
initVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
initVisitor.visitInsn(Opcodes.RETURN);
initVisitor.visitMaxs(0, 0);
initVisitor.visitEnd();
FieldVisitor fieldVisitor = classWriter.visitField(Opcodes.ACC_PUBLIC, "delegate", "L" + interfacePath + ";", null, null);
fieldVisitor.visitEnd();
MethodVisitor methodVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "count", "()I", null, null);
methodVisitor.visitCode();
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitFieldInsn(Opcodes.GETFIELD, classPath, "delegate", "L" + interfacePath + ";");
methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, interfacePath, "count", "()I");
methodVisitor.visitInsn(Opcodes.IRETURN);
methodVisitor.visitMaxs(0, 0);
methodVisitor.visitEnd();
classWriter.visitEnd();
byte[] code = classWriter.toByteArray();
CountService bytecodeProxy = (CountService) new ByteArrayClassLoader().getClass(className, code).newInstance();
Field filed = bytecodeProxy.getClass().getField("delegate");
filed.set(bytecodeProxy, delegate);
return bytecodeProxy;
}
使用方法
CountService asmBytecodeProxy = createAsmBytecodeDynamicProxy(delegate);
asmBytecodeProxy.count();
注意::
- 这俩种实现方式本质也是,“对象代理”,需要外面传来一个delegete对象,来实现增强。
- 俩种字节码动态代理,不走反射方法,性能会好很多。