JDK动态代理

JDK 动态代理(Java Dynamic Proxy)是 Java 标准库提供的一种代理模式实现,主要用于在运行时动态地创建接口的代理实例。动态代理可以用于拦截和修改方法调用,常用于实现 AOP(面向切面编程)、中间件、测试框架等。

JDK 动态代理主要涉及两个核心类:

  1. java.lang.reflect.Proxy:这是一个用于创建代理实例的工具类。它提供了一个 newProxyInstance() 静态方法,用于创建一个给定接口的代理实例。

  2. java.lang.reflect.InvocationHandler:这是一个接口,其实现类需要提供 invoke() 方法。当代理实例上的方法被调用时,invoke() 方法会被执行。在 invoke() 方法中,可以自定义如何处理方法调用,通过反射来调用目标对象的实际方法,以及在方法调用前后添加其他操作(如日志、安全检查等)。

举个简单的例子:


//定义一个接口
public interface MyInterface {

    String sayHi(String para);
}

//实现接口
public class MyClass implements MyInterface{

    @Override
    public String sayHi(String para) {
        System.out.println("Hi,"+para);
        return "OK";
    }
}
//实现handler的代码增强逻辑
public class MyClassProxy implements InvocationHandler {

    private Object target;
    private void before(){
        System.out.println("Before invoke method...");
    }

    private void after(){
        System.out.println("After invoke method...");
    }

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(proxy instanceof MyInterface){
            System.out.println(proxy.getClass() + "is instance of MyInterface");
        }
        before();
        Object result = method.invoke(target,args);
        after();
        return result;
    }
}

//测试类
public class JDKProxyTest {

    public static void main(String[] args) {
        MyClass target = new MyClass();
        InvocationHandler handler = new MyClassProxy(target);
        MyInterface proxy = (MyInterface) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),handler);
        proxy.sayHi("Java");
    }
}

输出:

class com.sun.proxy.$Proxy0is instance of MyInterface
Before invoke method...
Hi,Java
After invoke method...

 几个问题:

1.为什么代理类和目标类使用相同的类加载器?

在 JDK 动态代理中,代理类和目标类使用相同的类加载器有以下原因:

  1. 兼容性:确保代理类和目标类在相同的类路径下被加载,可以避免类加载器之间的不兼容性问题。类加载器之间可能存在不同的类路径和委托模式,如果代理类和目标类使用不同的类加载器,可能导致代理类无法找到目标类或目标类的依赖。

  2. 类型安全:当代理类和目标类使用相同的类加载器时,可以确保它们实现的接口是相同的。这意味着代理类可以安全地替换目标类,而不会引发类型转换异常(如 ClassCastException)。这对于在运行时修改或拦截方法调用至关重要。

  3. 简化实现:通过重用目标类的类加载器和接口列表,可以简化 JDK 动态代理的实现。例如,Proxy.newProxyInstance() 方法可以直接从目标类的类加载器和接口列表中获取必要的信息,无需手动创建或修改这些参数。这使得动态代理更加灵活和易于使用。

2.InvocationHandler的invoke方法的Object proxy参数的含义

InvocationHandler 接口的 invoke() 方法有三个参数:Object proxyMethod methodObject[] argsObject proxy 参数的含义如下:

这是代理实例本身。在 invoke() 方法中,你可以通过这个参数引用调用方法的代理对象。然而,在大多数情况下,你不需要使用此参数。实际上,在 invoke() 方法内部调用 proxy 上的方法会导致递归调用 invoke() 方法,从而引发栈溢出错误。通常,在 invoke() 方法中,你应该调用目标对象(而非代理对象)上的方法,并在方法调用前后添加其他操作(如日志、安全检查等)。从上文的代码输出来看,proxy就是代理类本身。

3.为什么参数要传Object proxy?

InvocationHandlerinvoke() 方法中,传递 Object proxy 参数的目的主要是为了提供更多的上下文信息和灵活性。虽然在大多数情况下,你可能不需要使用这个参数,但在某些特定场景下,它可能会派上用场。以下是一些使用 Object proxy 参数的可能场景:

  1. 代理对象的引用:有时候,你可能需要在 invoke() 方法中获取对代理对象的引用。例如,你可能需要将代理对象传递给其他方法或组件,以便在后续操作中继续使用代理而不是目标对象。

  2. 多个代理对象:如果 InvocationHandler 实例需要处理多个代理对象,你可以使用 Object proxy 参数来区分它们。这样,你可以在 invoke() 方法中根据不同的代理对象执行不同的操作。

  3. 自定义行为:在某些情况下,你可能需要根据代理对象的特性或状态来自定义 invoke() 方法的行为。例如,你可以在代理对象上添加自定义注解或元数据,并在 invoke() 方法中检查这些信息以决定如何处理方法调用。

然而,正如先前提到的,你应该避免在 invoke() 方法中直接调用 proxy 上的方法,因为这会导致无限递归和栈溢出错误。在大多数情况下,你应该在 invoke() 方法中调用目标对象上的方法,而不是代理对象上的方法。

总之,虽然 Object proxy 参数在许多场景下可能用不到,但它的存在仍然为 InvocationHandler 提供了更多的上下文信息和灵活性。在实际使用中,你可以根据具体需求和场景决定是否使用 Object proxy 参数。

 

你可能感兴趣的:(java,开发语言,代理模式)