AOP与代理模式
为什么将一个对象加入到IOC容器后,取出该对象后,发现它的方法多了一些功能?
原因是:如果该类被AOP切中,从IOC容器取出的对象,不是该类的对象,而是该类代理类的对象。
文章目录:
1.代理模式
2.静态代理实现
3.动态代理实现
AOP的实现原理
- 当调用容器的getBean方法后,Spring查找对象后会判断该对象的方法是否被某个切面切中
- 如果没有切中,就创建原有类的对象
- 如果实现过接口,则通过JDK动态代理生成代理类,并创建对象
- 如果没有实现过接口,则通过CGLib动态代理生成代理类,并创建对象
代理模式简介
代理模式为其他对象提供一种代理以控制对这个对象的访问。
在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式的作用
- 中介的作用,当调用者不能或不方便调用某个对象时,代理起到中介的作用,帮助调用者间接的调用对象。
代理模式分为两种
- 静态代理,在运行前,通过编写代码的方式生成代理类
- 动态代理,在运行后,通过反射机制生成代理类
静态代理实现步骤
- 代理者和被代理者都实现相同的接口
- 代理者包含被代理者的对象
- 创建代理对象时传入被代理对象
- 代理者执行方法时,会调用被代理者的方法,同时扩展新的功能
例子:
UserService接口
public interface UserService { void updateMoney(String name); }
UserService实现类
public class UserServiceImpl implements UserService { @Override public void updateMoney(String name) { System.out.println("账号向"+name+"转账"); } }
StaticProxy类
//代理类与客户类实现同一个接口 public class StaticProxy implements UserService { private UserService userService=null; //2.在构造方法中将参数传入 public StaticProxy(UserService userService){ this.userService=userService; } @Override public void updateMoney(String name) { System.out.println("打印日志"); userService.updateMoney(name); } }
测试:
public class testStaticProxy { public static void main(String[] args) { StaticProxy staticProxy = new StaticProxy(new UserServiceImpl()); staticProxy.updateMoney("李四"); } }
结果:
添加了打印日志的功能,但是一个代理类只能代理一种业务,如果有多种业务,就必须创建大量的代理类
动态代理的分类
- CGLib动态代理
JDK动态代理
JDK自带的,前提是:被代理类必须实现过接口。
- 实现InvocationHandler接口
- 实现invoke方法
- 通过Proxy.newProxyInstance方法返回代理对象
例子:
GameService接口:
public interface GameService { void shot(String name); }
GameService实现类:
public class GameServiceImpl implements GameService { @Override public void shot(String name) { System.out.println("我打了"+name); } }
JDKProxy.java
public class JDKProxy implements InvocationHandler{ //被代理对象 private Object object=null; //创建代理对象 public Object createProxy(Object object){ this.object=object; return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //扩展功能 System.out.println("打印打游戏时间!"); Object invoke = method.invoke(object, args); return invoke; } }
测试:
public class testJDKProxy { public static void main(String[] args) { JDKProxy().createProxy(new GameServiceImpl()); proxy.shot("张三"); } }
截图:
扩展打印打游戏时间功能
需要引入CGLib依赖,它的原理是:通过反射+继承机制动态生成被代理类的子类,所以被代理类不能是final的。
实现步骤
- 引入cglib
- 实现MethodInterceptor接口
- 实现intercept方法
- 通过Ehancer返回代理对象
例子:
Bank类
public class Bank { public void transferAccount(String formName, String toName, double money){ System.out.println(formName+"向"+toName+"转账"+money); } }
CGLBProxy类
public class CGLibProxy{ public static Object createProxy(Object obj){ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(obj.getClass()); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { //增强功能 System.out.println("日志"); return methodProxy.invokeSuper(o, objects); } }); return enhancer.create(); } }
测试:
public class testCGLBProxy { public static void main(String[] args) { Bank proxy = (Bank) CGLibProxy.createProxy(new Bank()); proxy.transferAccount("张三","李四",34); } }
扩展了增加日志功能
我的例子的码云地址:https://gitee.com/fan-gege/proxy.git
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)