likes
comments
collection
share

简单说一下动态代理

作者站长头像
站长
· 阅读数 7

这个也是去年面试遇到的问题,年过了记录一下,西安不知名的小公司,名字都忘记了,记得当时回答上来了,但是不够全面。

Java 中的动态代理是一种在运行时创建代理对象的机制,这使得我们可以在运行时动态地创建和使用接口的代理对象,而无需在编译时确定具体的实现类。动态代理通常用于实现 AOP(面向切面编程)和其他类似的横切关注点。

简述

它允许在运行时动态地创建代理类和代理对象,从而可以在代理对象上拦截对真实对象的方法调用,并在调用前后执行自定义的逻辑。动态代理通常用于实现一些横切关注点(cross-cutting concerns),如日志记录、性能监控、事务管理、安全检查等。

动态代理的一些常见应用包括

  1. AOP(面向切面编程): 动态代理是 AOP 的核心实现机制之一。通过动态代理,可以在不修改原始类代码的情况下,为类的方法添加额外的功能,如日志记录、性能监控、事务管理等。
  2. 远程代理: 动态代理可以用于实现远程调用(RPC)。在远程调用中,客户端通过代理对象调用远程服务器上的方法,而不需要知道具体的网络通信细节。
  3. 延迟加载: 动态代理可以用于延迟加载对象。在某些情况下,我们可能希望在首次访问对象时才进行初始化,而不是在对象创建时立即进行初始化。
  4. 安全控制: 动态代理可以用于实现安全控制机制。例如,可以在代理对象中检查用户的权限,然后决定是否允许执行特定的操作。
  5. 缓存: 动态代理可以用于实现缓存机制。例如,可以在代理对象中检查是否存在缓存结果,并在缓存中找到结果时直接返回,而不需要执行真正的方法调用。

为什么需要动态代理呢? 动态代理的出现主要是为了实现面向切面编程(AOP)中的横切关注点(cross-cutting concerns)。在实际开发中,很多功能可能会散布在应用程序的多个模块中,例如日志记录、事务管理、权限控制等。如果每个模块都直接嵌入到业务逻辑中,会导致代码重复、耦合度高、维护困难等问题。动态代理通过将这些横切关注点与核心业务逻辑分离开来,使得代码更加模块化、可维护性更高,并且提高了代码的重用性和灵活性。

Java 中动态代理主要有两种实现方式

  1. 基于接口的动态代理(JDK 动态代理): 基于 Java 标准库中的 java.lang.reflect.Proxy 类和 InvocationHandler 接口。该机制要求目标对象必须实现至少一个接口。

  2. 基于类的动态代理(CGLIB 动态代理): 使用第三方库 CGLIB(Code Generation Library)实现,不要求目标对象实现接口。

JDK 动态代理

JDK 动态代理通过 Proxy.newProxyInstance 方法创建代理对象。代理对象实现了指定的接口,并将方法的调用委托给实现了 InvocationHandler 接口的代理处理器。

以下是一个简单的 JDK 动态代理的示例:

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

// 接口
interface MyInterface {
    void doSomething();
}

// 实现类
class MyInterfaceImpl implements MyInterface {
    @Override
    public void doSomething() {
        System.out.println("Real object is doing something.");
    }
}

// InvocationHandler 实现
class MyInvocationHandler implements InvocationHandler {
    private Object target;

    public MyInvocationHandler(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 DynamicProxyExample {
    public static void main(String[] args) {
        // 创建真实对象
        MyInterface realObject = new MyInterfaceImpl();

        // 创建代理处理器
        MyInvocationHandler handler = new MyInvocationHandler(realObject);

        // 创建代理对象
        MyInterface proxyObject = (MyInterface) Proxy.newProxyInstance(
                DynamicProxyExample.class.getClassLoader(),
                new Class[]{MyInterface.class},
                handler
        );

        // 通过代理对象调用方法
        proxyObject.doSomething();
    }
}

CGLIB 动态代理

CGLIB 动态代理通过创建目标对象的子类来实现代理。CGLIB 动态代理不要求目标对象实现接口,但不能代理 final 类。

以下是一个简单的 CGLIB 动态代理的示例:

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

// 目标类
class MyTargetClass {
    public void doSomething() {
        System.out.println("Real object is doing something.");
    }
}

// MethodInterceptor 实现
class MyMethodInterceptor 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) {
        // 创建目标对象
        MyTargetClass targetObject = new MyTargetClass();

        // 创建代理拦截器
        MyMethodInterceptor methodInterceptor = new MyMethodInterceptor();

        // 创建 Enhancer 对象,设置父类和拦截器
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(MyTargetClass.class);
        enhancer.setCallback(methodInterceptor);

        // 创建代理对象
        MyTargetClass proxyObject = (MyTargetClass) enhancer.create();

        // 通过代理对象调用方法
        proxyObject.doSomething();
    }
}

无论是 JDK 动态代理还是 CGLIB 动态代理,都是在运行时生成代理类,并动态地处理被代理方法的调用。选择使用哪一种取决于具体的需求和场景,例如是否需要代理非接口类型的类,是否要求被代理的类实现接口等。

转载自:https://juejin.cn/post/7336140397709197322
评论
请登录