Spring:SpringAop配合自定義注解實現切面編程


此文章只作為筆記記錄,不作為講解文章。

1. SpringAop簡介

      傳統的OOP開發中的代碼邏輯是自上而下的,而這些過程會產生一些橫切性問題,這些橫切性的問題和我們的主業務邏輯關系不大,這些橫切性問題不會影響到主邏輯實現的,但是會散落到代碼的各個部分,難以維護。AOP是處理一些橫切性問題,AOP的編程思想就是把這些問題和主業務邏輯分開,達到與主業務邏輯解耦的目的。

      SpringAop的應用場景
  • 日志記錄
  • 權限驗證
  • 效率檢查
  • 事務管理
  • exception

2. 依賴包引入

//SpringBoot項目引入Aop依賴
       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <version>2.1.5.RELEASE</version>
        </dependency>


//Spring項目引入Aop依賴
        <!-- springAop依賴包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.2.4.RELEASE</version>
        </dependency>
        <!--  springAop依賴Aspect的語法標准包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.0.7.RELEASE</version>
        </dependency>

3. Aop實現示例

項目結構

3.1 定義依賴注入掃描器

AppConfig配置類

package com.java.study.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

//掃描注解
@ComponentScan("com.java.study")
//開啟Aop,默認為false(JDK代理模式) true(Cglib模式)
// (this) JDK代理時,指向接口和代理類proxy,cglib代理時 指向接口和子類(不使用proxy)
@EnableAspectJAutoProxy(proxyTargetClass = true) public class AppConfig { }

3.2 自定義service方法

TestService測試方法類

package com.java.study.service;

import org.springframework.stereotype.Component;

@Component("testService")
public class TestService {

    public void Test1(){
        System.out.println("這是測試方法 test1 ......");
    }

}

3.3 定義切面類

TestAdvice切面類

package com.java.study.aspect;

import com.java.study.custom.KthLog;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.logging.Logger;

@Component
@Aspect
public class TestAdvice {
    private final static Logger logger = Logger.getLogger("TestAdvice.class");

     // @annotation匹配的是自定義注解所標注的方法
    @Pointcut("@annotation(com.java.study.custom.KthLog)")
    public void loggerMother(){}

    @Pointcut("execution(* com.java.study..*())")
    public void loggerMother2(){}

    @Pointcut("execution(* com.java.study.*(java.lang.String))")
    public void loggerMother3(){}

    @Before("loggerMother() && @within(log)")
    public void Before(JoinPoint pointcut, KthLog log){
        System.out.println(" 方法名:"+ pointcut.getSignature().getName() +"日志輸出:"+log.value());
    }

    @After("loggerMother2()")
    public void Before(){
        System.out.println(" 測試增強方法 。。。。。。");
    }

    @Around("loggerMother2()&&!loggerMother3()")
    public Object arround(ProceedingJoinPoint joinPoint) {
        logger.info("方法環繞start.....");
        try {
            Object o = joinPoint.proceed();
            logger.info("方法環繞proceed,結果是 :" + o);
            return o;
        } catch (Throwable e) {
            return null;
        }
    }

}
//execution表達式 (用於匹配方法)
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
問號 ? 表示當前項可以有也可以沒有,其中各項的語義如下:
modifiers-pattern:方法的可見性,如public,protected;(private不能被代理)
ret-type-pattern:方法的返回值類型,如int,void等;
declaring-type-pattern:方法所在類的全路徑名,如com.spring.Aspect;
name-pattern:方法名類型,如buisinessService();
param-pattern:方法的參數類型,如java.lang.String;
throws-pattern:方法拋出的異常類型,如java.lang.Exception;
.. : 表示當前包及其子包
//within表達式 (用於匹配類)
within(declaring-type-pattern)
declaring-type-pattern:方法所在類的全路徑名,如com.spring.Aspect;
//args表達式 (args匹配的是運行時傳遞給方法的參數類型) 與 execution 不同
args(java.io.Serializable) //匹配運行時傳遞的參數類型為指定類型的、且參數個數和順序匹配

3.4 定義啟動類

TestApp啟動類

package com.java.study;

import com.java.study.config.AppConfig;
import com.java.study.service.TestService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestApp {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        TestService testService = (TestService) ac.getBean("testService");
        testService.Test1();
    }
}

4. 自定義注解

定義 KthLog注解類

package com.java.study.custom;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

//元注解 @Retention @Retention(RetentionPolicy.RUNTIME)
public @interface KthLog { String value() default ""; }

元注解講解

java.lang.annotation 提供了四種元注解,專門注解其他的注解(在自定義注解的時候,需要使用到元注解

   @Retention – 什么時候使用該注解

   @Target – 注解用於什么地方   

   @Documented – 注解是否將包含在JavaDoc中   

   @Inherited – 是否允許子類繼承該注解

1.)@Retention – 定義該注解的生命周期
  ●   RetentionPolicy.SOURCE : 在編譯階段丟棄。這些注解在編譯結束之后就不再有任何意義,所以它們不會寫入字節碼。@Override, @SuppressWarnings都屬於這類注解。
  ●   RetentionPolicy.CLASS : 在類加載的時候丟棄。在字節碼文件的處理中有用。注解默認使用這種方式
  ●   RetentionPolicy.RUNTIME : 始終不會丟棄,運行期也保留該注解,因此可以使用反射機制讀取該注解的信息。我們自定義的注解通常使用這種方式。

2.)@Target – 表示該注解用於什么地方。默認值為任何元素,表示該注解用於什么地方。可用的ElementType 參數包括
  ● ElementType.CONSTRUCTOR: 用於描述構造器
  ● ElementType.FIELD: 成員變量、對象、屬性(包括enum實例)
  ● ElementType.LOCAL_VARIABLE: 用於描述局部變量
  ● ElementType.METHOD: 用於描述方法
  ● ElementType.PACKAGE: 用於描述包
  ● ElementType.PARAMETER: 用於描述參數
  ● ElementType.TYPE: 用於描述類、接口(包括注解類型) 或enum聲明

3.)@Documented – 一個簡單的Annotations 標記注解,表示是否將注解信息添加在java 文檔中。

4.)@Inherited – 定義該注釋和子類的關系
  ● @Inherited 元注解是一個標記注解,@Inherited 闡述了某個被標注的類型是被繼承的。如果一個使用了@Inherited 修飾的annotation 類型被用於一個class,則這個annotation 將被用於該class 的子類。

5. 配合Aop增強類使用

修改TestService測試方法類

package com.java.study.service;

import com.java.study.custom.KthLog;
import org.springframework.stereotype.Component;

@Component("testService")
public class TestService {

    @KthLog("這是TestService類中******")
    public void Test1(){
        System.out.println("這是測試方法 test1 ......");
    }

}

之后使用TestAdvice切面類的loggerMother方法即可。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM