1 动态代理

Java中的动态代理机制是一种实现AOP(面向切面编程)技术的重要手段,它可以在不修改源代码的情况下进行增强操作。在Java中,有两种动态代理方式:一种是JDK动态代理,另一种是CGLIB动态代理。

1.1 jdk动态代理(基于接口的动态代理)

Java基于接口的动态代理是一种在运行时动态生成代理类的技术。它是通过反射机制在运行时生成代理对象,对被代理对象的方法进行拦截处理,实现增强功能,而不需要像静态代理一样手动编写代理类。

Java基于接口的动态代理需要实现两个接口:InvocationHandler和Proxy。InvocationHandler接口中定义了一个invoke方法,该方法接收一个代理对象、被代理对象的方法和参数,并返回代理对象对被代理对象的方法进行增强后的返回值。Proxy类中则提供了一个静态方法newProxyInstance,用于创建代理对象。

使用Java基于接口的动态代理,需要按照以下步骤:

  1. 定义一个接口,该接口中定义需要被代理的方法。
  2. 实现InvocationHandler接口,重写invoke方法,对需要被代理的方法进行增强处理。
  3. 使用Proxy类的newProxyInstance方法,创建代理对象。

Java基于接口的动态代理可以实现一些横切关注点,比如记录方法调用时长、打印日志、权限校验等,使得我们的代码更加灵活和易于维护。

1.2 cglib动态代理(基于类的动态代理)

Java CGLIB 动态代理是一种在运行时生成代理对象的技术,它可以在不修改原始类型(类)的基础上,为该类型创建一个代理子类,该代理子类可以拦截原始类中的方法调用。相较于Java JDK动态代理,它可以代理没有实现接口的类。

CGLIB 是 Code Generation Library(代码生成库)的简称,是一个强大的高性能的代码生成库,CGLIB 可以在运行期间扩展 Java 类和实现 Java 接口。它提供了很多实用的功能,例如方法拦截、字段拦截、方法调用前后拦截等等。

2 代码实现

下面以一个计算器为例,通过动态代理拦截计算器方法的执行,在方法执行前后打印日志。

src\main\java\myproxy
├──cglib_proxy      
│  ├──CglibTest.java
│  └──MyLoggerInterceptor.java
├──jdk_proxy                  
│  ├──JDKProxyTest.java       
│  └──MyLoggerProxy.java      
├──Calculator.java            
└──CalculatorImpl.java
public interface Calculator {
    public int add(int a, int b);
    public int sub(int a, int b);
}

public class CalculatorImpl implements Calculator {
    @Override
    public int add(int a, int b) {
        System.out.println("执行方法 add");
        return a + b;
    }

    @Override
    public int sub(int a, int b) {
        System.out.println("执行方法 sub");
        return a - b;
    }
}

2.1 jdk动态代理

在Java中,动态代理是使用Proxy类实现的。这个类提供了一个静态方法newProxyInstance(),用于创建动态代理对象。该方法需要传入三个参数:

  1. 类加载器(ClassLoader):用于加载代理类的类加载器。
  2. 代理接口数组(Class[]):需要代理的接口列表。
  3. 调用处理器(InvocationHandler):对方法的调用进行处理的回调对象。

通过调用newProxyInstance()方法,可以创建一个代理对象。当代理对象调用其实现的接口方法时,方法调用将被重定向到InvocationHandler对象的invoke()方法。在invoke()方法中,可以对调用方法进行额外的处理,例如记录日志或验证权限。

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


/**
 * 实现在方法执行前后添加日志功能
 */
public class MyLoggerProxy {
    private Object target;//被代理的目标对象

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

    public Object getProxy() {
        /*
        ClassLoader loader,
            被代理类的类加载器
        @NotNull Class<?>[] interfaces,
            被代理类实现的接口
        @NotNull reflect.InvocationHandler h
            代理的具体实现
         */
        Object proxyInstance = Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * 基于接口的动态代理:jdk动态代理
                     * @param proxy 代理对象
                     * @param method 被代理的方法
                     * @param args 被代理方法的参数列表
                     * @return 被代理方法的返回值
                     * @throws Throwable
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        beforeExecutingMethod(target.getClass().getSimpleName(), method.getName(), args);

                        Object ret = method.invoke(target, args);

                        //异常发生时的处理逻辑

                        afterExecutingMethod(target.getClass().getSimpleName(), method.getName(), ret);

                        return ret;
                    }
                });
        return proxyInstance;
    }

    public void beforeExecutingMethod(String clazzName, String methodName, Object[] args) {
        System.out.printf("执行方法 %s.%s 之前,方法参数为:%s\n", clazzName, methodName, Arrays.toString(args));
    }

    public void afterExecutingMethod(String clazzName, String methodName, Object object) {
        System.out.printf("执行方法 %s.%s 之后,返回值为:%s\n", clazzName, methodName, object.toString());
    }
}

public class JDKProxyTest {
    public static void main(String[] args) {
        MyLoggerProxy myLoggerProxy = new MyLoggerProxy(new CalculatorImpl());
        Calculator cal = (Calculator) myLoggerProxy.getProxy();

        cal.add(1, 2);
        System.out.println();
        cal.sub(4, 5);
    }
}

执行方法 CalculatorImpl.add 之前,方法参数为:[1, 2]
执行方法 add
执行方法 CalculatorImpl.add 之后,返回值为:3

执行方法 CalculatorImpl.sub 之前,方法参数为:[4, 5]
执行方法 sub
执行方法 CalculatorImpl.sub 之后,返回值为:-1

进程已结束,退出代码0

2.2 cglib动态代理

CGLIB(Code Generation Library)是针对JDK的动态代理机制的一个扩展,它通过生成字节码文件并加载到JVM中,实现对目标对象的动态代理。相对于JDK动态代理,CGLIB能够对类进行代理而不仅仅是接口,因此被广泛应用于框架实现。

下面详细介绍CGLIB动态代理的使用过程:

  1. 定义一个类作为目标类,并且定义需要被代理的方法;
  2. 在代理类中通过CGLIB的Enhancer类创建一个代理类对象;
  3. 给代理类对象设置回调函数,即MethodInterceptor接口的实现类,该回调函数负责拦截所有对目标类方法的调用;
  4. 使用代理类对象调用目标类方法即可。
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 实现在方法执行前后添加日志功能
 */
public class MyLoggerInterceptor implements MethodInterceptor {
    /**
     * 基于子类的动态代理:cglib动态代理
     *
     * @param o           要增强的对象
     * @param method      被拦截的方法
     * @param objects     被拦截方法的参数列表
     * @param methodProxy 对方法的代理,methodProxy.invokeSuper方法表示对被代理对象方法的调用
     * @return 被拦截方法的返回值
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        beforeExecutingMethod(o.getClass().getSimpleName(), method.getName(), objects);

        // 注意这里是调用invokeSuper而不是invoke,否则死循环;
        // methodProxy.invokeSuper执行的是原始类的方法;
        // method.invoke执行的是子类的方法;
        Object ret = methodProxy.invokeSuper(o, objects);

        //异常发生时的处理逻辑

        afterExecutingMethod(o.getClass().getSimpleName(), method.getName(), ret);

        return ret;
    }

    public void beforeExecutingMethod(String clazzName, String methodName, Object[] objects) {
        System.out.printf("执行方法 %s.%s 之前,方法参数为:%s\n", clazzName, methodName, Arrays.toString(objects));
    }

    public void afterExecutingMethod(String clazzName, String methodName, Object object) {
        System.out.printf("执行方法 %s.%s 之后,返回值为:%s\n", clazzName, methodName, object.toString());
    }
}

import net.sf.cglib.proxy.Enhancer;

public class CglibTest {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        //设置目标类的字节码文件
        enhancer.setSuperclass(CalculatorImpl.class);
        //设置回调函数
        enhancer.setCallback(new MyLoggerInterceptor());
        //创建代理类
        CalculatorImpl calculatorImpl = (CalculatorImpl) enhancer.create();

        //调用代理类的业务方法
        calculatorImpl.add(1, 2);
        System.out.println();
        calculatorImpl.sub(4, 5);
    }
}

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by net.sf.cglib.core.ReflectUtils$1 (file:/D:/java/maven-repo/cglib/cglib/3.3.0/cglib-3.3.0.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)
WARNING: Please consider reporting this to the maintainers of net.sf.cglib.core.ReflectUtils$1
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
执行方法 CalculatorImpl$$EnhancerByCGLIB$$ead943e.add 之前,方法参数为:[1, 2]
执行方法 add
执行方法 CalculatorImpl$$EnhancerByCGLIB$$ead943e.add 之后,返回值为:3

执行方法 CalculatorImpl$$EnhancerByCGLIB$$ead943e.sub 之前,方法参数为:[4, 5]
执行方法 sub
执行方法 CalculatorImpl$$EnhancerByCGLIB$$ead943e.sub 之后,返回值为:-1

进程已结束,退出代码0

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐