1.基本概念java
面向切面編程也叫Aop。面向對象的特色是繼承、封裝、多態。封裝要求咱們將不一樣的功能分散到不一樣的類中去實現,每一個類有本身的職責,這樣的好處是下降了代碼的複雜度,使得類能夠重用;可是在分散代碼的同時,也會增長代碼的複雜性,好比一些通用的功能,日誌,權限等。在以前進行app後端開發的時候,爲了跟蹤問題,須要對每一個api的請求日誌都記錄下來,能夠在每一個方法的入口處都加上log.info.....,可是這樣不夠靈活,若是之後對記錄的日誌的方式變化,那麼改動可想而知,也許你會說封裝起來,每一個類中進行調用,可是這樣就耦合太嚴重了。這時候就可使用Aop,在運行時動態的插入代碼;這種在運行時,動態地將代碼切入到類的指定方法、指定位置上的編程思想就是面向切面的編程。web
通常而言,咱們將切入到指定類指定方法的代碼片斷稱爲切面,將切入到哪些類,或者哪些方法的稱爲切入點。使用Aop,將不一樣類中重複的地方抽取出來,造成切面,在須要的地方插入到對象或方法上,從而動態改變原有的行爲。spring
2.一個簡單權限校驗的例子編程
基於註解的切面,須要定義註解,定義切面後端
1>定義註解api
package com.example.springbootDemo.service.aspect; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE,ElementType.METHOD})//註解類型,級別 @Retention(RetentionPolicy.RUNTIME)//運行時註解 public @interface PermissionCheck { String value() default ""; }
2>根據註解定義一個切面springboot
package com.example.springbootDemo.service.aspect; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.lang.reflect.Method; @Aspect @Component public class PermissionAspect { /** * 選取切入點爲自定義註解 */ @Pointcut("@annotation(com.example.springbootDemo.service.aspect.PermissionCheck)") public void permissionCheck(){} /** * 在切入點業務邏輯執行前進行操做 * @param joinPoint */ @Before(value = "permissionCheck();") public void before(JoinPoint joinPoint) throws Throwable { Signature signature = joinPoint.getSignature(); //獲取鏈接點的方法簽名對象 if (!(signature instanceof MethodSignature)){ throw new PermissionCheckException("user permission check fail,stop this request!"); } MethodSignature methodSignature = (MethodSignature) signature; Object target = joinPoint.getTarget(); Method method = target.getClass().getDeclaredMethod(methodSignature.getName(),methodSignature.getParameterTypes());//獲取到當前執行的方法 PermissionCheck annotation = method.getAnnotation(PermissionCheck.class);//獲取方法的註解 System.out.println(annotation); System.out.println(annotation.value()); System.out.println("我是在執行業務邏輯以前"); } /** * 在業務邏輯執行後進行的操做 * @param joinPoint */ @After(value = "permissionCheck();") public void after(JoinPoint joinPoint) throws NoSuchMethodException { System.out.println("我是在執行業務邏輯後"); } @Around(value = "permissionCheck();") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("around方法執行前1"); Object proceed = joinPoint.proceed(); System.out.println("around方法執行前2"); System.out.println("proceed:"+proceed.toString()); return proceed; } }
3>權限驗證不知足的時候須要定義異常,定義一個PermissionCheckException來標識權限不知足的異常信息app
package com.example.springbootDemo.service.aspect; public class PermissionCheckException extends RuntimeException { public PermissionCheckException(String message) { super(message); } }
4>定義Advice處理該異常this
package com.example.springbootDemo.service.aspect; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; @ControllerAdvice public class PermissionAdvice { @ExceptionHandler(value = PermissionCheckException.class) @ResponseStatus(value = HttpStatus.OK) @ResponseBody public String dealWithPermissionCheckException(PermissionCheckException exception){ System.out.println(exception.getMessage()); return exception.getMessage(); } }
注:PermissionCheck 註解必須聲明爲@Retention(RetentionPolicy.RUNTIME),不然,默認的是@Retention(RetentionPolicy.CLASS) 註解會在class字節碼文件中存在,但運行時沒法得到;.net
3.@Before @Around @After 等 advice 的執行順序
正常狀況的執行順序
異常狀況
注:Around中
Object proceed = joinPoint.proceed();
表示
Proceed with the next advice or target method invocation
表示處理返回的結果對象,必須返回;