記得今年年初剛開始面試的時候,被問的最多的就是你知道Spring的兩大核心嘛?那你說說什麼是AOP,什麼是IOC?我相信你可能也被問了不少次了。java
一、究竟是什麼是AOP?git
所謂AOP也就是面向切面編程,可以讓咱們在不影響原有業務功能的前提下,橫切擴展新的功能。這裏面有一個比較顯眼的詞咱們須要注意一下,橫切,它是基於橫切面對程序進行擴展的。github
二、AOP相關術語web
在Spring的AOP中有不少的術語,並且容易混淆,你們必定要先搞清楚這幾個概念:面試
鏈接點(Joinpoint):在程序執行過程當中某個特定的點,好比類初始化前、類初始化後,方法調用前,方法調用後;spring
切點(Pointcut):所謂切點就是你所切取的類中的方法,好比你橫切的這個類中有兩個方法,那麼這兩個方法都是鏈接點,對這兩個方法的定位就稱之爲切點;apache
加強(Advice):加強是織入到鏈接點上的一段程序,另外它還擁有鏈接點的相關信息;編程
目標對象(Target):加強邏輯的織入目標類,就是個人加強邏輯植入到什麼位置;tomcat
引介(Introduction):一種特殊的加強,它能夠爲類添加一些屬性喝方法;springboot
織入(Weaving):織入就是講加強邏輯添加到目標對象的過程;
代理(Proxy):一個類被AOP織入加強後,就會產生一個結果類,他是融合了原類和加強邏輯的代理類;
切面(Aspect):切面由切點和加強組成,他是橫切邏輯定義和鏈接點定義的組成;
三、AOP功能實踐
咱們這裏主要是學習SpringBoot中的一些功能,因此咱們這裏用的是SpringBoot工程,版本也是最新的2.0.5版本。
建立SpringBoot工程就不說了,咱們直接引入Maven的依賴:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency> <!-- 引入AOP --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.6.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.20</version> <configuration> <skip>true</skip> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> </executions> </plugin> </plugins> </build>
首先咱們來建立一個Controller類:
@RestController public class LoginController { @GetMapping(value = "/username") public String getLoginUserName(String userName, Integer age) { return userName + " --- " + age; } }
建立切面:
@Aspect @Component public class LogAspect { /** * 功能描述: 攔截對這個包下全部方法的訪問 * * @param:[] * @return:void **/ @Pointcut("execution(* com.example.springbootaop.controller.*.*(..))") public void loginLog() { } // 前置通知 @Before("loginLog()") public void loginBefore(JoinPoint joinPoint) { // 咱們從請求的上下文中獲取request,記錄請求的內容 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); System.out.println("請求路徑 : " + request.getRequestURL()); System.out.println("請求方式 : " + request.getMethod()); System.out.println("方法名 : " + joinPoint.getSignature().getName()); System.out.println("類路徑 : " + joinPoint.getSignature().getDeclaringTypeName()); System.out.println("參數 : " + Arrays.toString(joinPoint.getArgs())); } @AfterReturning(returning = "object", pointcut = "loginLog()") public void doAfterReturning(Object object) { System.out.println("方法的返回值 : " + object); } // 方法發生異常時執行該方法 @AfterThrowing(throwing = "e",pointcut = "loginLog()") public void throwsExecute(JoinPoint joinPoint, Exception e) { System.err.println("方法執行異常 : " + e.getMessage()); } // 後置通知 @After("loginLog()") public void afterInform() { System.out.println("後置通知結束"); } // 環繞通知 @Around("loginLog()") public Object surroundInform(ProceedingJoinPoint proceedingJoinPoint) { System.out.println("環繞通知開始..."); try { Object o = proceedingJoinPoint.proceed(); System.out.println("方法環繞proceed,結果是 :" + o); return o; } catch (Throwable e) { e.printStackTrace(); return null; } } }
註解概述:
@Apsect:將當前類標識爲一個切面;
@Pointcut:定義切點,這裏使用的是條件表達式;
@Before:前置加強,就是在目標方法執行以前執行;
@AfterReturning:後置加強,方法退出時執行;
@AfterThrowing:有異常時該方法執行;
@After:最終加強,不管什麼狀況都會執行;
@Afround:環繞加強;
測試:
異常測試:
四、定義自定義註解
應用場景:在我以前上個項目的時候,有這樣一個註解,就是在訪問其餘接口的時候必需要登陸,那麼這個時候咱們就定義一個註解,讓它去對用戶是否登陸進行校驗,那麼基於這樣的一個場景,咱們來定義一個校驗登陸的註解。
建立一個註解:
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Auth { String desc() default "驗證是否登陸"; }
建立一個AOP切面:
@Aspect @Component public class LoginAspect { @Pointcut(value = "@annotation(com.example.springbootaop.annotation.Auth)") public void access() { } @Before("access()") public void before() { System.out.println("開始驗證用戶是否登陸..."); } @Around("@annotation(auth)") public Object around(ProceedingJoinPoint pj, Auth auth) { // 獲取註解中的值 System.out.println("註解中的值 : " + auth.desc()); try { // 檢驗是否登陸 true 已經登陸 false 未登陸 Boolean flag = false; if (flag == true) { return "登陸成功"; } else { return "未登陸"; } } catch (Throwable throwable) { return null; } } }
測試未登陸:
測試登陸:
這樣咱們就能夠簡單的實現了一個登陸校驗的註解。
經過今天的分享你會使用AOP和自定義註解了嗎?我把源碼的地址放在下面,有興趣的朋友能夠看看。
GitHub地址:https://github.com/liangbintao/SpringBootIntegration.git
原創不易,若是感受不錯,給個順便擴散一下吧!