java基础-动态代理与静态代理

版权声明:本文为博主原创文章,转载需注明出处. https://blog.csdn.net/piaoslowly/article/details/81909741

java基础-动态代理与静态代理

代理主要有:静态代理和动态代理(jdk基于接口的实现,cglib基于类的实现)
jdk动态代理来自于反射,先了解反射,再来了解动态代理哦!
Proxy类它也是在反射包里面的,所以它也是反射的一部分,这部分太大了,使用也比较广泛所以拿出来单独讲解。
动态代理(动态加载,动态链接,各叫各的,其实带动态的都是一个意思只是叫法不同而已)

代理是什么?

代理:设计模式
代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

代理模式结构图:

为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。Java静态代理,动态代理都是这个模式,只不过动态代理机制以巧妙的方式近乎完美地实践了代理模式的设计理念。

下面来看下静态代理的实现

静态代理

静态实现

1.接口定义:

public interface ProxyInterface {
    void play();
    void over();
}

2.接口实现类定义:

public class RealObject implements ProxyInterface {

    @Override
    public void play() {
        System.out.println("read .... play");
    }

    @Override
    public void over() {

    }
}

2.1 接口实现类定义:
StudentObject,和上面RealObject一样,这里就不贴代码了

3.代理类实现

public class ProxyObject implements ProxyInterface {

    ProxyInterface proxyInterface;
    public ProxyObject(ProxyInterface proxyInterface){
        this.proxyInterface=proxyInterface;
    }

    @Override
    public void play() {
        System.out.println("代理前.....接送用户去机场");
        System.out.println("代理前.....给用户顶酒店");
        proxyInterface.play();
        System.out.println("代理后.....接送用户回家");
    }

    @Override
    public void over() {
        System.out.println("代理前..........");
        proxyInterface.over();
        System.out.println("代理后.....给用户。。。。");

    }
}

4.主方法

public class ProxyDome {
    public static void main(String[] args) {
        RealObject realObject=new RealObject();
        StudentObject studentObject=new StudentObject();

        //realObject,studentObject他们两个需要做的事,都被我一个proxy代理做了。
        ProxyObject p1 = new ProxyObject(realObject);
        ProxyObject p2 = new ProxyObject(studentObject);
        p1.play();
        p2.play();
    }
}

说明:这里可以看到RealObject,StudentObject都被ProxyObject这个代理去执行了,那是不是意味着RealObject,StudentObject不用做事了呢?
答案:不是,

就好像使用携程顶机票一样,携程只是一个代理,它代理航空公司做了一些事,我们用户只需要使用携程就可以订到机票了,但是真正打印机票的还是航空公司。携程可以做一个机场寄送服务,酒店预定服务,航空公司只需要做好机票服务就好了。这个就是代理模式。

可以看到上面代理类ProxyObject,在真正做事前做了一件事打印了“代理前”,“代理后”,然后才到了真正执行任务;实际应用中我们可以利用代理模式做很多其他事,而不仅仅只是打印一行日志。

优缺点

客户端不管关心具体的实现,它只需要知道自己订机票就好了,然后代理类会把他酒店,机场接送,机票一块儿做好了。
代理使客户端不需要知道实现类是什么,怎么做的,而客户端只需知道代理即可(解耦合),对于如上的客户端代码,new RealObject()可以应用工厂将它隐藏,上面只是一个简单的例子。

缺点:
1)代理类和实现类实现了相同的接口,代理类通过实现类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

上面ProxyObject这个类就可以看出,它和RealObject是不是很像,而且重复了很多代码!

2)代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。如上的代码是只为RealObject类的访问提供了代理,但是如果还要为其他类提供代理的话,就需要我们再次添加代理类。

如:

接口:

public interface RailInterface {
    void buy(); 
}

实现类:

public class MeetRail implements RailInterface {

    @Override
    public void buy() {
        System.out.println("read .... play");
    }
}

代理类:

public class ProxyRail implements RailInterface {

    RailInterface railInterface;
    public ProxyObject(RailInterface railInterface){
        this.railInterface = railInterface;
    }

    @Override
    public void buy() {
        System.out.println("read .... 日志服务");
        System.out.println("read .... 接送服务");
        railInterface.buy();
        System.out.println("代理后");
    }
}

到这里看到了吗?静态代理的缺点。
RealObject,MeetRail,现在有两个实现类,他们都需要记录请求日志,同时在订票前都有请求合法性验证,那么这个时候使用静态代理,我们需要写两个静态代理分别实现ProxyInterface ,RailInterface这两个接口,假如有三个,四个我们都需要去实现它们,这样重复代码大量,就失去了代理的意义了。
那么静态代理有价值吗?在某些单一的场景下
如:

RealObject realObject = new RealObject();
StudentObject studentObject = new StudentObject();

他们两个都实现了同一个接口,但是需要一个代理,那么静态代理就比较适合使用。

下面将要讲解动态代理怎么来解决一个代理解决所有问题。

java JDK实现动态代理(基于接口的代理)

反射包下面有一个:java.lang.reflect.Proxy,Proxy代理类。
Proxy类提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。

动态代理类(以下简称为代理类)是一个实现在创建类时在运行时指定的接口列表的类,该类具有下面描述的行为。代理接口是代理类实现的一个接口。 代理实例是代理类的一个实例。每个代理实例都有一个关联的调用处理程序对象,它可以实现接口InvocationHandler。通过其中一个代理接口的代理实例上的方法调用将被指派到实例的调用处理程序的Invoke 方法,并传递代理实例、识别调用方法的java.lang.reflect.Method对象以及包含参数的 Object 类型的数组。调用处理程序以适当的方式处理编码的方法调用,并且它返回的结果将作为代理实例上方法调用的结果返回。

JDK动态代理中必须实现该InvocationHandler接口
public interface InvocationHandler {
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
}
参数说明:
Object proxy:代理类实例对象。
Method method:要调用的方法
Object[] args:方法调用时所需要的参数

Proxy类:
Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法:
public static Object newProxyInstance(ClassLoader loader, Class

代理机制及其特点

首先让我们来了解一下如何使用 Java 动态代理。具体有如下四步骤:
- 通过实现 InvocationHandler 接口创建自己的调用处理器;
- 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
- 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
// 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用
InvocationHandler handler = new InvocationHandlerImpl(..); 

// 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... }); 

// 通过反射从生成的类对象获得构造函数对象
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class }); 

// 通过构造函数对象创建动态代理类实例
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });

实际使用过程更加简单,因为 Proxy 的静态方法 newProxyInstance 已经为我们封装了步骤 2 到步骤 4 的过程,所以简化后的过程如下

// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
InvocationHandler handler = new InvocationHandlerImpl(..); 

// 通过 Proxy 直接创建动态代理类实例
Interface proxy = (Interface)Proxy.newProxyInstance( classLoader, 
     new Class[] { Interface.class }, 
     handler );

首先是动态生成的代理类本身的一些特点

  1. 包:如果所代理的接口都是 public 的,那么它将被定义在顶层包(即包路径为空),如果所代理的接口中有非 public 的接口(因为接口不能被定义为 protect 或 private,所以除 public 之外就是默认的 package 访问级别),那么它将被定义在该接口所在包(假设代理了 com.ibm.developerworks 包中的某非 public 接口 A,那么新生成的代理类所在的包就是 com.ibm.developerworks),这样设计的目的是为了最大程度的保证动态代理类不会因为包管理的问题而无法被成功定义并访问;
  2. 类修饰符:该代理类具有 final 和 public 修饰符,意味着它可以被所有的类访问,但是不能被再度继承;
  3. 类名:格式是“$ProxyN”,其中 N 是一个逐一递增的阿拉伯数字,代表 Proxy 类第 N 次生成的动态代理类,值得注意的一点是,并不是每次调用 Proxy 的静态方法创建动态代理类都会使得 N 值增加,原因是如果对同一组接口(包括接口排列的顺序相同)试图重复创建动态代理类,它会很聪明地返回先前已经创建好的代理类的类对象,而不会再尝试去创建一个全新的代理类,这样可以节省不必要的代码重复生成,提高了代理类的创建效率。
  4. 类继承关系:该类的继承关系如图:

    由图可见,Proxy 类是它的父类,这个规则适用于所有由 Proxy 创建的动态代理类。而且该类还实现了其所代理的一组接口,这就是为什么它能够被安全地类型转换到其所代理的某接口的根本原因。

接着来了解一下被代理的一组接口有哪些特点

  • 首先,要注意不能有重复的接口,以避免动态代理类代码生成时的编译错误。
  • 其次,这些接口对于类装载器必须可见,否则类装载器将无法链接它们,将会导致类定义失败。
  • 再次,需被代理的所有非 public 的接口必须在同一个包中,否则代理类生成也会失败。
  • 最后,接口的数目不能超过 65535,这是 JVM 设定的限制。

最后再来了解一下异常处理方面的特点

从调用处理器接口声明的方法中可以看到理论上它能够抛出任何类型的异常,因为所有的异常都继承于 Throwable 接口,但事实是否如此呢?
答案是否定的,原因是我们必须遵守一个继承原则:即子类覆盖父类或实现父接口的方法时,抛出的异常必须在原方法支持的异常列表之内。所以虽然调用处理器理论上讲能够,但实际上往往受限制,除非父接口中的方法支持抛 Throwable 异常。那么如果在 invoke 方法中的确产生了接口方法声明中不支持的异常,那将如何呢?放心,Java 动态代理类已经为我们设计好了解决方法:它将会抛出 UndeclaredThrowableException 异常。这个异常是一个 RuntimeException 类型,所以不会引起编译错误。通过该异常的 getCause 方法,还可以获得原来那个不受支持的异常对象,以便于错误诊断。

动态生成的代理类,只能抛出父类中定义的类型。如果其他异常则会直接抛出UndeclaredThrowableException异常。

java JDK动态代理只能基于接口来实现,就是说被带来的类,必须要继承某个接口。(这里说的不是处理器类必须继承InvocationHandler)

查看动态代理生成的class文件

在启动过程中添加-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true,表示动态生成的代理类存放到磁盘,生成的文件在跟目录;默认是存放到内存中所以我们平时看不到。

jvm参数设置:-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true

举个栗子

1.接口

public interface ReadableInterface {
    void read();
    void write();
}

2.实现类

public class StudentImpl implements ReadableInterface {

    @Override
    public void read() {
        System.out.println("student...read");
    }

    @Override
    public void write() {

    }
}

3.处理器类


public class ProxyObjectImpl implements InvocationHandler {

    private Object proxied = null;

    public ProxyObjectImpl() {
    }

    public ProxyObjectImpl(Object proxied) {
        this.proxied = proxied;
    }


    /**
     * // 该方法负责集中处理动态代理类上的所有方法调用
     //
     * @param proxy:第一个参数既是代理类实例
     * @param method:第二个参数是被调用的方法对象
     * @param args:第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行
     * 注意:千万别把第一个参数放入method.invoke里面哦,不然就会死循环了,代理类执行代理类,变成自己代理自己了
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理做事前。。。。。。。");

        Object o=method.invoke(proxied, args);
        System.out.println("代理做事后。。。。。。");
        return o;
    }
}

注意:这个处理器类和静态代理中的代理类看似非常相似,但是有很大区别哦!比如静态代理中,刚开始接口中只有一个play(),实现类也需要改这个是应该的,但是代理类也需要跟着改(这就扯淡了),而且添加的第二个方法中还需要加入代理前的日志,代理后的逻辑,这部分完全重复了;后来又来了一个新接口,又要再写一个代理类去处理,这样就出现大量重复代码了额,而且无法维护了。而动态代理的这个“处理器类”这个只是处理一些扩展功能(可以增加也可以不加),然后通过反射,动态生成一个代理类Proxy0出来。

动态代理最终还是和静态代理一样,只不过动态代理的代理类是动态生成的,而静态代理类是我们手动去些的。

4.主类

//启动主类的时候加上idea设置里面添加vm参数:-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true,把动态生成的代理文件给保存到磁盘
public class DynamicProxyDemo {

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        proxy1();
        //proxy2();
    }
    //假如不知道类型,可以完全根据接口来
    获取代理类,然后代理它去执行
    private static void proxy1() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        Class<?> c = Class.forName("com.test.spring.proxy.doy.impl.StudentImpl");
        if (c == null) {
            return;
        }
        Object student = c.newInstance();
        //获取委托方
        ReadableInterface readableInterface = (ReadableInterface) Proxy.newProxyInstance(ReadableInterface.class.getClassLoader(),
                new Class[]{ReadableInterface.class},
                new ProxyObjectImpl(student));
        readableInterface.read();
        readableInterface.write();
    }

    //知道类型,然后根据类型去代理
    private static void proxy2() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        StudentImpl student = new StudentImpl();
        //获取委托方
        //基于接口实现动态代理,这里很好的说明了java JDK动态代理只能基于接口实现。
        ReadableInterface readableInterface = (ReadableInterface) Proxy.newProxyInstance(StudentImpl.class.getClassLoader(),
                StudentImpl.class.getInterfaces(),
                new ProxyObjectImpl(student));
        readableInterface.read();
    }
}

5.查看动态生成的代理类

代理的大概结构包括4部分:
静态字段:被代理的接口所有方法都有一个对应的静态方法变量;
静态块:主要是通过反射初始化静态方法变量;
具体每个代理方法:逻辑都差不多就是 h.invoke,主要是调用我们定义好的invocatinoHandler逻辑,触发目标对象target上对应的方法;
构造函数:从这里传入我们InvocationHandler逻辑;

public final class $Proxy0
  extends Proxy
  implements ReadableInterface
{
  private static Method m1;
  private static Method m4;
  private static Method m2;
  private static Method m3;
  private static Method m0;

  public $Proxy0(InvocationHandler paramInvocationHandler)
  {
    super(paramInvocationHandler);
  }

  public final boolean equals(Object paramObject)
  {
    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 read()
  {
    try
    {
      this.h.invoke(this, m4, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

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

  public final void write()
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final int hashCode()
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    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") });
      m4 = Class.forName("com.test.spring.proxy.doy.ReadableInterface").getMethod("read", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m3 = Class.forName("com.test.spring.proxy.doy.ReadableInterface").getMethod("write", 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());
    }
  }
}

这部分不做解释了,上问理论部分已经解说了。

源码解读

 ReadableInterface readableInterface = (ReadableInterface) Proxy.newProxyInstance(ReadableInterface.class.getClassLoader(),
                new Class[]{ReadableInterface.class},

Proxy.newProxyInstance

         Objects.requireNonNull(h);
        final Class<?>[] intfs = interfaces.clone();
        //代码权限校验
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        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;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        }catche(){
        }

Class

private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // prefix for all proxy class names
        private static final String proxyClassNamePrefix = "$Proxy";

        // next number to use for generation of unique proxy class names
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                /*
                 * Verify that the Class object actually represents an
                 * interface.
                 * 判断是否是接口
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * Verify that this interface is not a duplicate.
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }

              //假如接口中的方法不是公共的,则生成的代理类存放到con.sun.proxy包下面
            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * 这个就是前文理论部分提到的,一个代理类名字为$Proxy0,第一个为$Proxy1这样的.
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * 生成一个指定代理类
             * 1.动态地生成代理类的字节码数组
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                //2.动态地定义新生成的代理类
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) { 
                throw new IllegalArgumentException(e.toString());
            }
        }
}

动态创建代理类的类对象。首先是确定代理类所在的包,其原则如前所述,如果都为 public 接口,则包名为空字符串表示顶层包;如果所有非 public 接口都在同一个包,则包名与这些接口的包名相同;如果有多个非 public 接口且不同包,则抛异常终止代理类的生成。确定了包后,就开始生成代理类的类名,同样如前所述按格式“ProxyN”生成。类名也确定了,然后就开始动态生成代理类。

说白了就是通过:Class.forName加载类,然后通过反射的Modifier获取方法之类的后去方法。

个人总结

1.动态代理就是根据反射获取需要代理的方法,
2. 再做一些判断,是否共有的私有的,是否final的之类的,然后根据代理接口+方法名+修饰符生成字节码

//interfaces接口名称,
//proxyName代理的方法名
//accessFlags修饰符
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
  1. 最后根据根据字节码生成一个新的类(可以叫他代理类)
return defineClass0(loader, proxyName,  proxyClassFile, 0, proxyClassFile.length);

这个动态创建的类和静态的代理类功能都是一毛一样的,只不过这个类是动态去创建的,而不是想静态代理类那样事先写好的。

4.生成的动态代理类继承了Proxy,并且实现了我们需要代理的接口

public final class $Proxy0
  extends Proxy
  implements ReadableInterface

  .....


  public final void read()
  {
    try
    {
      this.h.invoke(this, m4, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

this.h,这个h就是Proxy类中的属性:protected InvocationHandler h;
这个属性是啥时候赋值的呢?就是我们调用new ProxyObjectImpl();这个就是h啦

private static void proxy2() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        StudentImpl student = new StudentImpl();
        //获取委托方
        ReadableInterface readableInterface = (ReadableInterface) Proxy.newProxyInstance(StudentImpl.class.getClassLoader(),
                StudentImpl.class.getInterfaces(),
                new ProxyObjectImpl(student));
        readableInterface.read();
        readableInterface.write();
    }

所以我们在调用代理类时,代理类又调用了“处理器”,处理器先去处理一些日志啊,其他的东西,然后接着调用了我们真正需要执行的方法。

整个过程就是这样的:

代理类-》”处理器“(可以加一些统一处理的东西,日志,事物等,当然也可以不加)-》”委托类“(真正执行任务的类)。

错误理解:
刚开始学习的时候,我对比了一下动态代理和静态代理。

静态代理类:

动态代理类:

在整个过程中看着是不是很像,而且动态代理还需要写一堆的东西才能调用。我刚开始觉得动态代理还不如静态代理,后来发现网上的例子,和我自己写的例子都是单一场景,单一结构的。而这个时候,动态代理看着肯定还不如静态代理呢。
实际场景中呢?
假如我们现在需要对每个方法进行日志打印,现在不只有一个接口需要代理,还有用户注册接口,购物模块的方法里面都需要记录日志怎么办?
如果是静态代理,我们需要实现一个用户注册接口的代理类,购物模块接口的代理类,其他模块的代理类,而且他们的代码除了继承的接口不一样之外,日志处理流程都是一模一样的,但是我们却需要在每个代理类里面编写,是不是很扯带。而且如果注册接口增加一个方法,代理类也需要更着增加,如果我们现在加入一个物流模块接口,还需要去编写一个物流模块,这是不是又要去编写代码,而且是重复的。

那么动态代理呢?
只需要编写一次处理器就OK了,其他就不用了。之后不管接口中添加了方法,还是添加了其他模块的接口类型,我们都不需要去做什么。

处理器和代理类不是一个哦,处理器只是处理一些事情,看着和代理类差不多,但他们不是一个东西哦。
代理类:是代理某一类接口去处理事情的,比如用户注册代理类是代理用户这册的,用户购物代理类是代理用户购物的。
处理器:是统一处理一些东西,比如日志,安全验证之类的。
动态代理中代理类是通过代码生成的,生成的注册代理类也只是能代理用户注册,生成的购物代理类只能代理用户购物,和静态一样哦。只不过代理类是动态去生成,而不是我们代码编写的。动态生成的代理类里面没有处理逻辑,他们都统一去调用“处理器”,然后处理器再代理他们去做事情。

Cglib字节码代理(基于类的代理)

CGLIB库介绍

代理提供了一个可扩展的机制来控制被代理对象的访问,其实说白了就是在对象访问的时候加了一层封装。JDK从1.3版本起就提供了一个动态代理,它使用起来非常简单,但是有个明显的缺点:需要目标对象实现一个或多个接口。假如你想代理没有接口的类呢?可以使用CGLIB库。

cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理,同样也不能处理私有方法。
cglib(Code Generation Library)是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
cglib封装了asm,可以在运行期动态生成新的class。
cglib用于AOP,jdk中的proxy必须基于接口,cglib却没有这个限制。

它被广泛使用在基于代理的AOP框架(例如Spring AOP和dynaop)提供方法拦截。Hibernate作为最流行的ORM工具也同样使用CGLIB库来代理单端关联(集合懒加载除外,它使用另外一种机制)。EasyMock和jMock作为流行的Java测试库,它们提供Mock对象的方式来支持测试,都使用了CGLIB来对没有接口的类进行代理。

在实现内部,CGLIB库使用了ASM这一个轻量但高性能的字节码操作框架来转化字节码,产生新类。除了CGLIB,像Groovy和BeanShell这样的脚本语言同样使用ASM来生成Java字节码。ASM使用了一个类似于SAX分析器的机制来达到高性能。我们不建议直接使用ASM,因为这样需要对JVM非常了解,包括类文件格式和指令集。

CGLIB API

CGLIB库的代码量不多,2.1.2版本的CGLIB库组织如下所示:

  • net.sf.cglib.core:底层字节码操作类;大部分与ASP相关。
  • net.sf.cglib.transform:编译期、运行期的class文件转换类。
  • net.sf.cglib.proxy:代理创建类、方法拦截类。
  • net.sf.cglib.reflect:更快的反射类、C#风格的代理类。
  • net.sf.cglib.util:集合排序工具类
  • net.sf.cglib.beans:JavaBean相关的工具类
    对于创建动态代理,大部分情况下你只需要使用proxy包的一部分API即可。

上面已经提到,CGLIB库是基于ASM的上层应用。对于代理没有实现接口的类,CGLIB非常实用。
本质上来说,对于需要被代理的类,它只是动态生成一个子类以覆盖非final的方法,同时绑定钩子回调自定义的拦截器。
由于使用ASM来生成动代理,所以比JDK动态代理还要快。

CGlib代理

  1. Enhancer 是CGLib的字节码增强器,可以方便的对类进行扩展,内部调用GeneratorStrategy.generate方法生成代理类的字节码。通过设置setSuperClass和setCallback, 设置好了SuperClass后, 可以使用create制作代理对象.
  2. CallbackFilter 可以实现不同的方法使用不同的回调方法, CallbackFilter中的accept方法, 根据不同的method返回不同的值i, 这个值是在callbacks中的顺序, 就是调用了callbacks[i].
  3. Mixin 这是一种将多个接口混合在一起的方式, 实现了多个接口.这种方式是一种多继承的替代方案, 很大程度上解决了多继承的很多问题, 实现和理解起来都比较容易.
  4. BeanCopier 用来对象之间拷贝属性.

Callback类型

  • net.sf.cglib.proxy.MethodInterceptor(使用最多的一种类型,属于自定义类型)满足了所有的代理需求,但对于某些特定场景它可能使用起来不太方便。为了方便使用和高性能,CGLIB提供了另外一些特殊的回调类型。例如,

  • net.sf.cglib.proxy.FixedValue:在强制一个特定方法返回固定值,在特定场景下非常有用且性能高。

  • net.sf.cglib.proxy.NoOp:它直接透传到父类的方法实现。
  • net.sf.cglib.proxy.LazyLoader:在被代理对象需要懒加载场景下非常有用,如果被代理对象加载完成,那么在以后的代理调用时会重复使用。
  • net.sf.cglib.proxy.Dispatcher:与net.sf.cglib.proxy.LazyLoader差不多,但每次调用代理方法时都会调用loadObject方法来加载被代理对象。
  • net.sf.cglib.proxy.ProxyRefDispatcher:与Dispatcher相同,但它的loadObject方法支持传入代理对象。

代码Dome

查看动态代理生成的class文件

在调用cglib生成字节码前执行如下代码,即可让cglib生成的代理类存放到target/cglib文件夹下。

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, “target/cglib”);

Dome

1.业务类

public class Person {

    public void play(){
        System.out.println("person is play ....");
    }

    public void getMoney(){
        System.out.println("give me 100$");
    }
}

2.设置一个CGLIB钩子(和jdk中的处理器是一样的)
这个钩子必须实现MethodInterceptor这个接口

public class CglibProxyHandler implements MethodInterceptor {
    public CglibProxyHandler() {
    }

    //1、代理对象;2、委托类方法;3、方法参数;4、代理方法的MethodProxy对象。
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("proxy before......");
        Object o1 = methodProxy.invokeSuper(o, objects);
        System.out.println("proxy after .......");
        return o1;
    }
}

3.main方法

public class CglibProxyDome {

    public static void main(String[] args) {
        //指定生成的代理文件存放地址
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "target/cglib");

        CglibProxyHandler cglibProxy = new CglibProxyHandler();
        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(Person.class);
        //设置回调对象
        enhancer.setCallback(cglibProxy);
        Person person = (Person) enhancer.create();
        person.play();
        person.getMoney();
    }
}

cglib使用的是继承委托类来实现的,所以不能处理私有类,final关键字修饰的方法或类

jdk需要提供接口,cglib需要是非私有类,且不能处理final关键字修饰的方法

4.查看动态生成的子类
代码太多了就不都粘贴出来了

public class Person$$EnhancerByCGLIB$$9da69ec0
  extends Person
  implements Factory
  ...

  final void CGLIB$play$0()
  {
    super.play();
  }

  public final void play()
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    if (this.CGLIB$CALLBACK_0 != null) {
      return;
    }
    super.play();
  }

  final void CGLIB$getMoney$1()
  {
    super.getMoney();
  }

  public final void getMoney()
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    if (this.CGLIB$CALLBACK_0 != null) {
      return;
    }
    super.getMoney();
  }

  final boolean CGLIB$equals$2(Object paramObject)
  {
    return super.equals(paramObject);
  }

  public final boolean equals(Object paramObject)
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
    if (tmp17_14 != null)
    {
      Object tmp41_36 = tmp17_14.intercept(this, CGLIB$equals$2$Method, new Object[] { paramObject }, CGLIB$equals$2$Proxy);
      tmp41_36;
      return tmp41_36 == null ? false : ((Boolean)tmp41_36).booleanValue();
    }
    return super.equals(paramObject);
  }

  final String CGLIB$toString$3()
  {
    return super.toString();
  }

  public final String toString()
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
    if (tmp17_14 != null) {
      return (String)tmp17_14.intercept(this, CGLIB$toString$3$Method, CGLIB$emptyArgs, CGLIB$toString$3$Proxy);
    }
    return super.toString();
  }

  final int CGLIB$hashCode$4()
  {
    return super.hashCode();
  }

  public final int hashCode()
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
    if (tmp17_14 != null)
    {
      Object tmp36_31 = tmp17_14.intercept(this, CGLIB$hashCode$4$Method, CGLIB$emptyArgs, CGLIB$hashCode$4$Proxy);
      tmp36_31;
      return tmp36_31 == null ? 0 : ((Number)tmp36_31).intValue();
    }
    return super.hashCode();
  }

  final Object CGLIB$clone$5()
    throws CloneNotSupportedException
  {
    return super.clone();
  }

  protected final Object clone()
    throws CloneNotSupportedException
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
    if (tmp17_14 != null) {
      return tmp17_14.intercept(this, CGLIB$clone$5$Method, CGLIB$emptyArgs, CGLIB$clone$5$Proxy);
    }
    return super.clone();
  }

通过cglib生成的字节码相比jdk实现来说显得更加复杂。
1、代理类Person

E n h a n c e r B y C G L I B
9da69ec0继承了委托类Person,且委托类的final方法不能被代理;
2、代理类为每个委托方法都生成两个方法,以play方法为例,一个是重写的play方法,一个是CGLIB p l a y 0方法,该方法直接调用委托类的play方法;
3、当执行代理对象的play方法时,会先判断是否存在实现了MethodInterceptor接口的对象cglib-CALLBACK_0,如果存在,则调用MethodInterceptor对象的intercept方法.
4、每个被代理的方法都对应一个MethodProxy对象,methodProxy.invokeSuper方法最终调用委托类的play方法。

说白了就是先看看有没有cglib-CALLBACK_0,如果则执行它,而cglib-CALLBACK_0最终还是需要调用委托类的play方法。它只是一个钩子,可以统一处理一些日志啊,权限啊之类的事之后再去调用委托类的方法。

dome2

在原先的dome1上面添加一个过滤器
1.过滤器

public class CglibProxyFilter implements CallbackFilter {

    @Override
    public int accept(Method method) {
        if("getMoney".equalsIgnoreCase(method.getName())){
            return 0;
        }
        return 1;
    }
}

2.main方法

public class CglibProxyDome {

    public static void main(String[] args) {
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "target/cglib");
        CglibProxyHandler cglibProxy = new CglibProxyHandler();

        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(Person.class);
        //设置了2个回调钩子
        enhancer.setCallbacks(new Callback[]{cglibProxy, NoOp.INSTANCE});
        enhancer.setCallbackFilter(new CglibProxyFilter());

        Person person = (Person) enhancer.create();
        person.play();
        person.getMoney();
    }
}

现在enhancer.setCallbacks(new Callback[]{cglibProxy, NoOp.INSTANCE});有两个钩子了,我们到底使用那个呢?
这个就取决于enhancer.setCallbackFilter过滤器里面的返回值了。这个返回值是0,1.
对应了Callback[0]= cglibProxy,Callback[1]=NoOp.INSTANCE。
这里需要注意哦,顺序是我们自己说的算的,所以需要配合注意顺序哦。0,1到底使用那个钩子,自己编码时决定。如果有3个钩子,就可以返回0,1,2这样来处理了。

FastClass实现机制(转载)

在前面定义的钩子里面执行的是:methodProxy.invokeSuper(o, objects);
进入远invokeSuper源码会发现它是一个fastClass,它是怎么提高效率的呢?

FastClass其实就是对Class对象进行特殊处理,提出下标概念index,通过索引保存方法的引用信息,将原先的反射调用,转化为方法的直接调用,从而体现所谓的fast,下面通过一个例子了解一下FastClass的实现机制。
1、定义原类

class Test {
    public void f(){
        System.out.println("f method");
    }

    public void g(){
        System.out.println("g method");
    }
}

2、定义Fast类

class FastTest {
    public int getIndex(String signature){
        switch(signature.hashCode()){
        case 3078479:
            return 1;
        case 3108270:
            return 2;
        }
        return -1;
    }

    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;
    }
}

在FastTest中有两个方法,getIndex中对Test类的每个方法根据hash建立索引,invoke根据指定的索引,直接调用目标方法,避免了反射调用。所以当调用methodProxy.invokeSuper方法时,实际上是调用代理类的CGLIB p l a y 0方法,CGLIB p l a y 0直接调用了委托类的play方法。

jdk和cglib动态代理实现的区别

1、jdk动态代理生成的代理类和委托类实现了相同的接口;
2、cglib动态代理中生成的字节码更加复杂,生成的代理类是委托类的子类,且不能处理被final关键字修饰的方法和类;
3、jdk采用反射机制调用委托类的方法,cglib采用类似索引的方式直接调用委托类方法;

参考地址

http://blog.csdn.net/hejingyuan6/article/details/36203505动静对比
https://www.ibm.com/developerworks/cn/java/j-lo-proxy1/index.htmljava JDK代理分析全面(大部分参考这个上面的)
http://www.importnew.com/23168.html

http://ifeve.com/cglib-desc/ cglib讲解很全面
http://panyongzheng.iteye.com/blog/1673575cglib应用讲解很清晰
http://blog.jobbole.com/105423/

猜你喜欢

转载自blog.csdn.net/piaoslowly/article/details/81909741