Java动态代理

 部分内容偷懒复制转载自java动态代理_飞!!!!的博客-CSDN博客

少量内容根据自己的理解有所完善补充。

1、代理模式

代理模式是常见的设计模式之一,Java我们通常通过new一个对象然后调用其对应的方法来访问我们需要的服务。代理模式则是通过创建代理类(proxy)的方式来访问服务,代理类通常会持有一个委托类对象,代理类不会自己实现真正服务,而是通过调用委托类对象的相关方法,来提供服务,所以其实我们调用的还是委托类的服务,但是中间隔了一个代理类。这么做是有好处的,我们可以在访问服务之前或者之后加一下我们需要的操作。例如Spring的面向切面编程,我们可以在切入点之前执行一些操作,切入点之后执行一些操作。这个切入点就是一个个方法。这些方法所在类肯定就是被代理了,在代理过程中切入了一些其他操作。

在这里插入图片描述

 2、静态代理

再说动态代理之前先说一下静态代理。静态代理就是程序员在编写代码的时候就已经把代理类的源码写好了,编译后就会生成.class文件。
简单代码实现
静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。我这举一个租客通过中介租房子的例子。
(1)首先创建一个person接口,这个接口就是租客和中介的公共接口,这个接口有一个rentHouse()方法。

public interface Person {

    //租房
    public void rentHouse();
}

(2)创建租客Renter类,实现上述接口

public class Renter implements Person{

    @Override
    public void rentHouse() {
        System.out.println("租客租房成功!");
        
    }

}

(3)创建中介类RenterProxy,同样实现Person接口,但是还另外持有一个租客类对象

public class RenterProxy implements Person{
    private Person renter;
    public RenterProxy(Person renter){
        this.renter = renter;
    }
    @Override
    public void rentHouse() {
        System.out.println("中介找房东租房,转租给租客!");
        renter.rentHouse();
        System.out.println("中介给租客钥匙,租客入住!");
        
    }

}

(4)新建测试类测试

public class StaticProxyTest {

    public static void main(String[] args) {
        Person renter = new Renter();
        RenterProxy proxy = new RenterProxy(renter);
        proxy.rentHouse();
    }

}

运行结果:
中介找房东租房,转租给租客!
租客租房成功!
中介给租客钥匙,租客入住!

3、动态代理

代理类在程序运行时创建的代理方式被成为动态代理。在静态代理中,代理类(RenterProxy)是自己已经定义好了的,在程序运行之前就已经编译完成。而动态代理是在运行时根据我们在Java代码中的“指示”动态生成的。动态代理相较于静态代理的优势在于可以很方便的对代理类的所有函数进行统一管理,如果我们想在每个代理方法前都加一个方法,如果代理方法很多,我们需要在每个代理方法都要写一遍,很麻烦。而动态代理则不需要。

在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。
(1)和静态代理相同,首先定义一个person接口

public interface Person {

    //租房
    public void rentHouse();
}

(2)创建被代理的类

public class Renter implements Person{

    @Override
    public void rentHouse() {
        System.out.println("租客租房成功!");
        
    }

}

(3)创建RenterInvocationHandler类,这个类实现了InvocationHandler接口,并持有一个被代理类的对象,InvocationHandler中有一个invoke方法,所有执行代理对象的方法都会被替换成执行invoke方法。然后通过反射在invoke方法中执行代理类的方法。在代理过程中,在执行代理类的方法前或者后可以执行自己的操作,这就是spring aop的主要原理。

public class RenterInvocationHandler implements InvocationHandler{
    //被代理类的对象
    private T target;
    
    public RenterInvocationHandler(T target){
        this.target = target;
    }

    /**
     * proxy:代表动态代理对象
     * method:代表正在执行的方法
     * args:代表调用目标方法时传入的实参
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        //代理过程中插入其他操作
        System.out.println("租客和中介交流");
        Object result = method.invoke(target, args);
        return result;
    }
}

(4)创建动态代理对象

public class ProxyTest {

    public static void main(String[] args) {

        //创建被代理的实例对象
        Person renter = new Renter();
        //创建InvocationHandler对象
        InvocationHandler renterHandler = new RenterInvocationHandler(renter);
        
        
        //创建代理对象,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
        Person renterProxy = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(),new Class[]{Person.class}, renterHandler);
        renterProxy.rentHouse();
        
        //也可以使用下面的方式创建代理类对象,Proxy.newProxyInstance其实就是对下面代码的封装
        /*try {
            //使用Proxy类的getProxyClass静态方法生成一个动态代理类renterProxy 
            Class renterProxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), new Class[]{Person.class});
            //获取代理类renterProxy的构造器,参数为InvocationHandler
            Constructor constructor = renterProxyClass.getConstructor(InvocationHandler.class);
            //使用构造器创建一个代理类实例对象
            Person renterProxy = (Person)constructor.newInstance(renterHandler);
            renterProxy.rentHouse();
            //
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }*/
    }

}

执行结果:
租客和中介交流
租客租房成功!

原理分析:
点击查看Proxy类newProxyInstance方法源码,重要部分我做了注释

 public static Object newProxyInstance(ClassLoader loader,
                                          Class[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        if (h == null) {
            throw new NullPointerException();
        }

        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, interfaces);
        }

        /*
         * Look up or generate the designated proxy class.
         */
         //生成动态代理类
        Class cl = getProxyClass0(loader, interfaces);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            //获取构造器参数是InvocationHandler类,constructorParams是Proxy的静态成员变量
            // private final static Class[] constructorParams ={ InvocationHandler.class };
            final Constructor cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                // create proxy instance with doPrivilege as the proxy class may
                // implement non-public interfaces that requires a special permission
                return AccessController.doPrivileged(new PrivilegedAction() {
                    public Object run() {
                        return newInstance(cons, ih);
                    }
                });
            } else {
                return newInstance(cons, ih);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        }
    }

    private static Object newInstance(Constructor cons, InvocationHandler h) {
        try {
           //使用构造器创建一个代理类实例对象
            return cons.newInstance(new Object[] {h} );
        } catch (IllegalAccessException | InstantiationException e) {
            throw new InternalError(e.toString());
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString());
            }
        }
    } 
  

Class cl = getProxyClass0(loader, interfaces);这行代码生成了一个代理类,这个类缓存在java虚拟机中。可以通过下列代码将其打印出来。

byte[] classByte = ProxyGenerator.generateProxyClass("$Proxy0",
                new Class[] { Person.class });
        String path = "D:/Eclipse/Workspace/Demo/bin/$Proxy0.class";
        try (FileOutputStream fos = new FileOutputStream(path)) {
            fos.write(classByte);
            fos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }

调用Proxy.newProxyInstance生成代理类的实现类的过程:

调用getProxyClass0寻找或生成指定代理类
(从缓存中取,如果没有,就生成一个放在缓存中 : 通过ProxyClassFactory生成)
缓存调用ProxyClassFactory生成代理类,proxy class的生成最终调用ProxyClassFactory的apply方法.
ProxyGenerator.generateProxyClass使用生成的代理类的名称,接口,访问标志生成proxyClassFile字节码

将class文件反编译:

public final class $Proxy0 extends Proxy
  implements Person
{
  private static Method m1;
  private static Method m3;
  private static Method m0;
  private static Method m2;
//通过带InvocationHandler参数的构造器生成代理类对象时,将我们写的RenterInvocationHandler对象传进来
//父类持有:protected InvocationHandler h
  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }

  public final boolean equals(Object paramObject)
    throws 
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void rentHouse()
    throws 
  {
    try
    {
    //执行代理类的rentHouse方法时,实际上是执行InvocationHandler类的invoke方法,也就是我们自己写的
    //RenterInvocationHandler类的invoke方法,在此方法中我们通过method.invoke(target, args)反射,再
    //执行具体类的rentHouse方法      
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final int hashCode()
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final String toString()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { 
      Class.forName("java.lang.Object") });
      //获取到了rentHouse方法的方法名
      m3 = Class.forName("test.father.Person").getMethod("rentHouse", 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]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

java自动生成了一个$Proxy0代理类,这个类在内存中,所以可以通过反射获取这个类的构造方法,然后创建的代理类实例对象。分析这个类源码,可以知道当调用代理类对象的rentHouse方法时的大概流程为:调用RenterInvocationHandler类的invoke方法,而RenterInvocationHandler类的invoke方法中又用反射调用了被代理类的rentHouse方法。
RenterInvocationHandler可以看成是中间类,它持有被代理对象,把外部对invoke的调用转为对被代理对象的调用。而代理类通过持有中间类,调用中间类的invoke方法,来达到调用被代理类的方法的目的

4、基于继承代理(cglib)

动态代理需要被代理类实现接口,如果被代理类没有实现接口,那么这么实现动态代理?这时候就需要用到CGLib了。这种代理方式就叫做CGlib代理。
Cglib代理也叫作子类代理,他是通过在内存中构建一个子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,然后加入自己需要的操作。因为使用的是继承的方式,所以不能代理final 类。

  • 代理类
public class CglibMethodInterceptor implements MethodInterceptor{
//主要的方法拦截类,它是Callback接口的子接口,需要用户实现

        @Override
        public Object intercept(Object obj,Method method,Object
        [] args,MethodProxy proxy )throws Throwable{
        System.out.println("before cgplib");
        Object result = null;
            try{
               //利用反射创建代理对象 
               result = proxy.invokeSuper(obj,args);
            }catch(Exception e){
            System.out.println("ex:"+e.getMessage());
                throw e;
            }finally{
                System.out.println("after cglib");
            }
            return result;
            }
        }
  • 调用类
public class Client{
    public static void main(String[] args){
    // 主要的增强类
    Enhancer enhancer=new Enhancer;
    //  目标类 , 设置父类,被增强的类
    enhancer.setSuperclass(RealSubject.class);
    // 回调对象
    enhancer.setCallback(new CglibMethodInterceptor());
    //生成代理类对象,用cglibProxy来增强RealSubject
    Subject subject=enhancer.create();
    subject.test();
    }
}
  • 原理解析

Cglib是一个优秀的动态代理框架,它的底层使用ASM在内存中动态的生成被代理类的子类,使用CGLIB即使代理类没有实现任何接口也可以实现动态代理功能。CGLIB具有简单易用,它的运行速度要远远快于JDK的Proxy动态代理:
CGLIB的核心类:

net.sf.cglib.proxy.Enhancer – 主要的增强类
net.sf.cglib.proxy.MethodInterceptor – 主要的方法拦截类,它是Callback接口的子接口,需要用户实现
net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用,如使用:
Object o = methodProxy.invokeSuper(proxy, args);//虽然第一个参数是被代理对象,也不会出现死循环的问题。
-net.sf.cglib.proxy.MethodInterceptor接口是最通用的回调(callback)类型,它经常被基于代理的AOP用来实现拦截(intercept)方法的调用。这个接口只定义了一个方法

public Object intercept(Object object, java.lang.reflect.Method method,
Object[] args, MethodProxy proxy) throws Throwable;

第一个参数是代理对像,第二和第三个参数分别是拦截的方法和方法的参数。原来的方法可能通过使用java.lang.reflect.Method对象的一般反射调用,或者使用 net.sf.cglib.proxy.MethodProxy对象调用。net.sf.cglib.proxy.MethodProxy通常被首选使用,因为它更快。

Java字节码生成开源框架–ASM:

ASM 是一个 Java 字节码操控框架。它能够以二进制形式修改已有类或者动态生成类。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
不过ASM在创建class字节码的过程中,操纵的级别是底层JVM的汇编指令级别,这要求ASM使用者要对class组织结构和JVM汇编指令有一定的了解。

你可能感兴趣的:(Java,java,代理模式,开发语言)