自定義注解、AOP實例

目錄
一、學習自定義注解
二、自己編寫一個注解,并在controller上使用這個注解,看一下5種通知類型的調用順序。
三、過濾器、攔截器、AOP的區(qū)別

一、學習自定義注解

自定義注解常用在日志,權限,加密等場景下。
我們使用最常見的@SpringbootApplication,看一下注解的定義中會用到哪些注解。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
  • @Documented 注解
    指明修飾的注解,可以被例如javadoc此類的工具文檔化,只負責標記,沒有成員取值。

  • @Retention 注解

    指明修飾的注解的生存周期,即會保留到哪個階段。
    RetentionPolicy的取值包含以下三種:
    SOURCE:源碼級別保留,編譯后即丟棄
    CLASS:編譯級別保留,編譯后的class文件中存在,在jvm運行時丟棄,這是默認值。
    RUNTIME: 運行級別保留,編譯后的class文件中存在,在jvm運行時保留,可以被反射調用。
    
  • @Target 注解

    指明了修飾的這個注解的使用范圍,即被描述的注解可以用在哪里。
    ElementType的取值包含以下幾種:
    TYPE:類,接口或者枚舉
    FIELD:域,包含枚舉常量
    METHOD:方法
    PARAMETER:參數(shù)
    CONSTRUCTOR:構造方法
    LOCAL_VARIABLE:局部變量
    ANNOTATION_TYPE:注解類型
    PACKAGE:包
    

二、自己編寫一個注解,并在controller上使用這個注解,看一下5種通知類型的調用順序。

MyAnnotation.java(我自定義的一個測試注解)

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public  @interface MyAnnotation {
    String value() default "";
}

MyAnnotationInterceptor.java(自定義測試注解的攔截器)

/**
 * MyAnnotation注解對應的攔截器
 * AspectJ 支持 5 種類型的通知注解:
 *
 * @Before: 前置通知, 在方法執(zhí)行之前執(zhí)行
 * @After: 后置通知, 在方法執(zhí)行之后執(zhí)行
 * @AfterRunning: 返回通知, 在方法返回結果之后執(zhí)行
 * @AfterThrowing: 異常通知, 在方法拋出異常之后
 * @Around: 環(huán)繞通知, 圍繞著方法執(zhí)行
 **/
@Component
@Aspect
public class MyAnnotationInterceptor {
    public static final Logger logger = LoggerFactory.getLogger(MyAnnotationInterceptor.class);

    @Pointcut("@annotation(com.lbc.demo.aop.MyAnnotation)")
    public void functionAspect() {

    }

    @Around(value = "functionAspect()")
    public void doAround(ProceedingJoinPoint pjp) throws Throwable {
        logger.info("around執(zhí)行方法之前");
        Object object = pjp.proceed();
        logger.info("around執(zhí)行方法之后--返回值:" + object);
    }

    @Before("functionAspect()")
    public void doBefore(JoinPoint joinPoint) {
        logger.info("doBefore");
    }

    @After("functionAspect()")
    public void doAfter(JoinPoint joinPoint) {
        logger.info("doAfter");
    }

    @AfterReturning(returning = "returnObj", pointcut = "functionAspect()")
    public void doAfterReturning(JoinPoint joinPoint, Object returnObj) {
        logger.info("AfterReturning");
    }

    @AfterThrowing(value = "functionAspect()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        logger.info("AfterThrowing");
    }
}
執(zhí)行結果:
image.png
直接放結論:
  • 正常情況


    image.png
  • 異常情況


    image.png
@Order注解的作用:
  • @Order(Ordered.HIGHEST_PRECEDENCE)代表這個過濾器在眾多過濾器中級別最高,也就是過濾的時候最先執(zhí)行
  • @Order(Ordered.LOWEST_PRECEDENCE)恰恰相反,表示級別最低,最后執(zhí)行過濾操作
@Inherited注解的作用:
  • 類繼承關系中@Inherited的作用
    類繼承關系中,子類會繼承父類使用的注解中被@Inherited修飾的注解
  • 接口繼承關系中@Inherited的作用
    接口繼承關系中,子接口不會繼承父接口中的任何注解,不管父接口中使用的注解有沒有被@Inherited修飾
  • 類實現(xiàn)接口關系中@Inherited的作用
    類實現(xiàn)接口時不會繼承任何接口中定義的注解

過濾器、攔截器、AOP的區(qū)別

過濾器
過濾器可以攔截到方法的請求和響應(ServletRequest request, SetvletResponse response),
并對請求響應做出響應的過濾操作,比如設置字符編碼、鑒權操作。
攔截器
Spring中攔截器有三個方法:preHandle,postHandle,afterCompletion。
public boolean preHandle(HttpServletRequest httpServletRequest, 
HttpServletResponse httpServletResponse, Object o)
表示被攔截的URL對應的方法執(zhí)行前的自定義處理

public void postHandle(HttpServletRequest httpServletRequest, 
HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView)
表示此時還未將modelAndView進行渲染,被攔截的URL對應的方法執(zhí)行后的自定義處理,。

public void afterCompletion(HttpServletRequest httpServletRequest, 
HttpServletResponse httpServletResponse, Object o, Exception e)
表示此時modelAndView已被渲染,執(zhí)行攔截器的自定義處理。
AOP切片
AOP操作可以對操作進行橫向的攔截,最大的優(yōu)勢在于可以獲取執(zhí)行方法的參數(shù),對方法進行統(tǒng)一的處理,常見使用日志,事務,請求參數(shù)安全驗證等。

順序
請求->>過濾器->>攔截器-->Aspect->>攔截器->>過濾器->>響應

三者使用場景類似
**從過濾器--》攔截器--》切面,攔截規(guī)則越來越細致,執(zhí)行順序依次是過濾器、攔截器、切面。**
一般情況下數(shù)據(jù)被過濾的時機越早對服務的性能影響越小,因此我們在編寫相對比較公用的代碼時,優(yōu)先考慮過濾器,然后是攔截器,最后是aop。
比如權限校驗,一般情況下,所有的請求都需要做登陸校驗,此時就應該使用過濾器在最頂層做校驗;日志記錄,一般日志只會針對部分邏輯做日志記錄,
而且牽扯到業(yè)務邏輯完成前后的日志記錄,因此使用過濾器不能細致地劃分模塊,此時應該考慮攔截器,
然而攔截器也是依據(jù)URL做規(guī)則匹配,因此相對來說不夠細致,因此我們會考慮到使用AOP實現(xiàn),AOP可以針對代碼的方法級別做攔截,很適合日志功能。

參考:http://www.itdecent.cn/p/8cbfff715581
https://zhuanlan.zhihu.com/p/96597358
https://blog.csdn.net/u011277123/article/details/91532149
http://www.itdecent.cn/p/7f54e7250be3
https://www.cnblogs.com/natian-ws/p/10824363.html
https://www.cnblogs.com/quartz/p/12601091.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容