返回

动态代理与JDK的那些事儿

后端

前言

在Java开发中,动态代理是一种非常重要的技术,它允许我们动态地创建代理对象,并赋予其与原对象相同的接口和行为。动态代理的应用场景非常广泛,比如:

  • 日志记录: 可以使用动态代理来记录对象方法的调用。
  • 性能监控: 可以使用动态代理来监控对象方法的执行时间。
  • 安全: 可以使用动态代理来对对象方法进行访问控制。
  • 事务: 可以使用动态代理来对对象方法进行事务管理。

基于JDK的动态代理

Java中基于JDK的动态代理,主要利用java.lang.reflect包下的Proxy类。Proxy类的newProxyInstance方法可以创建一个新的代理对象,该代理对象继承了java.lang.reflect.InvocationHandler接口。InvocationHandler接口有一个invoke方法,该方法会在代理对象的方法被调用时触发。

以下是一个基于JDK的动态代理的例子:

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

public class DynamicProxyExample {

    public static void main(String[] args) {
        // 创建一个目标对象
        Foo target = new Foo();

        // 创建一个InvocationHandler对象
        InvocationHandler handler = new MyInvocationHandler(target);

        // 创建一个代理对象
        Foo proxy = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                new Class[]{Foo.class}, handler);

        // 调用代理对象的方法
        proxy.foo();
    }

    // 定义一个接口
    interface Foo {
        void foo();
    }

    // 定义一个目标对象
    static class FooImpl implements Foo {
        @Override
        public void foo() {
            System.out.println("FooImpl.foo()");
        }
    }

    // 定义一个InvocationHandler对象
    static class MyInvocationHandler implements InvocationHandler {

        private final Object target;

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

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

基于CGLIB的动态代理

Java中基于CGLIB的动态代理,主要利用CGLIB库。CGLIB是一个开源的Java库,它提供了动态创建类的功能。使用CGLIB可以动态地创建代理类,该代理类继承了目标类的所有方法。

以下是一个基于CGLIB的动态代理的例子:

import net.sf.cglib.proxy.Enhancer;

public class CglibProxyExample {

    public static void main(String[] args) {
        // 创建一个目标对象
        Foo target = new Foo();

        // 创建一个代理对象
        Foo proxy = (Foo) Enhancer.create(Foo.class, new MyMethodInterceptor());

        // 调用代理对象的方法
        proxy.foo();
    }

    // 定义一个接口
    interface Foo {
        void foo();
    }

    // 定义一个目标对象
    static class FooImpl implements Foo {
        @Override
        public void foo() {
            System.out.println("FooImpl.foo()");
        }
    }

    // 定义一个MethodInterceptor对象
    static class MyMethodInterceptor implements MethodInterceptor {

        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("MyMethodInterceptor.intercept()");
            return method.invoke(obj, args);
        }
    }
}

基于JDK的动态代理与基于CGLIB的动态代理的对比

基于JDK的动态代理与基于CGLIB的动态代理各有优缺点。

基于JDK的动态代理的优点:

  • 简单易用。
  • 不需要修改目标类的代码。

基于JDK的动态代理的缺点:

  • 只能代理实现了接口的类。
  • 性能开销较大。

基于CGLIB的动态代理的优点:

  • 可以代理任何类,包括没有实现接口的类。
  • 性能开销较小。

基于CGLIB的动态代理的缺点:

  • 需要修改目标类的字节码。
  • 可能导致目标类的安全性降低。

结论

动态代理是一种非常重要的技术,它可以帮助我们动态地创建代理对象,并赋予其与原对象相同的接口和行为。Java中基于JDK的动态代理和基于CGLIB的动态代理都是非常常用的动态代理实现方式,它们各有优缺点。在实际开发中,我们可以根据具体情况选择合适的动态代理实现方式。