寫一個最簡單明細的切面,希望可以復制了直接用。我會盡量把注釋寫詳細
1.引入jar
因為我是用的springboot,所以只需要引入一個包,如果你習慣的是引入兩個包 也可以引入下面的兩個包
SpingBoot:
<!--aop切面-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Spring:
<!--aop切面-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
2.切面類
作用:可以做登錄攔截或者參數攔截
首先新建一個.java文件,在類上加上@Component和@Aspect 注解
@Component //把切面類加入到IOC容器中
@Aspect //使之成為切面類
@Order(1) //如果有多個 可以定義來控制順序 數字越小執行順序靠前
public class AopHandlerAspect {
//日志 我是使用的hutool中的日志方法 你可以改為你自己的
private Log logger = LogFactory.get();
//為了記錄執行時間 方便調試 如果不需要可以去掉
ThreadLocal<Long> startTime = new ThreadLocal<>();
//定義一個切入點 我這里是從controller切入 不是從注解切入
//詳情看下方的 切入點表達式
@Pointcut("execution(public * cn.o.generate.controller.*.*(..))")
public void pointCut() {}
//在進入方法前執行 可以對參數進行限制或者攔截
//通常在這邊做日志存儲存到數據庫中
@Before("pointCut()")
public void before(JoinPoint joinPoint) throws Throwable {
logger.info("==================前置執行=====================>");
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
logger.info("請求來源: =》" + request.getRemoteAddr());
logger.info("請求URL:" + request.getRequestURL().toString());
logger.info("請求方式:" + request.getMethod());
logger.info("響應方法:" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
logger.info("請求參數:" + Arrays.toString(joinPoint.getArgs()));
logger.info("==================前置執行完成==================>");
startTime.set(System.currentTimeMillis());
}
//環繞執行
//定義需要匹配的切點表達式,同時需要匹配參數
@Around("pointCut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
StaticLog.info("==================方法環繞前置start=====================>");
//這句必須有 往下執行方法
Object result = pjp.proceed();
StaticLog.info("==================方法環繞后置start=====================>");
StaticLog.info("耗時(毫秒):" + time);
StaticLog.info("返回數據:{}", result);
StaticLog.info("==================方法環繞end======================>");
return result;
}
//在方法執行后執行 可以打印返回的數據 判斷數據是否是自己需要的
@After("pointCut()")
public void after(JoinPoint point) {
if (startTime.get() == null) {
startTime.set(System.currentTimeMillis());
}
logger.info("==================后置執行======================>");
logger.info("耗時(毫秒):" + (System.currentTimeMillis() - startTime.get()));
logger.info("返回數據:{}", point.getArgs());
logger.info("==================后置執行完成==================>");
}
}
切入點表達式
定義切入點的時候需要一個包含名字和任意參數的簽名,還有一個切入點表達式,如execution(public * com.example.aop...(..))
切入點表達式的格式:execution([可見性]返回類型[聲明類型].方法名(參數)[異常])
其中[]內的是可選的,其它的還支持通配符的使用:
1) *:匹配所有字符
2) ..:一般用於匹配多個包,多個參數
3) +:表示類及其子類
4)運算符有:&&,||,!
切入點表達式關鍵詞用例:
1)execution:用於匹配子表達式。
//匹配com.cjm.model包及其子包中所有類中的所有方法,返回類型任意,方法參數任意
@Pointcut(“execution(* com.cjm.model...(..))”)
public void before(){}
2)within:用於匹配連接點所在的Java類或者包。
//匹配Person類中的所有方法
@Pointcut(“within(com.cjm.model.Person)”)
public void before(){}
//匹配com.cjm包及其子包中所有類中的所有方法
@Pointcut(“within(com.cjm..*)”)
public void before(){}
3) this:用於向通知方法中傳入代理對象的引用。
@Before(“before() && this(proxy)”)
public void beforeAdvide(JoinPoint point, Object proxy){
//處理邏輯
}
4)target:用於向通知方法中傳入目標對象的引用。
@Before(“before() && target(target)
public void beforeAdvide(JoinPoint point, Object proxy){
//處理邏輯
}
5)args:用於將參數傳入到通知方法中。
@Before(“before() && args(age,username)”)
public void beforeAdvide(JoinPoint point, int age, String username){
//處理邏輯
}
6)@within :用於匹配在類一級使用了參數確定的注解的類,其所有方法都將被匹配。
@Pointcut(“@within(com.cjm.annotation.AdviceAnnotation)”)
- 所有被@AdviceAnnotation標注的類都將匹配
public void before(){}
7)@target :和@within的功能類似,但必須要指定注解接口的保留策略為RUNTIME。
@Pointcut(“@target(com.cjm.annotation.AdviceAnnotation)”)
public void before(){}
8)@args :傳入連接點的對象對應的Java類必須被@args指定的Annotation注解標注。
@Before(“@args(com.cjm.annotation.AdviceAnnotation)”)
public void beforeAdvide(JoinPoint point){
//處理邏輯
}
9)@annotation :匹配連接點被它參數指定的Annotation注解的方法。也就是說,所有被指定注解標注的方法都將匹配。
@Pointcut(“@annotation(com.cjm.annotation.AdviceAnnotation)”)
public void before(){}
10)bean:通過受管Bean的名字來限定連接點所在的Bean。該關鍵詞是Spring2.5新增的。
@Pointcut(“bean(person)”)
public void before(){}