spring中的多线程aop方法拦截
日常开发中,常用spring的aop机制来拦截方法,记点日志、执行结果、方法执行时间啥的,很是方便,比如下面这样:(以spring-boot项目为例)
一、先定义一个Aspect
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
|
二、定义一个service
1 2 3 4 5 6 7 8 9 10 |
|
三、跑一把
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
输出:
1 2 3 |
|
第2行即aop拦截后输出的内容。但有些时候,我们会使用多线程来调用服务,这时候aop还能不能拦到呢?
四、多线程
4.1 场景1:Runnable中传入了Spring上下文
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
把刚才的main方法,改成用线程池调用(即:多线程)
1 2 3 4 5 6 7 8 9 |
|
输出如下:
1 2 3 |
|
很明显,仍然正常拦截到了,而且从线程id上看,确实是一个新线程。
4.2 场景2:Runnable中没传入Spring上下文
1 2 3 4 5 6 7 8 9 10 11 |
|
与RunnableA的区别在于,完全与spring上下文没有任何关系,服务实例是手动new出来的。
修改main方法:
1 2 3 4 5 6 7 8 9 |
|
输出:
1 2 |
|
全都是手动new出来的对象,与spring没半毛钱关系,aop不起作用也符合预期。这种情况下该怎么破?
轮到CGLib出场了,其实spring的aop机制,跟它就有密切关系,大致原理:CGLib会从被代理的类,派生出一个子类,然后在子类中覆写所有非final的public方法,从而达到"方法增强"的效果。为此,我们需要写一个代理类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
|
关键点都在intercept方法里,被代理的类有方法调用时,在intercept中处理拦截逻辑,为了方便使用这个代理类,再写一个小工具:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
有了它就好办了:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
手动new的地方,改成用ProxyUtils生成代理类实例,还是跑刚才的main方法:
1 2 3 |
|
第2行的输出,便是AopProxy类拦截的输出,成功拦截,皆大欢喜!
注意事项:
1. 被代理的类,不能是内部类(即嵌套在类中的类),更不能是final类
2. 要拦截的方法,不能是private方法或final方法