前言
最近在写项目的时候,又写到很久没写的 AOP 切面实现一个需求,又想到上次同学面试的时候被问到了 Spring AOP 的实现原理是什么,以前就知道是用了代理模式,但是也没有进行过多的去研究,刚好碰到了也就研究一下代理模式。
什么是代理模式
代理模式就是通过一个代理对象来间接访问目标对象,这样可以在不改变目标对象的情况下,为它添加一些额外的功能或行为。简单来说,代理就是“替身”,它在幕后帮目标对象做一些额外的事。要扩展功能,就不需要更改源目标的代码了,只需要在代理类增加就可以了。
代理模式的分类
- 静态代理:代理类在编译时就已经确定,通常需要手动创建代理类。
- 动态代理:代理类在运行时动态生成,不需要提前创建,代理对象的创建由代理框架(如 JDK 动态代理或 CGLIB)控制。
特点 | 静态代理 | 动态代理 |
---|---|---|
代理类创建时机 | 代理类在编译时已经确定,需要手动创建代理类 | 代理类在运行时动态生成,不需要提前创建 |
实现方式 | 手动编写代理类,代理类与目标类关系固定 | 使用代理框架(如 JDK 动态代理、CGLIB)在运行时创建代理类 |
目标类要求 | 目标类必须实现接口(或继承某个类) | JDK 动态代理:目标类必须实现接口;CGLIB:不需要接口 |
静态代理的实现
静态代理就是在程序编译时就创建好代理类,并让代理类和目标类实现相同的接口。代理类通过调用目标类的方法来完成任务,同时可以在调用前后添加一些额外的操作。
示例:静态代理模式
接口定义:
public interface UserService {
void save(String username);
void update(Long id, String username);
void delete(Long id);
}
目标类实现:
public class UserServiceImpl implements UserService {
@Override
public void save(String username) {
System.out.println("增加用户: " + username);
}
@Override
public void update(Long id, String username) {
System.out.println("编辑用户: " + username);
}
@Override
public void delete(Long id) {
System.out.println("删除用户: " + id);
}
}
静态代理类:
public class UserServiceProxy implements UserService {
private UserServiceImpl userService;
public UserServiceProxy(UserServiceImpl userService) {
this.userService = userService;
}
@Override
public void save(String userName) {
// 在调用实际方法之前,做一些操作(如记录日志)
System.out.println("日志:在添加用户之前");
userService.save(userName); // 调用真实对象的方法
System.out.println("日志:在添加用户之后");
}
@Override
public void update(Long id, String userName) {
System.out.println("日志:在更新用户之前");
userService.update(id, userName);
System.out.println("日志:在更新用户之后");
}
@Override
public void delete(Long id) {
System.out.println("日志:在删除用户之前");
userService.delete(id); // 调用真实对象的方法
System.out.println("日志:在删除用户之后");
}
}
使用代理
public class Main {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
UserService userServiceProxy = new UserServiceProxy(userService);
userServiceProxy.save("yunzhi");
userServiceProxy.update(1L, "yunzhi");
userServiceProxy.delete(1L);
}
}
打印结果:
日志:在添加用户之前
增加用户: yunzhi
日志:在添加用户之后
日志:在更新用户之前
编辑用户: yunzhi
日志:在更新用户之后
日志:在删除用户之前
删除用户: 1
日志:在删除用户之后
结论:
静态代理的实现方式相对直观,代码也较为简洁易懂。然而,这种模式的缺点也非常明显:当需要代理的接口数量增多时,每增加一个接口就必须创建一个相应的代理类,这样会导致大量的代理类代码,造成系统的臃肿和维护上的困难。
动态代理的实现
与静态代理不同,动态代理是在运行时通过代理框架(如 JDK 动态代理或 CGLIB)来生成代理对象,不需要提前编写代理类。
JDK动态代理
JDK 动态代理,当目标类实现了接口时,并将接口的方法委托给目标类的实现。
JDK动态代理的实现步骤:
1.定义接口和实现类。
2.创建InvocationHandler实现,负责目标方法的拦截和增强
3.使用 Proxy 类创建代理对象。
创建一个 JdkLoggingInvocationHandler 类,统一实现日志处理代理
public class JdkLoggingInvocationHandler implements InvocationHandler {
private Object target;
public JdkLoggingInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("日志:调用 " + method.getName() + " 方法");
// 调用目标对象的方法
Object result = method.invoke(target, args);
// 在方法调用后添加日志
System.out.println("日志:执行 " + method.getName() + " 方法完成");
return result;
}
}
使用代理
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
JdkLoggingInvocationHandler handler = new JdkLoggingInvocationHandler(userService);
UserService proxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class<?>[]{UserService.class},
handler);
proxy.save("yunzhi");
proxy.update(1L, "yunzhi");
proxy.delete(1L);
}
打印结果:
日志:调用 save 方法
增加用户: yunzhi
日志:执行 save 方法完成
日志:调用 update 方法
编辑用户: yunzhi
日志:执行 update 方法完成
日志:调用 delete 方法
删除用户: 1
日志:执行 delete 方法完成
注意点
这里我们要注意当前的代理对象不是原始对象了,通过 Proxy.newProxyInstance 创建的代理对象,实际上并不是 UserServiceImpl 或其他实现类的实例,它是通过 Proxy 类在运行时生成的,实现了目标接口,并通过 InvocationHandler 实现了方法的增强逻辑。
目标类没有接口就不能使用JDK代理
这里我们把 UserService 移除掉,不是实现接口的形式
public class UserServiceImpl {
}
使用代理
UserServiceImpl userService = new UserServiceImpl();
JdkLoggingInvocationHandler handler = new JdkLoggingInvocationHandler(userService);
UserServiceImpl proxy = (UserServiceImpl) Proxy.newProxyInstance(
UserServiceImpl.class.getClassLoader(),
new Class<?>[]{UserServiceImpl.class},
handler);
这里我们可以发现,如果目标类不是接口的话就没办法进行代理增强。
结论
在静态代理中,每个目标类都需要一个单独的代理类,代理类的代码重复且难以维护。
在JDK动态代理中,你只需要编写一个 InvocationHandler 来统一处理所有目标类的代理方法,增强逻辑可以复用,但是必须要求目标类是有接口实现。
CGLIB 动态代理
CGLIB 代理可以对任何没有实现接口的类进行代理,因为它是通过继承目标类并重写方法来实现的,在实际的场景中,有一些业务不总是实现接口,为了增强这些没有接口的类,所以 Spring 使用 CGLIB 的方式实现动态代理。
截取 Spring 项目中使用 AOP 的对象
CGLIB 动态代理的实现步骤:
1.定义目标类
CGLIB 代理是基于类的,而不是接口的,所以目标类不需要实现接口。
2.创建 MethodInterceptor 实现
MethodInterceptor 用于拦截目标方法,并进行增强逻辑。这个类相当于 JDK 动态代理中的 InvocationHandler。
3.使用 Enhancer 创建代理对象
Enhancer 是 CGLIB 的核心类,用于创建动态代理对象。它通过继承目标类并重写目标方法来实现代理。
创建一个 CglibLoggingMethodInterceptor 类,统一实现日志处理代理
public class CglibLoggingMethodInterceptor implements MethodInterceptor {
private Object target;
public CglibLoggingMethodInterceptor(Object target) {
this.target = target;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("日志:调用 " + method.getName() + " 方法");
Object result = proxy.invokeSuper(obj, args); // 调用父类的方法
System.out.println("日志:执行 " + method.getName() + " 方法完成");
return result;
}
}
使用代理
public static void main(String[] args) {
UserServiceImpl userServiceImpl = new UserServiceImpl();
// 创建 CGLIB 代理的 Enhancer 对象
Enhancer enhancer = new Enhancer();
// 设置代理的目标类(即 UserServiceImpl 类)
// CGLIB 通过继承这个目标类来生成代理类
enhancer.setSuperclass(UserServiceImpl.class);
// 设置方法拦截器,代理会调用这个拦截器
// 这里使用了一个自定义的 CglibLoggingMethodInterceptor 来拦截方法并添加日志增强
enhancer.setCallback(new CglibLoggingMethodInterceptor(userServiceImpl));
// 创建代理对象
// 通过 Enhancer.create() 创建代理对象,此对象会继承 UserServiceImpl 类并重写其方法
UserServiceImpl proxy = (UserServiceImpl) enhancer.create();
proxy.save("yunzhi");
proxy.update(1L, "yunzhi");
proxy.delete(1L);
}
打印结果:
日志:调用 save 方法
增加用户: yunzhi
日志:执行 save 方法完成
日志:调用 update 方法
编辑用户: yunzhi
日志:执行 update 方法完成
日志:调用 delete 方法
删除用户: 1
日志:执行 delete 方法完成
注意点
这里我们要注意当前的代理对象不是原始对象了,通过 Enhancer 类来创建代理对象。这个代理对象是通过继承目标类 UserServiceImpl 来创建的,代理对象并不是原始的 UserServiceImpl 对象,而是通过继承生成的新类。
总结:
静态代理:
- 适用于简单场景,代码重复且不灵活。
JDK 动态代理:
- 代理对象实现了目标接口,实际的目标对象没有改变。
- 代理对象通过
Proxy
类动态生成,并通过InvocationHandler
处理方法增强。
CGLIB 代理:
- 代理对象继承自目标类并重写目标方法,实际的目标类没有改变。
- 代理对象通过
Enhancer
动态生成,并通过MethodInterceptor
处理方法增强。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。