通知的分类
切入点以及表达式的使用
自定义注解
通知的分类
// @Before 前置通知
// @Around 环绕通知
// @AfterReturning 后置通知
// @AfterThrowing 异常通知
// @After 最终通知
// 无异常时执行顺序
前置通知
环绕通知的调用目标方法之前的代码
目标方法
环绕通知的调用目标方法之后的代码
后置通知
最终通知
// 有异常时执行顺序
前置通知
环绕通知的调用目标方法之前的代码
目标方法 抛出异常 异常通知
最终通知
切入点以及表达式的使用
package com.example.demo.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* @author wangli66
* @describtion aop advice类型,pointcut总结
* @create-time 9:40 2020/4/3
*/
@Component
@Aspect
public class AopTest {
/**
* @Description: 切入点的execution语法
* 切入点表达式的语法格式
* execution([权限修饰符] [返回值类型] [简单类名/全类名] [方法名]([参数列表]))
* execution(* com.example.demo.aop.TestAdvice.*(..))
* 第一个 * 表示任意的权限修饰符,任意的返回值类型;第二个 * 表示任意方法名;..表示方法参数任意
* execution(public * com.example.demo.aop.TestAdvice.*(..))
* 第一个 * 表示任意返回值类型;其他同上
* execution(* *.TestAdvice(..)) || execution(* *.add(..))
* 表达式中可以使用|| && !运算符,但是要注意将表达式写完整
*
* @param: []
* @return: void
* @Date: 2020/4/3
*/
@Pointcut("execution(public * com.example.demo.aop.TestAdvice.*(..))")
public void testPointCut() {
}
/**
* @Description: 前置通知,在目标方法执行之前执行执行的通知。
* 前置通知方法,可以没有参数,也可以额外接收一个JoinPoint,Spring会自动将该对象传入,
* 代表当前的连接点,通过该对象可以获取目标对象 和 目标方法相关的信息。
* 注意,如果接收JoinPoint,必须保证其为方法的第一个参数,否则报错。
* @param: []
* @return: void
* @Date: 2020/4/3
*/
@Before("testPointCut()")
public void testBefore() {
System.out.println("前置通知");
}
/**
* @Description: 环绕通知,在目标方法执行之前和之后都可以执行额外代码的通知。
* 在环绕通知中必须显式的调用目标方法,目标方法才会执行,这个显式调用时通过ProceedingJoinPoint来实现的,可以在环绕通知中接收一个此类型的形参,spring容器会自动将该对象传入,注意这个参数必须处在环绕通知的第一个形参位置。
* 要注意,只有环绕通知可以接收ProceedingJoinPoint,而其他通知只能接收JoinPoint。
* 环绕通知需要返回返回值,否则真正调用者将拿不到返回值,只能得到一个null。
* 环绕通知有控制目标方法是否执行、有控制是否返回值、有改变返回值的能力。
* 环绕通知虽然有这样的能力,但一定要慎用,不是技术上不可行,而是要小心不要破坏了软件分层的“高内聚 低耦合”的目标
* @param: []
* @return: void
* @Date: 2020/4/3
*/
@Around(value="execution(public * com.example.demo.aop.TestAdvice.*(..)) && @annotation(AdviceTargetAnno)")
public void testAround(ProceedingJoinPoint pjp) {
System.out.println("环绕通知进来了-----");
try {
pjp.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("环绕通知结束了-----");
}
/**
* @Description: 后置通知,在目标方法执行之后执行的通知。
* 在后置通知中也可以选择性的接收一个JoinPoint来获取连接点的额外信息,但是这个参数必须处在参数列表的第一个。
* @param: []
* @return: void
* @Date: 2020/4/3
*/
@AfterReturning("testPointCut()")
public void testReturn() {
System.out.println("后置通知");
}
/**
* @Description:最终通知,是在目标方法执行之后执行的通知。
* 和后置通知不同之处在于,后置通知是在方法正常返回后执行的通知,如果方法没有正常返-例如抛出异常,则后置通知不会执行。
* 而最终通知无论如何都会在目标方法调用过后执行,即使目标方法没有正常的执行完成。
* 另外,后置通知可以通过配置得到返回值,而最终通知无法得到。
* 最终通知也可以额外接收一个JoinPoint参数,来获取目标对象和目标方法相关信息,但一定要保证必须是第一个参数。
* @param: []
* @return: void
* @Date: 2020/4/3
*/
@After("testPointCut()")
public void testAfter() {
System.out.println("最终通知");
}
/**
* @Description: 异常通知,在目标方法抛出异常时执行的通知
* 可以配置传入JoinPoint获取目标对象和目标方法相关信息,但必须处在参数列表第一位
* 另外,还可以配置参数,让异常通知可以接收到目标方法抛出的异常对象。
* @param: []
* @return: void
* @Date: 2020/4/3
*/
@AfterThrowing("testPointCut()")
public void testThrowing(JoinPoint joinPoint) {
System.out.println("异常通知");
}
}
自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AdviceTargetAnno {
String methodName() default "";
}
// 可以用来修饰注解,是注解的注解,称为元注解。
// @Target
表示此注解所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。
// @Retention
Retention注解有一个属性value,是RetentionPolicy类型的,Enum RetentionPolicy是一个枚举类型,
这个枚举决定了Retention注解应该如何去保持,也可理解为Rentention 搭配 RententionPolicy使用。RetentionPolicy有3个值:CLASS RUNTIME SOURCE
按生命周期来划分可分为3类:
1、RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
2、RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
3、RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
这3个生命周期分别对应于:Java源文件(.java文件) ---> .class文件 ---> 内存中的字节码。
那怎么来选择合适的注解生命周期呢?
首先要明确生命周期长度 SOURCE < CLASS < RUNTIME ,所以前者能作用的地方后者一定也能作用。
一般如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解,比如@Deprecated使用RUNTIME注解
如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS注解;
如果只是做一些检查性的操作,比如 @Override 和 @SuppressWarnings,使用SOURCE 注解
// @Documented
注解表明这个注解应该被 javadoc工具记录. 默认情况下,javadoc是不包括注解的. 但如果声明注解时指定了 @Documented,则它会被 javadoc 之类的工具处理, 所以注解类型信息也会被包括在生成的文档中,是一个标记注解,没有成员。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术