java自定義註解學習(三)_註解解析及應用

養碼青年發表於2018-12-24

上篇文章已經介紹了註解的基本構成資訊。這篇文章,主要介紹註解的解析。畢竟你只宣告瞭註解,是沒有用的。需要進行解析。主要就是利用反射機制在執行時進行檢視和利用這些資訊

常用方法彙總

在Class、Field、Method、Constructor中都有如下方法:

//獲取所有的註解
public Annotation[] getAnnotations()
//獲取所有本元素上直接宣告的註解,忽略inherited來的
public Annotation[] getDeclaredAnnotations()
//獲取指定型別的註解,沒有返回null
public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
//判斷是否有指定型別的註解
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)

Annotation 是一個藉口,它表示註解,原始碼為:

public interface Annotation {
    boolean equals(Object obj);
    int hashCode();
    String toString();
    //返回真正的註解型別
    Class<? extends Annotation> annotationType();
}

實際上,所有的註解型別、內部實現時,都是擴充套件的Annotation

對於Method和Contructor,他們都有方法引數

public Annotation[][] getParameterAnnotations()

應用註解

日常工作中,每個公司都會自定義註解進行記錄日誌的,我們就做一個簡單的記錄日誌操作的註解,結合aop和springboot

1.建立springboot專案

這裡不再贅述,主要需要引入aop

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

2.定義自定義註解

package com.kevin.anno.annotation;

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

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

3.定義aspect及解析註解

package com.kevin.anno.aspect;

import com.kevin.anno.annotation.KevinLog;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

@Aspect
@Component
public class LogAscpect {

    private final static Logger logger = LoggerFactory.getLogger(LogAscpect.class);

    @Pointcut("@annotation(com.kevin.anno.annotation.KevinLog)")
    public void log() {
    }

    @Around("log()")
    public Object aroundAdvice(ProceedingJoinPoint point) throws Throwable {
        Object object = null;
        long start = System.currentTimeMillis();

        Method method = ((MethodSignature) MethodSignature.class.cast(point.getSignature())).getMethod();
        KevinLog kevinLog = method.getAnnotation(KevinLog.class);

        String operationName = kevinLog.value();
        object = point.proceed(point.getArgs());
        long end = System.currentTimeMillis();
        Long excuteTime = end - start;

        print(operationName, excuteTime, point);
        return object;
    }

    private void print(String operationName, Long excuteTime, ProceedingJoinPoint point) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        //operationName
        logger.info("operationName={}", operationName);
        //time
        logger.info("time={}", excuteTime);
        // url
        logger.info("url={}", request.getRequestURL());
        //method
        logger.info("method = {}", request.getMethod());
        //ip
        logger.info("ip = {}", request.getRemoteAddr());
        //類方法
        logger.info("class_method={}", point.getSignature().getDeclaringTypeName() + "." + point.getSignature().getName());
        //引數
        logger.info("args = {}", point.getArgs());
    }


}

4. 在請求方法上加上自定義註解

package com.kevin.anno.controller;

import com.kevin.anno.annotation.KevinLog;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @RequestMapping(value = "/hello")
    @KevinLog("kevin test !")
    public String hello() {
        return "hello kevin";
    }

}

5.啟動測試

訪問:http://localhost:8080/hello

頁面出現:hello kevin

控制檯列印資訊如下:

2018-10-22 10:38:22.456  INFO 3916 --- [nio-8080-exec-2] com.kevin.anno         : operationName=kevin test !
2018-10-22 10:38:22.456  INFO 3916 --- [nio-8080-exec-2] com.kevin.anno.aspect.LogAscpect         : time=7
2018-10-22 10:38:22.456  INFO 3916 --- [nio-8080-exec-2] com.kevin.anno.aspect.LogAscpect         : url=http://localhost:8080/hello
2018-10-22 10:38:22.456  INFO 3916 --- [nio-8080-exec-2] com.kevin.anno.aspect.LogAscpect         : method = GET
2018-10-22 10:38:22.457  INFO 3916 --- [nio-8080-exec-2] com.kevin.anno.aspect.LogAscpect         : ip = 0:0:0:0:0:0:0:1
2018-10-22 10:38:22.457  INFO 3916 --- [nio-8080-exec-2] com.kevin.anno.aspect.LogAscpect         : class_method=com.kevin.anno.controller.HelloController.hello
2018-10-22 10:38:22.457  INFO 3916 --- [nio-8080-exec-2] com.kevin.anno.aspect.LogAscpect         : args = {}

總結

其實, 大家可以自己寫這玩玩,比較這個demo還用到了aop,工作中很少接觸到aop。以至於面試的時候,問你aop的時候,自己都沒有實際的應用過。

好了。玩的開心!


相關文章