淺析Java自定義注解aop切面的使用介紹


  Java自定義注解的簡單介紹就不說了,這里主要說一下自定義注解 aop 切面的使用。

一、什么是AOP?

1、AOP為Aspect Oriented Programming的縮寫,意為:面向切面編程。

  AOP是一種編程范式,隸屬於軟工范疇,指導開發者如何組織程序結構。AOP最早由AOP聯盟的組織提出的,制定了一套規范。Spring將AOP思想引入到框架中,必須遵守AOP聯盟的規范,通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。

  AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生范型。

  利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率

2、AOP的作用及優勢是什么?

(1)作用:

  AOP采取橫向抽取機制,取代了傳統縱向繼承體系重復性代碼(性能監視、事務管理、安全檢查、緩存)

  在程序運行期間,不修改源碼對已有方法進行增強

  將業務邏輯和系統處理的代碼(關閉連接、事務管理、操作日志記錄)解耦

(2)優勢:減少重復代碼、提高開發效率、維護方便

3、AOP相關術語介紹:

  Joinpoint(連接點)   -- 所謂連接點是指那些被攔截到的點。在spring中這些點指的是方法,因為spring只支持方法類型的連接點

  Pointcut(切入點)        -- 所謂切入點是指我們要對哪些Joinpoint進行攔截的定義

  Advice(通知/增強)    -- 所謂通知是指攔截到Joinpoint之后所要做的事情就是通知。通知分為前置通知、后置通知、異常通知、最終通知、環繞通知(切面要完成的功能)

  Introduction(引介) -- 引介是一種特殊的通知在不修改類代碼的前提下, Introduction可以在運行期為類動態地添加一些方法或Field

  Target(目標對象)     -- 代理的目標對象

  Weaving(織入)      -- 是指把增強應用到目標對象來創建新的代理對象的過程

  Proxy(代理)        -- 一個類被AOP織入增強后,就產生一個結果代理類

  Aspect(切面)           -- 是切入點和通知的結合,以后咱們自己來編寫和配置的

  以上內容摘自Spring AOP文檔!

二、編寫自定義注解

@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD,ElementType.TYPE})
public @interface AnnoationCeshi {
  public String ceshiValue() default "";
}

三、編寫切面

1、編寫切面

@Component @Aspect public class AnnoationCeshiFine {   // @annotation 表示 使用該注解的方法
  @Around(value = "@annotation(AnnoationCeshi)")   public Object aroundMethod(ProceedingJoinPoint jp) throws Throwable { // String module = getModule(jp); // 邏輯代碼 // System.out.printf("**********" + module);
        return jp.proceed();   }
}

  @Component  等價於   @Controller @Service  ...  spring 掃描並被 spring 容器管理

  @Aspect 作用是把當前類標識為一個切面供容器讀取

  AnnoationCeshi 為自己定義的注解

  只要加了  @AnnocationCeshi 注解的 方法 都會執行 上述代碼

2、定義切面

// 定義切點 // @poincut execution 表達式 //exxcution( * com.xy.xywx.controller.CeshiController.*(..)) CeshiController 類中所有方法 public 可省略
@Pointcut("execution(public * com.xy.xywx.controller..*.*(..))") public void poincut(){}

  @poincut 定義切面,里面為 excution 表達式,其中:

(1)excution 表達式中 public 可省略不寫

(2)第一個 * 表示 controller 包中的所有類

(3)第二個 * 表示 類中的所有方法

(4).. 表示 不限參數

@Before(value = "poincut()") //通知前增強
@AfterReturning(value = "poincut()") // 通知后 增強
@AfterThrowing(value = "poincut()") // 異常 增強
@After(value = "poincut()") // final 增強
@Around(value = "poincut()")

3、具體使用

  @Around(value = "poincut()") public Object aroundMethod(ProceedingJoinPoint jp) throws Throwable { Object[] args = jp.getArgs(); Arrays.stream(args).forEach(x -> System.out.println(x)); return jp.proceed(); }

  return   jp.proceed() 重點

四、具體介紹

@Component //加入到spring容器
@Aspect     //切面
public class AspectDemo { // 方法用途(切入點表達式可以用&&,||,!來組合使用)
    @Pointcut("@annotation(com.example.demo.annotation.AnnotationDemo)") public void asAnnotation() {} // 方法用途:在AnnotationDemo注解之前執行,標識一個前置增強方法,相當於BeforeAdvice的功能
    @Before("asAnnotation()") public void beforeRun() { System.out.println("在AnnotationDemo注解之前執行"); } /* 方法用途: * @Around 環繞增強,相當於MethodInterceptor,對帶@AnnotationDemo注解的方法進行切面,並獲取到注解的屬性值 * ProceedingJoinPoint: 環繞通知 */ @Around("asAnnotation() && @annotation(annotationDemo)") public Object around(ProceedingJoinPoint joinPoint,AnnotationDemo annotationDemo) { Object obj = null; try { // AnnotationDemo注解的屬性值
 annotationDemo.remark(); Object[] args = joinPoint.getArgs(); String arg = JSON.toJSONString(args); System.out.println("請求參數:"+ arg); Signature signature = joinPoint.getSignature(); // 方法package路徑
            String methodUrl = signature.getDeclaringTypeName(); // 方法名,不包含package路徑
            String method = signature.getName(); System.out.println("正在執行" + method + "方法,路徑:" + methodUrl); // obj是返回的結果,joinPoint.proceed用於啟動目標方法執行,非常重要
            obj = joinPoint.proceed(args); } catch (Throwable throwable) { throwable.printStackTrace(); } System.out.println("切面執行"); return obj; } /** * 方法用途: 在AnnotationDemo注解之后執行 * final增強,不管是拋出異常或者正常退出都會執行。 * @AfterReturning: 后置增強,似於AfterReturningAdvice, 方法正常退出時執行 * @AfterThrowing: 異常拋出增強,相當於ThrowsAdvice */ @AfterReturning(returning = "obj", Pointcut = "asAnnotation()") public void after(Object obj) { System.out.println("在AnnotationDemo注解之后執行"); logger.info("執行成功,Result:{}", JSON.toJsonString(obj)); } }

五、具體應用

1、比如我們限制下重復點擊

(1)先定義注解

(2)再寫注解的 AOP

@Slf4j @Aspect @Component public class NoRepeatSubmitAop { // @Autowired 可引入一些類
    @Around("execution(* com.enmox.emcs.*.controller.*Controller.*(..)) && @annotation(noRepeatSubmit)") public Object doAround(ProceedingJoinPoint pjp, NoRepeatSubmit noRepeatSubmit) { try { // 業務處理:通過注解的 time()拿到默認設置的值,然后進行一些業務處理即可
       int time = noRepeatSubmit.time() return pjp.proceed(); } catch (EmcsCustomException e) { throw new EmcsCustomException(e.getMessage()); } catch (Throwable e) { log.error("校驗表單重復提交時異常: {}", LogUtil.getStack(e)); throw new EmcsCustomException("校驗表單重復提交時異常"); } } }

2、比如我們記錄操作日志

/** * 記錄操作日志 */ @Aspect @Component @Slf4j public class RecordOperLogAspect { @Pointcut("@annotation(com.baj.iam.authn.aop.UserOperLog)") public void userLogpointcut() {} @Pointcut("@annotation(com.baj.iam.authn.aop.AdminOperLog)") public void adminLogpointcut() {} @Pointcut("@annotation(com.baj.iam.authn.aop.AppOperLog)") public void appLogpointcut() {} /** * 用戶日志 * @param userOperLog */ @After("userLogpointcut() && @annotation(userOperLog)") public void recordUserOperLog(UserOperLog userOperLog) { // 業務處理
 } @Before("userLogpointcut() && @annotation(userOperLog)") public void recordUpdatePasswordLog(JoinPoint point, UserOperLog userOperLog) { try { // 單獨的before切面記錄修改密碼,因為after拿不到用戶信息
        } catch (Throwable e) { log.error("記錄操作日志異常: {}", e.getMessage(), e); } } /** * 應用操作日志 * @param appOperLog */ @Around("appLogpointcut() && @annotation(appOperLog)") public Object recordAppOperLog(ProceedingJoinPoint point, AppOperLog appOperLog) throws Throwable { // 業務處理
 } }

 


免責聲明!

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



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