java之四种动态代理的实现

王大爷 2020年12月09日 553次浏览

动态代理说白了还是为了最终生成新的代理类,把原先的老类包裹一层,生成新的类。是字节码编程一个特殊使用分支,即代理对象的增强。

一.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, "&lt;init&gt;", "()V", null, null);  
          initVisitor.visitCode();  
           initVisitor.visitVarInsn(Opcodes.ALOAD, 0);  
           initVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "&lt;init&gt;", "()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对象,来实现增强。
  • 俩种字节码动态代理,不走反射方法,性能会好很多。

参考