第十一讲-AOP之动态代理增强JDK
第十一讲-AOP之动态代理增强JDK
JDK实现动态代理需要三个参数:
- ClassLoader loader, 类加载器,JDK代理是在运行期间生成代理类的字节码,因此需要类加载器在运行时被加载
- Class<?>[] interfaces, 需要实现的接口
- InvocationHandler h 实现接口中的方法,规定方法中的行为,代理类将来被执行时就会调用该方法
使用JDK实现的动态代理简单示例:
package com.cherry.a12;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkProxyDemo {
interface Foo{
void foo();
}
static class Target implements Foo{
@Override
public void foo() {
System.out.println("target foo");
}
}
// 使用JDK代理实现功能增强 --> 只能针对接口代理
// cglib代理既可以针对接口代理,也可以针对非接口代理
public static void main(String[] args) {
// 创建一个代理对象
/**
* ClassLoader loader, 类加载器
* Class<?>[] interfaces, 需要实现的接口
* InvocationHandler h 实现接口中的方法,规定方法中的行为,代理类将来被执行时就会调用该方法
*/
// 获取类加载器
ClassLoader loader = JdkProxyDemo.class.getClassLoader();
Foo proxy = (Foo) Proxy.newProxyInstance(loader, new Class[]{Foo.class}, new InvocationHandler() {
@Override // 代理对象自己 正在执行的方法 方法中的参数
public Object invoke(Object p, Method method, Object[] args) throws Throwable {
System.out.println("before...");
return null;
}
});
// 调用代理对象中的方法(也就是实现的父接口中的方法)
proxy.foo();
}
}
这里代理对象为什么能够转为接口类型呢?因此JDK中Proxy.newInstance()方法是创建出来一个代理对象,而这个代理对象又实现了父接口,因此可以转为父接口类型。此外,JDK的代理实现就是对接口实现代理,生成的代理对象能够转为接口对象也是一个合理的解释。
运行结果如下:
before...
我们发现InvocationHandler中的方法被执行了,但是此时只能算是一个增强的功能(前置增强的功能),
但是待增强的目标类的原始逻辑却没有被增强,换句话说,就是该接口的实现类中的实现方法却没有被增强,所以呢,我们也要执行目标类中重写接口中的方法:
首先定义一个目标对象,并在并在InvocationHandler中使用反射调用该方法,并获取结果以及返回结果:
// 准备一个目标对象
Target target = new Target();
// ...... 省略中间代码 ......
Object result = method.invoke(target, args);
return result;
运行测试如下:
before...
target foo
我们发现此时不仅对目标方法实现了功能的增强,也执行了目标类中重写了父类的目标方法!当然我们也可以实现后置增强,完整的代码如下:
package com.cherry.a12;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkProxyDemo {
interface Foo{
void foo();
}
static class Target implements Foo{
@Override
public void foo() {
System.out.println("target foo");
}
}
// 使用JDK代理实现功能增强 --> 只能针对接口代理
// cglib代理既可以针对接口代理,也可以针对非接口代理
public static void main(String[] args) {
// 准备一个目标对象
Target target = new Target();
// 创建一个代理对象
/**
* ClassLoader loader, 类加载器
* Class<?>[] interfaces, 需要实现的接口
* InvocationHandler h 实现接口中的方法,规定方法中的行为,代理类将来被执行时就会调用该方法
*/
// 获取类加载器
ClassLoader loader = JdkProxyDemo.class.getClassLoader();
Foo proxy = (Foo) Proxy.newProxyInstance(loader, new Class[]{Foo.class} , new InvocationHandler() {
@Override // 代理对象自己 正在执行的方法 方法中的参数
public Object invoke(Object p, Method method, Object[] args) throws Throwable {
System.out.println("before...");
Object result = method.invoke(target, args);
System.out.println("after...");
return result;
}
});
// 调用代理对象中的方法(也就是实现的父接口中的方法)
proxy.foo();
}
}
运行结果如下:
before...
target foo
after...
JDK实现的代理的特点如下:
- JDK的动态代理和我们的目标类(接口实现类)是兄弟关系,因为它们都实现了共同的父接口!
- 既然它们两个都是兄弟关系,它们两者之间可以相互转换吗?不能的!比如猫和狗都实现了‘动物’这个接口,两者之间是不能进行转换的,但是两者都能转为‘动物(父接口)’
- 目标类可以使用final关键字修饰吗?可以的,目标类和代理类是兄弟关系,所有不存在继承关系,换句话说,目标类和代理类也仅仅是兄弟关系,并没有其它关系!
关于目标类和代理类仅仅是兄弟关系的演示(目标类使用final关键字修饰):
package com.cherry.a12;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkProxyDemo {
interface Foo{
void foo();
}
static final class Target implements Foo{
@Override
public void foo() {
System.out.println("target foo");
}
}
// 使用JDK代理实现功能增强 --> 只能针对接口代理
// cglib代理既可以针对接口代理,也可以针对非接口代理
public static void main(String[] args) {
// 准备一个目标对象
Target target = new Target();
// 创建一个代理对象
/**
* ClassLoader loader, 类加载器
* Class<?>[] interfaces, 需要实现的接口
* InvocationHandler h 实现接口中的方法,规定方法中的行为,代理类将来被执行时就会调用该方法
*/
// 获取类加载器
ClassLoader loader = JdkProxyDemo.class.getClassLoader();
Foo proxy = (Foo) Proxy.newProxyInstance(loader, new Class[]{Foo.class}, new InvocationHandler() {
@Override // 代理对象自己 正在执行的方法 方法中的参数
public Object invoke(Object p, Method method, Object[] args) throws Throwable {
System.out.println("before...");
Object result = method.invoke(target, args);
System.out.println("after...");
return result;
}
});
// 调用代理对象中的方法(也就是实现的父接口中的方法)
proxy.foo();
}
}
运行如下:
before...
target foo
after...
分类:
Spring 高级49讲
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构