动态代理之jdk和cglib

代理模式

代理模式的定义:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。


代理模式的主要优点有:

  • 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
  • 代理对象可以扩展目标对象的功能;
  • 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性

其主要缺点是:

  • 代理模式会造成系统设计中类的数量增加
  • 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
  • 增加了系统的复杂度;

代理模式的结构与实现

代理模式的结构比较简单,主要是通过定义一个继承抽象主题的代理来包含真实主题,从而实现对真实主题的访问,下面来分析其基本结构和实现方法。

1. 模式的结构

代理模式的主要角色如下。

  1. 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
  2. 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
  3. 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

其结构图如图 1 所示。

生活中的场景

  1. 房东:真实主题(Real Subject)类,就是代理模式中的真实对象。
  2. 中介:代理(Proxy)类,他可以出租房东房子,可以对房子进行装修,定时打扫增强功能。
  3. 租客类:客户端,他直接从中介那里租房子。

JDK动态代理

利用Java的反射技术(Java Reflection),在运行时创建一个实现某些给定接口的新类(也称“动态代理类”)及其实例(对象),代理的是接口(Interfaces),不是类(Class),也不是抽象类。在运行时才知道具体的实现,spring aop就是此原理。

一、Proxy.newProxyInstance

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException

newProxyInstance,方法有三个参数:

loader: 用哪个类加载器去加载代理对象

interfaces:动态代理类需要实现的接口

h:动态代理方法在执行时,会调用h里面的invoke方法去执行

二、使用Proxy实现:

  •   定义接口
public interface PersonService {
    void hello();
}
  • 定义实现类
/**
 * PersonService实现类
 *
 * @author yangyanping
 * @date 2022-07-04
 */
public class PersonServiceImpl implements PersonService {
    public void hello(){
        System.out.println("Hello world !");
    }
}
  • 定义InvocationHandler 实现类 
/**
 * InvocationHandler 实现类
 * 
 * @author yangyanping 
 * @date 2022-07-04
 */
public class TargetInvocationHandler implements InvocationHandler {
    private Object target;

    public TargetInvocationHandler(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);

        System.out.println(" 调用后" + result);

        return result;
    }
}
  •    Proxy 
public class Proxy implements java.io.Serializable {

    private static final long serialVersionUID = -2222568056686623797L;

    /** parameter types of a proxy class constructor */
    private static final Class<?>[] constructorParams =
        { InvocationHandler.class };

    /**
     * a cache of proxy classes
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

    /**
     * the invocation handler for this proxy instance.
     * @serial
     */
    protected InvocationHandler h;

    /**
     * Prohibits instantiation.
     */
    private Proxy() {
    }

    /**
     * Constructs a new {@code Proxy} instance from a subclass
     * (typically, a dynamic proxy class) with the specified value
     * for its invocation handler.
     *
     * @param  h the invocation handler for this proxy instance
     *
     * @throws NullPointerException if the given invocation handler, {@code h},
     *         is {@code null}.
     */
    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }
}
  • 测试代理类 
/**
 * 测试类
 *
 * @author yangyanping
 * @date 2022-07-04
 */
public class TestProxy {
    public static void main(String[] args) {
        System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
        System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
        PersonService personService = new PersonServiceImpl();

        ClassLoader loader = TestProxy.class.getClassLoader();
        Class<?>[] interfaces = personService.getClass().getInterfaces();

        PersonService proxy = (PersonService) Proxy.newProxyInstance(loader, interfaces, new TargetInvocationHandler(personService));

        proxy.hello();
    }
}
  • 代理类源码 
package com.sun.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.jdk.PersonService;

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

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void hello() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("proxy.jdk.PersonService").getMethod("hello");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

  我们主要看下hello方法的代码,它调用了 Proxy.InvocationHandler#invoke 方法,就是调用了我们编写的 TargetInvocationHandler 的 invoke 方法,在 invoke 方法中使用反射 调用目标对象的方法,所以 JDK的动态代理是基于反射实现的。

public final void hello() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

二、ProxyFactory实现: 

/**
 * JDK动态代理工厂
 *
 * @author yangyanping
 * @date 2022-07-05
 */
public class ProxyFactory {

    /**
     * 维护一个目标对象 , Object
     */
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    /**
     * 1. ClassLoader loader : 指定当前目标对象使用的类加载器, 获取加载器的方法固定
     * 2. Class<?>[] interfaces: 目标对象实现的接口类型,使用泛型方法确认类型
     * 3. InvocationHandler h : 事情处理,执行目标对象的方法时,会触发事情处理器方法, 会把当前执行的目标对象方法
     */
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("JDK代理开始~~");
                        //反射机制调用目标对象的方法
                        Object returnVal = method.invoke(target, args);
                        System.out.println("JDK代理提交");
                        return returnVal;

                    }
                });
    }
}

CGLIB

什么是 CGLIB?

CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。

CGLIB作为一个开源项目,其代码托管在github,地址为:https://github.com/cglib/cglib

CGLIB 原理

CGLIB 原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。

CGLIB 底层:使用字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

CGLIB缺点:对于final方法,无法进行代理。

CGLIB 的应用

广泛的被许多AOP的框架使用,例如Spring AOP

为什么使用 CGLIB?

CGLIB代理主要通过对字节码的操作,为对象引入间接级别,以控制对象的访问。我们知道Java中有一个动态代理也是做这个事情的,那我们为什么不直接使用Java动态代理,而要使用CGLIB呢?答案是CGLIB相比于JDK动态代理更加强大,JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了。

CGLIB组成结构

CGLIB底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类。除了CGLIB库外,脚本语言(如Groovy)也使用ASM生成字节码。ASM使用类似SAX的解析器来实现高性能。我们不鼓励直接使用ASM,因为它需要对Java字节码的格式足够的了解。

CGLIB的API

1、Jar包:

  • cglib-nodep-2.2.jar:使用nodep包不需要关联asm的jar包,jar包内部包含asm的类.
  • cglib-2.2.jar:使用此jar包需要关联asm的jar包,否则运行时报错.

 2、CGLIB类库:

由于基本代码很少,学起来有一定的困难,主要是缺少文档和示例,这也是CGLIB的一个不足之处。

本系列使用的CGLIB版本是2.2。

  • net.sf.cglib.core: 底层字节码处理类,他们大部分与ASM有关系。
  • net.sf.cglib.transform: 编译期或运行期类和类文件的转换
  • net.sf.cglib.proxy: 实现创建代理和方法拦截器的类
  • net.sf.cglib.reflect: 实现快速反射和C#风格代理的类
  • net.sf.cglib.util: 集合排序等工具类
  • net.sf.cglib.beans: JavaBean相关的工具类

本篇介绍通过MethodInterceptor和Enhancer实现一个动态代理。

一、首先说一下JDK中的动态代理:

JDK中的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的,但是,JDK中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高。

二、使用CGLib实现:

使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。

下面,将通过一个实例介绍使用CGLib实现动态代理。

1、被代理类:

首先,定义一个类,该类没有实现任何接口。

/**
 * 没有实现接口,需要CGlib动态代理的目标类
 *
 * @author yangyanping
 * @date 2022-07-04
 */
public class PersonServiceImpl {
    public void hello(){
        System.out.println("Hello world !");
    }
}

2、拦截器:

定义一个拦截器。在调用目标方法时,CGLib会回调MethodInterceptor接口方法拦截,来实现你自己的代理逻辑,类似于JDK中的InvocationHandler接口。

/**
 * 目标对象拦截器,实现MethodInterceptor
 *
 * @author yangyanping
 * @date 2022-07-04
 */
public class TargetInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("调用前");
        // 调用父类方法
        Object result = methodProxy.invokeSuper(o, objects);

        // 调用代理,死循环
        //methodProxy.invoke(o, objects); 
        System.out.println(" 调用后" + result);
        
        return result;
    }
}

参数:Object为由CGLib动态生成的代理类实例,Method为上文中实体类所调用的被代理的方法引用,Object[]为参数值列表,MethodProxy为生成的代理类对方法的代理引用。

返回:从代理实例的方法调用返回的值。

其中,proxy.invokeSuper(obj,arg) 调用代理类实例上的proxy方法的父类方法(即实体类TargetObject中对应的方法)

在这个示例中,只在调用被代理类方法前后各打印了一句话,当然实际编程中可以是其它复杂逻辑。

3、生成动态代理类:

/**
 * Cglib 测试类
 *
 * @author yangyanping
 * @date 2022-07-04
 */
public class TestCglib {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(PersonServiceImpl.class);
        enhancer.setCallback(new TargetInterceptor());

        PersonServiceImpl personService =  (PersonServiceImpl) enhancer.create();

        personService.hello();
    }
}

这里Enhancer类是CGLib中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展,以后会经常看到它。

首先将被代理类TargetObject设置成父类,然后设置拦截器TargetInterceptor,最后执行enhancer.create()动态生成一个代理类,并从Object强制转型成父类型TargetObject。

最后,在代理类上调用方法。

4、代理类代码:

 添加System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, path); 可以生成代理类代码

/**
 * Cglib 测试类
 *
 * @author yangyanping
 * @date 2022-07-04
 */
public class TestCglib {
    public static void main(String[] args) {
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/yangyanping/Downloads/code/ql-er/demo");
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(PersonServiceImpl.class);
        enhancer.setCallback(new TargetInterceptor());

        PersonServiceImpl personService =  (PersonServiceImpl) enhancer.create();

        personService.hello();
    }
}

  生成三个类,分别是

PersonServiceImpl$$EnhancerByCGLIB$$edc8f724$$FastClassByCGLIB$$6cca272d
PersonServiceImpl$$EnhancerByCGLIB$$edc8f724
PersonServiceImpl$$FastClassByCGLIB$$46bf7c40

4、代理工厂ProxyFactory

/**
 * Cglib动态代理工厂
 *
 * @author yangyanping
 * @date 2022-07-05
 */
public class ProxyFactory implements MethodInterceptor {
    /**
     * 维护一个目标对象
     */
    private Object target;

    /**
     * 构造器,传入一个被代理的对象
     */
    public ProxyFactory(Object target) {
        this.target = target;
    }

    /**
     * 返回一个代理对象:  是 target 对象的代理对象
     */
    public Object getProxyInstance() {
        //1. 创建一个工具类
        Enhancer enhancer = new Enhancer();
        //2. 设置父类
        enhancer.setSuperclass(target.getClass());
        //3. 设置回调函数
        enhancer.setCallback(this);
        //4. 创建子类对象,即代理对象
        return enhancer.create();
    }

    /**
     * 重写  intercept 方法,会调用目标对象的方法
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("Cglib代理模式 ~~ 开始");
        Object returnVal = methodProxy.invokeSuper(o, objects);
        System.out.println("Cglib代理模式 ~~ 提交");

        return returnVal;
    }
}

猜你喜欢

转载自blog.csdn.net/yangyanping20108/article/details/125587498
今日推荐