JAvaはコードを優雅にする方法--カスタム注釈

5690 ワード

一、注釈とは何か
JAvaでは、注釈は2つ、メタ注釈とカスタム注釈に分けられます.@Autowired、@Overrideなど、カスタム注釈としてよく使用されます.
二、javaのメタ注釈
注釈を記述する注釈として理解できます.これらのメタ注釈を除いて、すべての注釈はカスタム注釈です.
  • @Document:javaドキュメントに注記情報を追加するかどうかを示す
  • @Target:注釈がどこで使用されるかを示します.
  • ElementType.CONTRUCTOR:コンストラクタを記述するための
  • ElementType.FIELD:メンバー変数、オブジェクト、属性(enumインスタンスを含む)
  • ElementType.LOCAL_VARABLE:局所変数
  • を記述する
  • ElementType.METHOD:記述方法
  • ElementType.PACKAGE:パケットを記述するための
  • ElementType.PARAMETER:パラメータを記述するための
  • ElementType.TYPE:クラス、インタフェース(注釈タイプを含む)またはenum宣言
  • を記述するために使用される
  • @Retention:注釈のライフサイクルを定義します.
  • RetentionPolicy.SOURCE:コンパイル時に破棄されます.バイトコードは書き込みません.@Override,@SuppressWarningsはこのような注釈に属します.
  • RetentionPolicy.CLASS:クラスのロード時に破棄されます.バイトコードファイルの処理に役立ちます.注記デフォルトでは、この方法が使用されます.
  • RetentionPolicy.RUNTIME:常に破棄されず、実行期間もこの注釈を保持するため、反射メカニズムを使用してこの注釈情報を読み取ることができます.

  • @Inherited:コメントとサブクラスの関係を定義
  • は、マークされたタイプが継承されていることを示すマーク注記です.@Inherited修飾を使用したannotationタイプがclassに使用される場合、このannotationはclassのサブクラスに使用されます.


  • 三、カスタム注釈
    カスタム注記作成規則:
  • Annotation型は@interfaceと定義され、すべてのAnnotationはjava.lang.Annotationというインタフェースを自動的に継承し、他のクラスやインタフェースを継承することはできません.
  • パラメータメンバーはpublicまたはデフォルト(default)の2つの反問権修飾
  • のみを使用できます.
  • パラメータメンバーは、8つの基本データ型とString、Enum、Class、annotationsなどのデータ型、およびこれらのタイプの配列しか使用できません.
  • クラスメソッドとフィールドの注釈情報を取得するにはjavaの反射技術によりAnnotationオブジェクトを取得する必要があり、それ以外に注釈オブジェクトを取得する方法はない
  • .
    四、注釈実装:アクセスログを記録する
    4.1 pomファイル
    
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.aspectj
            aspectjweaver
            1.8.9
        
        
            org.apache.commons
            commons-lang3
        
        
            com.google.guava
            guava
            30.1-jre
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
    
    

    4.2注釈をカスタマイズ
    /**
     * @Author: KD
     * @Date: 2021/1/1 1:02   
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface TheLog {
    
        /**
         *       
         *
         * @return
         */
        public String name();
    
        /**
         *        id    
         *
         * @return
         */
        public String idExpression();
    }
    

    このクラスでは、注記の2つのプロパティが定義されています.1つ目はname、2つ目はSpel式です.
    4.3うどん
    /**
     * @Author: KD
     * @Date: 2021/1/1 1:02   
     */
    @Aspect
    @Component
    public class TheAspect {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(TheAspect.class);
    
        @Autowired
        HttpServletRequest request;
    
        @Around("@annotation(com.example.zhujie.anno.TheLog)")//    TheLog         
        public Object log(ProceedingJoinPoint pjp) throws Exception {
    
            Method method = ((MethodSignature)pjp.getSignature()).getMethod();
            TheLog theLog = method.getAnnotation(TheLog.class);
    
            Object response = null;
    
            try {
                //       
                response = pjp.proceed();
            } catch (Throwable throwable) {
                throw new Exception(throwable);
            }
    
            if (StringUtils.isNotEmpty(theLog.idExpression())) {
                SpelExpressionParser parser = new SpelExpressionParser();
                Expression expression = parser.parseExpression(theLog.idExpression());
    
                EvaluationContext context = new StandardEvaluationContext();
                //      
                Object[] args = pjp.getArgs();
    
                //           
                LocalVariableTableParameterNameDiscoverer discoverer
                        = new LocalVariableTableParameterNameDiscoverer();
                String[] parameterNames = discoverer.getParameterNames(method);
    
                //       context 
                if (parameterNames != null) {
                    for (int i = 0; i < parameterNames.length; i++) {
                        context.setVariable(parameterNames[i], args[i]);
                    }
                }
    
                //     resp      context ,                      
                if (response != null) {
                    context.setVariable(
                            CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, response.getClass().getSimpleName()),
                            response);
                }
    
                //      ,    
                String itemId = String.valueOf(expression.getValue(context));
    
                //       
                handle(theLog.name(), itemId);
            }
    
            return response;
        }
    
    
        private void handle(String name, String theId) {
            //         
            LOGGER.info("theType = " + name + ",theId = " + theId);
        }
    }
    

    ここでは主にaspectjのクラスライブラリで反射を実現し,注釈属性を取得し,Spel式を解析し,最後にhandle法でログの印刷出力を実現する.
    4.4注釈の使用
    /**
     * @Author: KD
     * @Date: 2021/1/1 12:41   
     */
    @Controller
    public class TestController {
    
        @GetMapping("test")
        @ResponseBody
        @TheLog(name="test1",idExpression="#id")
        public String test(String id){
            return id;
        }
    
    }
    

    ここでidExpressionはSpel式であり,「#id」は取得方法におけるidという局所変数を表す.
    urlへのアクセス:
    localhost:8080/test?id=123123
    

    ログは次のように印刷されます.
    2021-01-01 13:43:38.157  INFO 52624 --- [nio-8080-exec-1] com.example.zhujie.anno.TheAspect        : theType = test1,theId = 123123