Java中的动态代理机制是一种实现AOP(面向切面编程)技术的重要手段,它可以在不修改源代码的情况下进行增强操作。在Java中,有两种动态代理方式:一种是JDK动态代理,另一种是CGLIB动态代理。
Java基于接口的动态代理是一种在运行时动态生成代理类的技术。它是通过反射机制在运行时生成代理对象,对被代理对象的方法进行拦截处理,实现增强功能,而不需要像静态代理一样手动编写代理类。
Java基于接口的动态代理需要实现两个接口:InvocationHandler和Proxy。InvocationHandler接口中定义了一个invoke方法,该方法接收一个代理对象、被代理对象的方法和参数,并返回代理对象对被代理对象的方法进行增强后的返回值。Proxy类中则提供了一个静态方法newProxyInstance,用于创建代理对象。
使用Java基于接口的动态代理,需要按照以下步骤:
Java基于接口的动态代理可以实现一些横切关注点,比如记录方法调用时长、打印日志、权限校验等,使得我们的代码更加灵活和易于维护。
Java CGLIB 动态代理是一种在运行时生成代理对象的技术,它可以在不修改原始类型(类)的基础上,为该类型创建一个代理子类,该代理子类可以拦截原始类中的方法调用。相较于Java JDK动态代理,它可以代理没有实现接口的类。
CGLIB 是 Code Generation Library(代码生成库)的简称,是一个强大的高性能的代码生成库,CGLIB 可以在运行期间扩展 Java 类和实现 Java 接口。它提供了很多实用的功能,例如方法拦截、字段拦截、方法调用前后拦截等等。
下面以一个计算器为例,通过动态代理拦截计算器方法的执行,在方法执行前后打印日志。
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;
}
}
在Java中,动态代理是使用Proxy类实现的。这个类提供了一个静态方法newProxyInstance(),用于创建动态代理对象。该方法需要传入三个参数:
通过调用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
CGLIB(Code Generation Library)是针对JDK的动态代理机制的一个扩展,它通过生成字节码文件并加载到JVM中,实现对目标对象的动态代理。相对于JDK动态代理,CGLIB能够对类进行代理而不仅仅是接口,因此被广泛应用于框架实现。
下面详细介绍CGLIB动态代理的使用过程:
<dependency>
<groupId>cglibgroupId>
<artifactId>cglibartifactId>
<version>3.3.0version>
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