Java 动态代理

Java动态代理

首先我们介绍代理模式

静态代理模式

在这里插入图片描述

静态代理

  1. 可以做到在不修改目标对象前提下,对目标功能扩展。
  2. 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.

动态代理模式

使用静态代理,会有一个问题,就是当Subject接口更改了之后,所有的Proxy类也要跟着更改。动态代理有两种实现方式,JDK内部自带的实现方法和利用cglib实现方式。静态代理的代理类是我们自己定义好的,而动态代理的代理类时根据我们的代码动态生成的。

JDK内部自带动态代理

在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。

创建一个动态代理对象步骤:

  • 创建一个InvocationHandler对象
  • 使用Proxy类的getProxyClass静态方法生成一个动态代理类stuProxyClass
  • 获得stuProxyClass 中一个带InvocationHandler参数的构造器constructor
  • 通过构造器constructor来创建一个动态实例stuProxy
InvocationHandler stuHandler = new MyInvocationHandler<Person>(stu);
Class<?> stuProxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), new Class<?>[] {Person.class});
Constructor<?> constructor = PersonProxy.getConstructor(InvocationHandler.class);
Person stuProxy = (Person) cons.newInstance(stuHandler);

上述四部可以简化为两步:

//创建一个与代理对象相关联的InvocationHandler
  InvocationHandler stuHandler = new MyInvocationHandler<Person>(stu);
//创建一个代理对象stuProxy,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
  Person stuProxy= (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);

下面是具体实现

public interface Person {
    void giveMoney();
}
//接口的实现类
public class Student implements Person {
    private String name;
    public Student(String name) {
        this.name = name;
    }
    @Override
    public void giveMoney() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
       System.out.println(name + "上交班费50元");
    }
}

实现InvocationHanlder方法

public class StuInvocationHandler<T> implements InvocationHandler {
   //invocationHandler持有的被代理对象
    T target;
    
    public StuInvocationHandler(T target) {
       this.target = target;
    }
    
    /**
     * proxy:代表动态代理对象
     * method:代表正在执行的方法
     * args:代表调用目标方法时传入的实参
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理执行" +method.getName() + "方法");
        MonitorUtil.start();
        Object result = method.invoke(target, args);
        MonitorUtil.finish(method.getName());
        return result;
    }
}

测试类:

public class ProxyTest {
    public static void main(String[] args) {
        Person zhangsan = new Student("张三");
        InvocationHandler stuHandler = new StuInvocationHandler<Person>(zhangsan);
        Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);
        stuProxy.giveMoney();
    }
}

JDK动态代理的原理分析

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h) {
        Objects.requireNonNull(h);
        final Class<?> caller = System.getSecurityManager() == null
                                    ? null
                                    : Reflection.getCallerClass();
    	//获得代理类的构造器
        Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
        return newProxyInstance(caller, cons, h);
    }
private static Object newProxyInstance(Class<?> caller, // null if no SecurityManager
                                       Constructor<?> cons,
                                       InvocationHandler h) {
    if (caller != null) {
        checkNewProxyPermission(caller, cons.getDeclaringClass());
    }
    //通过代理类的构造器构造代理类实例,返回代理类实例对象
    return cons.newInstance(new Object[]{h});
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.Person;

public final class $Proxy0 extends Proxy implements Person
{
  private static Method m1;
  private static Method m2;
  private static Method m3;
  private static Method m0;
  
  /**
  *注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白
  *为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个
  *被代理对象的实例,不禁会想难道是....? 没错,就是你想的那样。
  *
  *super(paramInvocationHandler),是调用父类Proxy的构造方法。
  *父类持有:protected InvocationHandler h;
  *Proxy构造方法:
  *    protected Proxy(InvocationHandler h) {
  *         Objects.requireNonNull(h);
  *         this.h = h;
  *     }
  *
  */
  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }
  
  //这个静态块本来是在最后的,我把它拿到前面来,方便描述
   static
  {
    try
    {
      //看看这儿静态块儿里面有什么,是不是找到了giveMoney方法。请记住giveMoney通过反射得到的名字m3,其他的先不管
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m3 = Class.forName("proxy.Person").getMethod("giveMoney", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }

  /**
  * 
  *这里调用代理对象的giveMoney方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。
  *this.h.invoke(this, m3, null);这里简单,明了。
  *来,再想想,代理对象持有一个InvocationHandler对象,InvocationHandler对象持有一个被代理的对象,
  *再联系到InvacationHandler中的invoke方法。嗯,就是这样。
  */
  public final void giveMoney()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  //注意,这里为了节省篇幅,省去了toString,hashCode、equals方法的内容。原理和giveMoney方法一毛一样。
}

CGLIB实现动态代理

CGLIB使用ASM框架直接对字节码进行操作,可以对普通的类进行代理。

CGLIB动态代理的使用

创建一个实体类

public class PersonService {
    public PersonService(){
        System.out.println("PersonService构造");
    }
    final public Integer getPerson(String code){
        System.out.println("PersonService:getPerson>>" + code);
        return null;
    }
    public void setPerson(){
        System.out.println("setPerson");
    }
}

创建一个实现MethodInterceptor接口的类

public class CglibProxyIntercepter implements MethodInterceptor {

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before");
        Object ob = methodProxy.invokeSuper(o,objects);
        System.out.println("after");
        return ob;
    }
}

通过Enhancer创建代理类并执行方法

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(PersonService.class);
    enhancer.setCallback(new CglibProxyIntercepter());
    PersonService proxy = (PersonService) enhancer.create();
    proxy.setPerson();
    proxy.getPerson("1");

}

CGLIB动态代理原理分析

CGLIB共生成3个类,分别为代理类的FastClass,代理类,目标类的FastClass,FastClass,这里简单说一下,就是给每个方法编号,通过编号找到方法,这样可以避免频繁使用反射导致效率比较低,也可以叫做FastClass机制。

public class Target$$EnhancerByCGLIB$$788444a0 extends Target implements Factory
{
    private boolean CGLIB$BOUND;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static final Method CGLIB$g$0$Method;
    private static final MethodProxy CGLIB$g$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$f$1$Method;
    private static final MethodProxy CGLIB$f$1$Proxy;
    
    static void CGLIB$STATICHOOK1()
    {
      CGLIB$THREAD_CALLBACKS = new ThreadLocal();
      CGLIB$emptyArgs = new Object[0];
      Class localClass1 = Class.forName("net.sf.cglib.test.Target$$EnhancerByCGLIB$$788444a0");
      Class localClass2;
      Method[] tmp60_57 = ReflectUtils.findMethods(new String[] { "g", "()V", "f", "()V" }, (localClass2 = Class.forName("net.sf.cglib.test.Target")).getDeclaredMethods());
      CGLIB$g$0$Method = tmp60_57[0];
      CGLIB$g$0$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "g", "CGLIB$g$0");
      CGLIB$f$1$Method = tmp60_57[1];
      CGLIB$f$1$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "f", "CGLIB$f$1");
    }
    
    final void CGLIB$g$0()
    {
      super.g();
    }
    
    public final void g()
    {
      MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
      if (tmp4_1 == null)
      {
          CGLIB$BIND_CALLBACKS(this);
          tmp4_1 = this.CGLIB$CALLBACK_0;
      }
      if (this.CGLIB$CALLBACK_0 != null) {
          tmp4_1.intercept(this, CGLIB$g$0$Method, CGLIB$emptyArgs, CGLIB$g$0$Proxy);
      }
      else{
          super.g();
      }
    }
}

FastClass机制分析

public class test10 {
    public static void main(String[] args){
        Test tt = new Test();
        Test2 fc = new Test2();
        int index = fc.getIndex("f()V");
        fc.invoke(index, tt, null);
    }
}

class Test{
    public void f(){
        System.out.println("f method");
    }
    
    public void g(){
        System.out.println("g method");
    }
}
class Test2{
    public Object invoke(int index, Object o, Object[] ol){
        Test t = (Test) o;
        switch(index){
        case 1:
            t.f();
            return null;
        case 2:
            t.g();
            return null;
        }
        return null;
    }
    
    public int getIndex(String signature){
        switch(signature.hashCode()){
        case 3078479:
            return 1;
        case 3108270:
            return 2;
        }
        return -1;
    }
}

JDK动态代理与CGLIB动态代理的对比

  1. JDK动态代理是接口代理,被代理类A需要实现接口,业务代理类b也需要实现该接口并继承Proxy类,所以这种动态代理不能代理普通类,因为Java只允许单继承。
  2. CGLIB生成的代理类会直接继承被代理类,通过生成子类字节码来实现,比反射快一点,没有性能问题。但是由于cglib会继承被代理类,需要重写被代理方法,所以被代理类不能是final类,被代理方法不能是final。

JDK动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

  1. 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP

  2. 如果目标对象实现了接口,可以强制使用CGLIB实现AOP

  3. 如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

JDK动态代理和CGLIB字节码生成的区别?

  1. JDK动态代理只能对实现了接口的类生成代理,而不能针对类
  2. CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
  3. 因为是继承,所以该类或方法最好不要声明成final
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值