动态代理、JDK动态代理、CGLIB动态代理,从例子理解动态代理

代理模式

​ 代理模式的主要作用是不想在原有的类上做扩展,所以可以借助一个代理类来调用原有类的方法,然后将扩展内容在代理类中进行扩展(增强)。其中代理对象起到调用者和目标类之间的一个桥梁作用(类似于中介)。

​ 动态代理更为灵活,无需对每个需要扩展的类(目标类)单独创建一个代理类,也不要我们实现目标类的接口,可以直接代理实现类。

​ 从jvm角度理解,动态代理是运行时动态生成字节码文件,并加载入jvm中。 java中主要有两种动态代理方法,一种jdk动态代理,另一种就是CGLib动态代理。

例子

​ 有一个接口SendMessage接口,其中有一个发送短信方法sendDefineMsg(String msg),我们想要在客户端调用发送短信的方法,并且在发送短信时,做一些扩展内容,但是不想在SendMessage接口实现类SendMessageImpl中增加代码。那么如何通过动态代理来实现呢?

public interface SendMessage {
    public void SendDefineMessage(String msg);
}
public class SendMessageImpl implements  SendMessage {
    public void SendDefineMessage(String msg) {
        System.out.println("send the message: "+msg);
    }
}

下面我们从动态代理的两种主要方式来解决该问题:

jdk动态代理

​ jdk动态代理的核心主要是invocationHandler接口和proxy类。

​ 其中invocationHandler接口起到一个类似于中转的作用,在调用目标类的方法时,需要先要调用invoke(Object proxy, Method method, Object[] args)方法(invocationHandler接口必须要实现的方法), 那么要扩展的内容可以就在invoke()方法中进行增强。其中proxy是代理类的对象,method用于发起对目标类中方法的调用,args为调用方法的参数。

proxy类用于生成代理类对象。其中借助newProxyInstance(ClassLoader loader, Class<?> intefaces, InvocationHandler h)可以生成目标类的代理类对象。loader是目标类的类加载器, interfaces为目标类实现的接口集合, hinvocationHandler的实现类对象。

根据上面jdk动态代理提供的相关接口和类,可以按照下面的步骤来使用动态代理实现短信发送的增强:

  1. 自定义实现InvocationHandler接口,并实现invoke()方法
public class ProxyInvocationHandlerImpl implements InvocationHandler {
    // 被代理的类对象
    private Object object;

    public ProxyInvocationHandlerImpl(Object object){
        this.object = object;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 执行被代理类所调用的方法之前扩展的内容
        System.out.println("执行sendMessage()之前:......");
        // 代理类会调用invoke()发放,通过向invoke()方法中传入被代理类的对象,从而实现被代理类中的方法
        Object res = method.invoke(object, args);
        // 执行被代理类所调用的方法之后扩展的内容
        System.out.println("执行sendMessage()之后:......");
        return res;
    }
}
  1. 通过proxy类生成代理对象
public class ProxyFactory {
    // 这里传入的object是被代理的类对象
    public static Object GetStaticProxy(Object object){
        return Proxy.newProxyInstance(
                // 被代理类的类加载器
                object.getClass().getClassLoader(),
                // 被代理类所实现的接口
                object.getClass().getInterfaces(),
                // 自定义的InvocationHandler实现类
                new ProxyInvocationHandlerImpl(object)
                );
    }
}
  1. 通过代理对象调用短信发送方法
public class Test {
    public static void main(String[] args) {
        // 被代理的类对象
        SendMessage sendMessage = new SendMessageImpl();
        // 代理调用sendMessage()方法
        SendMessage proxy = (SendMessage) ProxyFactory.GetProxy(new SendMessageImpl());
        proxy.SendDefineMessage("消息");
    }
}

//执行sendMessage()之前:......
//send the message: 消息
//执行sendMessage()之后:......

注意:jdk动态代理只能代理接口实现类,若想要代理未实现接口类中的方法时,就需要通过下面的CGLIB动态代理。

CGLib动态代理

CGLIBopen in new window(Code Generation Library)是一个基于ASMopen in new window的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式实现代理。很多知名的开源框架都使用到了CGLIBopen in new window, 例如 Spring 中的 AOP 模块中:如果目标对象实现了接口,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理。
CGLIB动态代理的核心是MethodInterceptor接口和Enhancer类。

​ 其中MethodInterceptor接口内部包含一个类似于invoke()方法的intercept()方法,用于拦截增强被代理类的方法。intercept(Obbject obj, Method method, Object[] args, MethodProxy proxy)。其中obj为被代理的对象,method被拦截的方法,args方法的参数,proxy用于调用原始方法

Enhancer类用于生成代理对象。

​ 由于CGLIB动态代理最大的不同之处在于,其可以代理未实现任何接口的类。我们将例子中的发送短信接口SendMessage改为类。

public class SendMessage {
    public void SendDefineMessage(String msg) {
        System.out.println("send the message: "+msg);
    }
}
  1. 实现Interceptor接口,并实现intercept()方法
public class MethodInterceptorImpl implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // 执行被代理类所调用的方法之前扩展的内容
        System.out.println("执行sendMessage()之前:......"+method.getName());
        // 代理类会调用invoke()发放,通过向invoke()方法中传入被代理类的对象,从而实现被代理类中的方法
        Object res = methodProxy.invokeSuper(o, args);
        // 执行被代理类所调用的方法之后扩展的内容
        System.out.println("执行sendMessage()之后:......"+method.getName());
        return res;
    }
}
  1. 使用Enhancer类生成代理对象
public class ProxyFactory {

    public static Object GetProxy(Class<?> clazz){
        // 创建动态代理增强类
        Enhancer enhancer = new Enhancer();
        // 设置类加载器
        enhancer.setClassLoader(clazz.getClassLoader());
        // 设置被代理类
        enhancer.setSuperclass(clazz);
        // 设置方法拦截器
        enhancer.setCallback(new MethodInterceptorImpl());
        // 创建代理类
        return enhancer.create();
    }
}
  1. 使用代理对象调用调用短信发送方法
public class Test {
    public static void main(String[] args) {
        SendMessage sendMessage = (SendMessage) ProxyFactory.GetProxy(SendMessage.class);
        sendMessage.SendDefineMessage("消息2");
    }
}

//执行sendMessage()之前:......SendDefineMessage
//send the message: 消息2
//执行sendMessage()之后:......SendDefineMessage

注意:CGLIB是一个开源项目,使用前需要导入依赖

<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>3.3.0</version>
</dependency>
两者的对比
  • JDK动态代理实现了接口的类或者直接代理接口,而CGLIB可以代理未实现任何接口的类。另外CGLIB是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理被final修饰的类和方法。
  • 大部分情况下,jdk动态代理的效率更加优秀。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值