MST

星途 面试题库

面试题:Java动态代理机制深度剖析

深入阐述Java动态代理机制,包括JDK动态代理和CGLIB动态代理的原理、实现方式以及它们的区别与应用场景。请详细说明在JDK动态代理中InvocationHandler接口和Proxy类的作用及使用方法,在CGLIB动态代理中Enhancer类的关键作用。给出具体的代码示例展示如何在不同场景下合理使用这两种动态代理技术,并且说明如果要优化动态代理的性能,你会从哪些方面入手。
39.7万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

Java动态代理机制

  1. JDK动态代理
    • 原理:JDK动态代理是基于接口实现的。它利用反射机制在运行时创建代理类的字节码,该代理类实现了目标接口,并在代理类的方法中调用InvocationHandler的invoke方法来处理实际的业务逻辑。
    • 实现方式
      • 定义一个接口,目标类实现该接口。
      • 创建一个实现InvocationHandler接口的类,在invoke方法中编写代理逻辑。
      • 使用Proxy类的静态方法newProxyInstance创建代理对象。
    • InvocationHandler接口:该接口只有一个invoke方法,代理对象的方法调用都会转发到这个invoke方法。invoke方法接收三个参数:代理对象本身、被调用的方法对象、方法的参数数组。通过反射调用目标对象的方法,并可以在前后添加额外的逻辑。
    • Proxy类:提供了创建代理对象的静态方法newProxyInstance。该方法接收三个参数:类加载器、目标对象实现的接口数组、InvocationHandler实现类的实例。通过这个方法创建的代理对象会实现指定接口,并将方法调用转发给InvocationHandler。
  2. CGLIB动态代理
    • 原理:CGLIB动态代理是基于继承实现的。它通过字节码生成库,在运行时生成目标类的子类,并重写目标类的方法,在子类方法中加入代理逻辑。
    • 实现方式
      • 创建一个目标类(可以是普通类,不一定非要实现接口)。
      • 创建一个实现MethodInterceptor接口的类,在intercept方法中编写代理逻辑。
      • 使用Enhancer类来设置目标类、回调接口等信息,并创建代理对象。
    • Enhancer类:CGLIB的核心类之一,用于生成代理类。通过设置目标类、回调接口等属性,调用create方法生成代理对象。它会在运行时动态生成目标类的子类,并将方法调用委托给MethodInterceptor。
  3. 区别
    • JDK动态代理:要求目标对象必须实现接口,代理类实现相同接口;基于反射,性能相对较低。
    • CGLIB动态代理:目标对象可以是普通类,代理类是目标类的子类;基于字节码生成,性能相对较高。
  4. 应用场景
    • JDK动态代理:适用于目标对象实现了接口的场景,如常见的业务逻辑接口。
    • CGLIB动态代理:适用于目标对象没有实现接口的场景,或者对性能要求较高的场景。

代码示例

  1. JDK动态代理示例
// 定义接口
interface HelloService {
    void sayHello();
}

// 目标类实现接口
class HelloServiceImpl implements HelloService {
    @Override
    public void sayHello() {
        System.out.println("Hello!");
    }
}

// InvocationHandler实现类
class HelloInvocationHandler implements InvocationHandler {
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method invocation");
        Object result = method.invoke(target, args);
        System.out.println("After method invocation");
        return result;
    }
}

// 创建代理对象并调用方法
public class JDKProxyExample {
    public static void main(String[] args) {
        HelloService target = new HelloServiceImpl();
        HelloInvocationHandler handler = new HelloInvocationHandler(target);
        HelloService proxy = (HelloService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                handler);
        proxy.sayHello();
    }
}
  1. CGLIB动态代理示例
// 目标类
class HelloService {
    public void sayHello() {
        System.out.println("Hello!");
    }
}

// MethodInterceptor实现类
class HelloMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Before method invocation");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("After method invocation");
        return result;
    }
}

// 创建代理对象并调用方法
public class CGLIBProxyExample {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(HelloService.class);
        enhancer.setCallback(new HelloMethodInterceptor());
        HelloService proxy = (HelloService) enhancer.create();
        proxy.sayHello();
    }
}

性能优化方面

  1. 缓存代理对象:避免重复创建代理对象,减少创建代理对象的开销。
  2. 减少反射调用:在InvocationHandler或MethodInterceptor中,尽量减少反射调用的次数,例如可以缓存Method对象。
  3. 使用合适的代理方式:根据场景选择JDK动态代理或CGLIB动态代理,对于性能要求高且目标类未实现接口的场景,优先选择CGLIB。
  4. 优化字节码生成:对于CGLIB,可以通过调整字节码生成的参数来优化性能,如设置合适的缓存策略。