往期相关文档
AOP核心概念和SpringAOP切面
最全 SpringAOP 切面表达式
本篇博文尽力在实际生产和实例代码之间做好平衡,除SpringAOP相关语法知识外,还用到了Lombok的一些注解,并基于Junit进行测试。
导言
AOP相关知识可以看看这篇博文,里面对AOP核心概念做了解释,以下抽出部分核心知识结合SpringAOP做出解释
- 切面 Aspect : 在SpringAOP中Aspect就是被
@Aspect
注解注上的类,其中的撰写Advice通知逻辑。- 通知 Advice :
@Around、@After、@Before
等注解注入的对象 - 切点 PointCut : 可以使用
@PointCut
声明,也可以在Advice注解内声明,本文采用后一种方法。
- 通知 Advice :
- JointPoint 连接点: 在SpringAOP中用于获取
被横切点
的上下文信息和控制其执行流程对于环绕Advice可以
定义Service
@Service
class ServiceDemo {
public void doA(String str) {
System.out.println("i am doAing,str=" + str);
}
public void doB() {
System.out.println("i am doBing");
}
}
上文代码中定义了两个Service方法,A方法带参,B方法不带参。使用@Service
注入IOC容器。
定义日志切面
使用最强大最全面的环绕切面
@Aspect // 声明切面类
@Component //注入IOC
@Slf4j // 日志服务
class AspectDemo {
@Around("execution(public * ServiceDemo.*(..))") //切面注解
@SneakyThrows
public Object logAspect(ProceedingJoinPoint pjp) {
// 获取方法签名
String signature = pjp.getSignature().toString();
// 方法开始前打日志(输出方法引用串和参数列表)
log.info("{} start,param={}", signature, pjp.getArgs());
// 调用实际方法
Object res = pjp.proceed();
// 方法结束后打日志
log.info("{} end", signature);
// 此处的返回值是被切面处理后给客户端的返回值
return res;
}
}
所有的切面(Aspect)方法,即被@Around、@After、@Before
注上的方法所在的类都需要@Aspect
,否则SpringIOC容器不会将该bean识别为切面(SpringAOP基于IOC容器,参考这里)。当然该Aspect类也是需要注入IOC容器的,本Aspect使用@Service
注解注入。
对于日志服务,本Aspect使用Lombok的@Slf4j
注解,并且因为调用pjp.proceed()
会抛出Throwable
异常,所以使用lombok的@SneakyThrows
注解。
重点介绍下@Around("execution(public * ServiceDemo.*(..))")
:
@Around
声明logAspect
方法是环绕通知(Advice),并且使用execution PCD
PointCut切面表达式约束了logAspect
方法的作用范围: 所有ServiceDemo类的实例下的public方法,匹配任意返回值 ,匹配任意方法名,匹配任意数量参数。
以下就execution PCD做进一步解释
execution PCD(PointCut Designators)
SpringAOP的PCD是完全兼容AspectJ的。
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)
throws-pattern?)
execution(修饰符匹配式? 返回类型匹配式 类名匹配式? 方法名匹配式(参数匹配式) 异常匹配式?)
代码块中带?
符号的匹配式都是可选的,对于execution PCD
必不可少的只有三个:
- 返回值匹配值
- 方法名匹配式
- 参数匹配式
接下来分析下execution(public * ServiceDemo.*(..))
: 该PCD 匹配public修饰符,返回值是*
,即任意返回值类型都行,ServiceDemo
是类名匹配式只要全局依可见性唯一就行,.*
是方法名匹配式,匹配所有方法,..
是参数匹配式,匹配任意参数数量、任意参数类型的方法。
再举一些其他例子:
-
execution(* com.xyz.service..*.*(..))
: 匹配com.xyz.service及其子包下的任意方法。 -
execution(* joke(Object+)))
:匹配任意名字为joke
的方法,且其动态入参是是Object类型或该类的子类。 -
execution(* joke(String,..))
:匹配任意名字为joke
的方法,该方法 一个入参为String(不可以为子类),后面可以有任意个入参且入参类型不限 -
execution(* com..*.*Dao.find*(..))
: 匹配指定包下find开头的方法 -
execution(* com.baobaotao.Waiter+.*(..))
: 匹配com.baobaotao包下Waiter
及其子类的所有方法。
SpringAOP其他
更多SpringAOP Advice请看这里,更多PCD语法请看下期。
测试
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Loader.class)
public class AOPSt1 {
@Resource
private ServiceDemo serviceDemo;
@Test
@SneakyThrows
public void test_Aspect() {
serviceDemo.doA("hello world");
serviceDemo.doB();
serviceDemo.doC(1);
}
}
输出:
总结
SpringAOP切面核心就一个地方: 被@Aspect
注解的类,将这个类注入IOC容器中。
之后在其中使用advice注解定义横切逻辑(利用@Around、@After等
)。
环绕切面配合ProceedingJoinPoint
类使用,其他切面看情况使用JoinPoint
或不带参。
参考文章
一键三连吧