关于动态代理的个人记录
静态代理
代理类持有一个目标对象的引用,所以本质上是代理对象将调用转发给目标对象,调用真实对象的方法
public interface UserService {void addUser(String name);
}public class UserServiceImpl implements UserService {@Overridepublic void addUser(String name) {System.out.println("执行 addUser: " + name);}
}public class UserServiceProxy implements UserService {private UserService target;public UserServiceProxy(UserService target) {this.target = target;}@Overridepublic void addUser(String name) {System.out.println("代理:前置操作");target.addUser(name); // 调用真实对象的方法System.out.println("代理:后置操作");}
}
实际执行的还是 UserServiceImpl 的 addUser 方法。
动态代理
jdk动态代理会生成一个代理类方法,这个代理类对象的每次方法调用都会转到InvocationHandler.invoke()
方法里
invoke() 里可以决定:
【1】要不要调用真实对象的方法(有目标对象的情况)
【2】或者自己“模拟实现”(没有目标对象的情况,比如拼 SQL、发 HTTP 请求等)
模拟处理举例:
UserService userService = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(),new Class[]{UserService.class},(proxy, method, args) -> {System.out.println("代理执行方法: " + method.getName());if ("addUser".equals(method.getName())) {System.out.println("模拟插入数据库: " + args[0]);}return null;}
);
userService.addUser("Tom");
这里 addUser 没有真实实现类,但代理在 invoke 里写了逻辑,所以看起来就像方法执行了。
示例
public interface MathCalculator {int div(int i, int j);int mul(int i, int j);int sub(int i, int j);int add(int i, int j);}
@Component
public class MathCalculatorImpl implements MathCalculator {@Overridepublic int div(int i, int j) {return 0;}@Overridepublic int mul(int i, int j) {return 0;}@Overridepublic int sub(int i, int j) {return 0;}@Overridepublic int add(int i, int j) {return i+j;}
}
@Testvoid test01() {// JDK 动态代理 (JDK Dynamic Proxy)MathCalculatorImpl target = new MathCalculatorImpl();InvocationHandler h= new InvocationHandler() {/** proxy:代理对象* method:准备调用的目标对象的这个方法* args:参数*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//目标方法执行Object result = method.invoke(target, args);return result;}};/** ClassLoader loader:类加载器(目标对象)* Class<?>[] interfaces:目标对象实现的接口类型* InvocationHandler h:执行Handler*/MathCalculator proxyInstance = (MathCalculator)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), h);int add=proxyInstance.add(1, 2);System.out.println(add);}
解释:
对 proxyInstance.add 的调用会被代理对象拦截。这种拦截会触发提供的 InvocationHandler 的 invoke 方法
方法调用被拦截: 当在 proxyInstance 上调用 add(1, 2) 时,代理对象本身并没有 add 方法的实现。它会将这个调用导向 InvocationHandler 的 invoke 方法。
invoke 方法执行: invoke 方法随即被执行。传递给它的参数是:
proxy:代理对象本身。
method:一个 Method 对象,代表 add(int, int) 方法。
args:一个 Object 数组,包含参数 {1, 2}。
目标方法被调用: 在 invoke 方法内部,有一行代码 Object result = method.invoke(target, args);。这才是真正神奇的地方。正在显式地使用 method 对象(它代表 add 方法)和 target 对象(MathCalculatorImpl)来调用实际实现类中的 add 方法。
也就是说:这里虽然不是像静态代理那样直接调用,但是触发到InvocationHandler的invoke方法里面具体操作是使用反射来调用目标对象的add方法
简而言之,代理将调用委托给了 InvocationHandler 的 invoke 方法,而 InvocationHandler 的职责是决定如何处理这个调用,在代码中,它的决定就是将调用转发给真正的 target 对象。【这里也是区别于静态代理的,直接调用目标对象的方法是静态代理的典型操作】
总结
Java动态代理的经典实现,它的核心思想就是:
1.方法拦截:
当你调用 proxyInstance.add(1, 2) 时,这个调用并不会直接进入 MathCalculatorImpl 类【具体实现类】。Proxy 对象会拦截这个调用,并将其重定向到 InvocationHandler 的 invoke 方法。
2.反射调用
在 invoke 方法内部,我们可以只写一些逻辑,看起来像方法调用了,这里当然就没有进行反射操作了。
但是我们也可以像Object result = method.invoke(target, args);
这样,使用反射来调用目标对象的对应方法。
这里的 method 参数是一个 java.lang.reflect.Method 对象,它代表了你最初调用的 add 方法。method.invoke(target, args) 正是通过反射机制,在运行时动态地调用了 target 对象 (MathCalculatorImpl 的实例) 上的 add 方法,并将参数 args 传递进去。
所以,虽然 proxyInstance 本身没有直接实现 add 方法,但它通过 InvocationHandler 和反射,最终还是调用了 MathCalculatorImpl 【实现类】的 add 方法
这就是动态代理与静态代理最大的不同。静态代理是编译时就写好的,直接调用目标对象的方法;而动态代理则是在运行时生成代理对象,并通过反射来间接调用目标对象的方法。