本章介紹的@Aspect攔截用的非注解方式,而是通過切入點@Pointcut指定要攔截的目錄芝囤,本章節(jié)實現(xiàn)了攔截請求和返參后對其進行修改,類似于過濾器的作用
1.第一步先先創(chuàng)建一個參數(shù)類用于傳參測試
User.java
package com.example.springboot.cheng.domain; //這行地址不需要復(fù)制
import lombok.Data;
@Data
public class User {
private String name;
private Integer age;
private String email;
}
2.第二步在controller層創(chuàng)建一個測試類用于測試送丰,對于返回類型AjaxResult這個是自己項目封裝的 怒详,可以自由修改為String也可以
AspectTestController.java
package com.example.springboot.controller; //這行地址不需要復(fù)制
import com.example.springboot.cheng.domain.User;
import com.example.springboot.common.core.domain.AjaxResult;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/atc")
public class AspectTestController {
/**
* 用于測試攔截有參方法
* @param user
* @return
*/
@PostMapping
public AjaxResult achieve(@RequestBody User user){
System.out.println("方法執(zhí)行-----------");
return AjaxResult.success(user);
}
/**
* 用于測試攔截?zé)o參的方法
* @return
*/
@GetMapping
public AjaxResult achieve(){
System.out.println("方法執(zhí)行-----------");
return AjaxResult.success();
}
}
3.最后一步,創(chuàng)建一個攔截器
TestAspect.java
package com.example.springboot.common.aspectj; //這行地址不需要復(fù)制
import com.example.springboot.cheng.domain.User;
import com.example.springboot.common.core.domain.AjaxResult;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
/**
* @description 使用該切面攔截時烙懦,有兩種方式,選擇其中一種注掉另一種即可驱入,
* 建議使用第二種,因為可以攔截后修改傳入?yún)?shù):
* 1.@Before+@After
* 2.@Around
*/
@Aspect
@Component
public class TestAspect {
private static final Logger logger = LoggerFactory.getLogger(TestAspect.class);
ThreadLocal<Long> startTime = new ThreadLocal<>();
/**
* 第一個*代表返回類型不限
* 第二個*代表所有類
* 第三個*代表所有方法
* (..) 代表參數(shù)不限
* com.example.springboot.controller 測試的controller層
*/
@Pointcut("execution(public * com.example.springboot.controller.*.*(..))")
public void pointCut(){};
//@Before(value = "pointCut()")
public void before(JoinPoint joinPoint){
System.out.println("方法執(zhí)行前執(zhí)行......before");
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
logger.info("<=====================================================");
logger.info("請求來源: =》" + request.getRemoteAddr());
logger.info("請求URL:" + request.getRequestURL().toString());
logger.info("請求方式:" + request.getMethod());
logger.info("響應(yīng)方法:" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
logger.info("請求參數(shù):" + Arrays.toString(joinPoint.getArgs()));
logger.info("連接點的方法簽名對象:"+joinPoint.getSignature());
logger.info("連接點所在的目標對象:"+joinPoint.getTarget());
logger.info("代理對象:"+joinPoint. getThis());
logger.info("------------------------------------------------------");
startTime.set(System.currentTimeMillis());
}
// 定義需要匹配的切點表達式氯析,同時需要匹配參數(shù)
/**
* @description 要攔截修改參數(shù)的值只有使用這個方法亏较,Around相當于before+after
* @param pjp
* @param arg 類型可以根據(jù)pointCut指定切點類下的方法確定,也可以使用統(tǒng)一的Object,也可以不寫參數(shù)
* @return
* @throws Throwable
*/
@Around("pointCut() && args(arg)")
public Object around(ProceedingJoinPoint pjp, Object arg) throws Throwable{
logger.info("入?yún)ⅲ簕}",arg);
logger.info("方法環(huán)繞start...around");
//修改入?yún)?讓傳入年齡參數(shù)減去5
User user = (User)pjp.getArgs()[0];
user.setAge(user.getAge()-5);
Object[] objects = new Object[]{user};
Object object = pjp.proceed(objects);
logger.info("方法環(huán)繞end...around");
logger.info("出參:{}",object.toString());
return object;
}
//@After("within(com.example.springboot.controller.*)")
public void after(){
System.out.println("方法之后執(zhí)行...after.");
}
/**
*
* @param AjaxResult rst 該參數(shù)類型需要與測試的Controller層的返回值類型一致掩缓,否則不生效雪情,也就是找不到
* 該測試中的AjaxResult是測試項目中封裝好的出參
*/
//@AfterReturning(pointcut="pointCut()",returning = "rst")
public void afterRunning(AjaxResult rst){
if(startTime.get() == null){
startTime.set(System.currentTimeMillis());
}
System.out.println("方法執(zhí)行完執(zhí)行...afterRunning");
logger.info("耗時(毫秒):" + (System.currentTimeMillis() - startTime.get()));
logger.info("返回數(shù)據(jù):{}", rst);
logger.info("==========================================>");
}
@AfterThrowing("within(com.example.springboot.controller.*)")
public void afterThrowing(){
System.out.println("異常出現(xiàn)之后...afterThrowing");
}
}
4.注意
4.1 關(guān)于第二點中的返回參數(shù)類型AjaxResult
如果想要學(xué)習(xí)或者加上該封裝結(jié)構(gòu),可以在筆者的項目筆記目錄下尋找“項目返參結(jié)構(gòu)封裝”就可以直接復(fù)制了你辣,當然也可以自己返回一個String或者Object類型先測試用著
4.2 本測試中注掉的注解
本測試例子中已經(jīng)把 @Before和@After和@AfterReturning注掉了巡通,只使用的@Around,可以直接用postman調(diào)用測試的舍哄,測完還可以放開前面三個注解宴凉,再把@Around注掉再測就可以了。
4.2 @Around測試無參方法的問題
無參的那個測試不能用@Around測試表悬,想要用@Around測試需要改一下弥锄,@Around("pointCut() && args(arg)")改為@Around("pointCut() "),下面的入?yún)⒑托薷膮?shù)部分去掉即可
5.測試
攔截測試1.png