动态代理是一种在运行时动态生成代理类的代理方式。动态代理的常用实现方法有以下两种。

1. JDK 动态代理

JDK 动态代理是一种使用 Java 标准库中的 java.lang.reflect.Proxy 类来实现动态代理的技术。在 JDK 动态代理中,被代理类必须实现一个或多个接口,并通过 InvocationHandler 接口来实现代理类的具体逻辑。

具体来说,当使用 JDK 动态代理时,需要定义一个实现 InvocationHandler 接口的类,并在该类中实现代理类的具体逻辑。然后,通过 Proxy.newProxyInstance() 方法来创建代理类的实例。该方法接受三个参数:类加载器、代理类要实现的接口列表和 InvocationHandler 对象,如下代码所示:

import org.example.demo.service.AliPayService;
import org.example.demo.service.PayService;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//动态代理:使用JDK提供的api(InvocationHandler、Proxy实现),此种方式实现,要求被代理类必须实现接口
public class PayServiceJDKInvocationHandler implements InvocationHandler {
    
    //目标对象即就是被代理对象
    private Object target;
    
    public PayServiceJDKInvocationHandler( Object target) {
        this.target = target;
    }
    
    //proxy代理对象
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //1.安全检查
        System.out.println("安全检查");
        //2.记录日志
        System.out.println("记录日志");
        //3.时间统计开始
        System.out.println("记录开始时间");

        //通过反射调用被代理类的方法
        Object retVal = method.invoke(target, args);

        //4.时间统计结束
        System.out.println("记录结束时间");
        return retVal;
    }

    public static void main(String[] args) {

        PayService target=  new AliPayService();
        //方法调用处理器
        InvocationHandler handler = 
            new PayServiceJDKInvocationHandler(target);
        //创建一个代理类:通过被代理类、被代理实现的接口、方法调用处理器来创建
        PayService proxy = (PayService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                new Class[]{PayService.class},
                handler
        );
        proxy.pay();
    }
}

JDK 动态代理的优点是实现简单,易于理解和掌握,但是它的缺点是只能代理实现了接口的类,无法代理没有实现接口的类。

2. CGLIB 动态代理

CGLIB 动态代理是一种使用 CGLIB 库来实现动态代理的技术。在 CGLIB 动态代理中,代理类不需要实现接口,而是通过继承被代理类来实现代理。 具体来说,当使用 CGLIB 动态代理时,需要定义一个继承被代理类的子类,并在该子类中实现代理类的具体逻辑。然后,通过 Enhancer.create() 方法来创建代理类的实例。该方法接受一个类作为参数,表示要代理的类,如下代码所示:

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.example.demo.service.AliPayService;
import org.example.demo.service.PayService;

import java.lang.reflect.Method;

public class PayServiceCGLIBInterceptor implements MethodInterceptor {

    //被代理对象
    private Object target;
    
    public PayServiceCGLIBInterceptor(Object target){
        this.target = target;
    }
    
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //1.安全检查
        System.out.println("安全检查");
        //2.记录日志
        System.out.println("记录日志");
        //3.时间统计开始
        System.out.println("记录开始时间");

        //通过cglib的代理方法调用
        Object retVal = methodProxy.invoke(target, args);

        //4.时间统计结束
        System.out.println("记录结束时间");
        return retVal;
    }
    
    public static void main(String[] args) {
        PayService target=  new AliPayService();
        PayService proxy= (PayService) Enhancer.create(target.getClass(),new PayServiceCGLIBInterceptor(target));
        proxy.pay();
    }
}

CGLIB 动态代理的优点是可以代理没有实现接口的类,但是它的缺点是实现相对复杂,需要了解 CGLIB 库的使用方法。

小结

综上所述,动态代理的实现方法主要有 JDK 动态代理和 CGLIB 动态代理。JDK 动态代理中,代理类必须实现一个或多个接口,而 CGLIB 动态代理中,代理类不需要实现接口,但代理类不能是 final 类型,因为它是通过定义一个被代理类的子类来实现动态代理的,因此开发者需要根据具体的需求选择合适的技术来实现动态代理。

特殊说明

以上内容来自我的《Java 面试突击训练营》,这门课程是有着十几年工作经验(前 360 开发工程师),10 年面试官经验的我,花费 4 年时间打磨完成的一门视频面试课。学完训练营的课程之后,基本可以应对目前市面上绝大部分公司的面试了,并且课程配备了 9 大就业服务,帮助上千人找到 Java 工作,其中上百人拿到大厂 Offer,学员最高薪资 70W 年薪,面试课目录和 9 大服务如下:

加我微信咨询:vipStone【备注:训练营】