可獲得兩大新人禮包
36份一線互聯網Java面試電子書
84個Java稀缺面試題視頻
1. 什麼是AOP
AOP:Aspect Oriented Programming的縮寫,意為:面向切面編程。面向切面編程的目標就是分離關注點。什麼是關注點呢?就是你要做的事,就是關注點。假如你是個公子哥,沒啥人生目標,天天就是衣來伸手,飯來張口,整天只知道玩一件事!那麼,每天你一睜眼,就光想著吃完飯就去玩(你必須要做的事),但是在玩之前,你還需要穿衣服、穿鞋子、疊好被子、做飯等等等等事情,這些事情就是你的關注點,但是你只想吃飯然後玩,那麼怎麼辦呢?這些事情通通交給別人去幹。在你走到飯桌之前,有一個專門的僕人A幫你穿衣服,僕人B幫你穿鞋子,僕人C幫你疊好被子,僕人C幫你做飯,然後你就開始吃飯、去玩(這就是你一天的正事),你幹完你的正事之後,回來,然後一系列僕人又開始幫你幹這個幹那個,然後一天就結束了!
AOP的好處就是你只需要幹你的正事,其它事情別人幫你幹。也許有一天,你想裸奔,不想穿衣服,那麼你把僕人A解僱就是了!也許有一天,出門之前你還想帶點錢,那麼你再僱一個僕人D專門幫你幹取錢的活!這就是AOP。每個人各司其職,靈活組合,達到一種可配置的、可插拔的程序結構。
2. SpringBoot中的AOP處理
2.1 AOP依賴
使用AOP,首先需要引入AOP的依賴。
2.2 實現AOP切面
SpringBoot中使用AOP非常簡單,假如我們要在項目中打印一些log,在引入了上面的依賴之後,我們新建一個類LogAspectHandler,用來定義切面和處理方法。只要在類上加個@Aspect註解即可。
@Aspect
@Component
public class LogAspectHandler {
}
這裡主要介紹幾個常用的註解:
1.@Pointcut:定義一個切面,即上面所描述的關注的某件事入口。
2.@Before:在做某件事之前做的事。
3.@After:在做某件事之後做的事。
4.@AfterReturning:在做某件事之後,對其返回值做增強處理。
5.@AfterThrowing:在做某件事拋出異常時,處理。
具體看如下代碼註釋:
/**
* 使用AOP處理log
* @author shengwu ni
* @date 2018/05/04 20:24
*/
@Aspect
@Component
public class LogAspectHandler {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 定義一個切面,攔截cn.nxcoder.blog.controller包下的所有方法
*/
@Pointcut("execution(* com.itcodai.demo7.controller.*.*(..))")
public void pointCut() {}
/**
* 在上面定義的切面方法之前執行該方法
* @param joinPoint jointPoint
*/
@Before("pointCut()")
public void doBefore(JoinPoint joinPoint) {
// 獲取執行的方法和參數
Signature signature = joinPoint.getSignature();
String classMethod = signature.getDeclaringTypeName() + "." + signature.getName();
logger.info("即將執行方法: {}", classMethod);
}
/**
* 在上面定義的切面方法之後執行該方法
* @param joinPoint jointPoint
*/
@After("pointCut()")
public void doAfter(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
String classMethod = signature.getDeclaringTypeName() + "." + signature.getName();
logger.info("方法{}已經執行完", classMethod);
}
/**
* 在上面定義的切面方法返回後執行該方法,可以捕獲返回對象或者對返回對象進行增強
* @param joinPoint jointPoint
* @param Object result
*/
@AfterReturning(pointcut = "pointCut()", returning = "result")
public void doAfterReturning(JoinPoint joinPoint, Object result) {
Signature signature = joinPoint.getSignature();
String classMethod = signature.getDeclaringTypeName() + "." + signature.getName();
logger.info("方法{}執行完畢,返回參數為:{}", classMethod, result);
logger.info("對返回參數進行業務上的增強:{}", result + "增強版");
}
/**
* 在上面定義的切面方法執行拋異常時,執行該方法
* @param joinPoint jointPoint
* @param ex ex
*/
@AfterThrowing(pointcut = "pointCut()", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
Signature signature = joinPoint.getSignature();
String method = signature.getDeclaringTypeName() + "." + signature.getName();
// 處理異常的邏輯
logger.info("執行方法{}出錯,異常為:{}", method, ex);
}
}
JointPoint對象很有用,它可以獲取請求的方法名,包括參數(joinPoint.getArgs())等,在@Before中,我們還可以獲取用戶訪問的ip等信息,比如:
// 獲取請求的url和ip
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String url = request.getRequestURL().toString();
String ip = request.getRemoteAddr();
在@AfterReturning中,屬性returning的值必須要和參數保持一致,否則檢測不到。對於返回值的增強,可以根據業務需要做相應的封裝,上面只是做個demo。 下面寫個Controller來測試一下:
@RestController
public class TestController {
@GetMapping("/test/{name}")
public String testAop(@PathVariable String name) {
return "Hello " + name;
}
}
啟動程序,在瀏覽器中輸入:localhost:8080/test/aop看下結果:
即將執行方法: com.itcodai.demo7.controller.TestController.testAop
方法com.itcodai.demo7.controller.TestController.testAop已經執行完
方法com.itcodai.demo7.controller.TestController.testAop執行完畢,返回參數為:Hello aop
對返回參數進行業務上的增強:Hello aop增強版
當然了,除了如上定義切面的方式外,還可以針對某個註解定義切面,比如我們對具有@GetMapping註解的方法做切面,可以如下定義切面:
@Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
public void annotationCut() {}
然後在其他方法上使用該切面,就會切入註解是@GetMapping的方法。因為在實際項目中,可能對於不同的註解有不同的邏輯處理,比如@GetMapping、@PostMapping、@DeleteMapping等。所以這種按照註解的切入方式也很常用。
閱讀更多 李紅 的文章