Пользовательская аннотация как перехватчик для ведения журнала метода

Java-гуру,

Я довольно новый для annotations и не искал этого много, поэтому, пожалуйста, несите меня...

Я хотел бы реализовать Custom Annotation, который будет intercept вызовом метода; чтобы начать с чего-то очень простого, он может просто распечатать имя и параметры методов, чтобы я мог избежать инструкции logger.

Пример вызова:

public MyAppObject findMyAppObjectById(Long id) throws MyCustomException {
    log.debug("in findMyAppObjectById(" + id + ")");
    //....
}   

можно преобразовать в:

@LogMethodCall(Logger.DEBUG)
public MyAppObject findMyAppObjectById(Long id) throws MyCustomException {
    //....
}   

Могу ли я получить некоторые подсказки об этом?

Ответы

Ответ 1

Основываясь на ваших ответах на мои комментарии, вы не сможете сделать это только с помощью аннотаций. Вы можете, конечно, создать свои аннотации и создать некоторый отражающий код, который будет обнаружен тогда и выполнить некоторый код, но это не изменит ваш код слишком сильно, потому что вам нужно будет вызвать метод parser, прежде чем вы вызовете свои методы и Я думаю, это не поможет вам слишком много, так как вам нужно будет вызвать метод парсера перед каждым вызовом.

Если вам нужно поведение, о котором вы говорили (автоматический вызов), вам нужно будет объединить ваши аннотации с некоторыми рамками AOP, такими как Spring (простая Java) или AspectJ (код AspectJ). С этого момента вы можете установить точки и каждый раз, когда эта точка будет достигнута, некоторый код может быть выполнен. Затем вы можете настроить код до и/или после выполнения метода.

Если первого сценария достаточно, вы можете сделать что-то вроде:

Logger: enum

public enum Logger {
    INFO,
    DEBUG;
}

LogMethodCall: аннотация

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

@Retention( RetentionPolicy.RUNTIME ) // the annotation will be available during runtime
@Target( ElementType.METHOD )         // this can just used in methods
public @interface LogMethodCall {

    Logger logLevel() default Logger.INFO;

}

Лицо: аннотированный класс

public class Person {

    // will use the default log level (INFO)
    @LogMethodCall
    public void foo( int a ) {
        System.out.println( "foo! " + a );
    }

    @LogMethodCall( logLevel = Logger.DEBUG )
    public void bar( int b ) {
        System.out.println( "bar! " + b );
    }

}

Утилиты: класс с статическим методом журнала (это будет выполнять "синтаксический анализ" )

public class Utils {

    public static void log( Object o, String methodName ) {

        // gets the object class
        Class klass = o.getClass();

        // iterate over its methods
        for ( Method m : klass.getMethods() ) {

            // verify if the method is the wanted one
            if ( m.getName().equals( methodName ) ) {

                // yes, it is
                // so, iterate over its annotations
                for ( Annotation a : m.getAnnotations() ) {

                    // verify if it is a LogMethodCall annotation
                    if ( a instanceof LogMethodCall ) {

                        // yes, it is
                        // so, cast it
                        LogMethodCall lmc = ( LogMethodCall ) a;

                        // verify the log level
                        switch ( lmc.logLevel() ) {
                            case INFO:
                                System.out.println( "performing info log for \"" + m.getName() + "\" method" );
                                break;
                            case DEBUG:
                                System.out.println( "performing debug log for \"" + m.getName() + "\" method" );
                                break;
                        }

                    }
                }

                // method encountered, so the loop can be break
                break;

            }

        }

    }

}

AnnotationProcessing: класс с кодом для проверки обработки аннотаций

public class AnnotationProcessing {

    public static void main(String[] args) {

        Person p = new Person();
        Utils.log( p, "foo" );
        p.foo( 2 );
        Utils.log( p, "bar" );
        p.bar( 3 );

    }
}

Конечно, вам нужно будет улучшить свой код в соответствии с вашими потребностями. Это просто начальная точка.

Подробнее об аннотациях:

Подробнее о AOP:

Ответ 2

Используйте Spring AOP вместе с аннотацией Java. Spring AOP отменяет требование для записи класса util для анализа классов Java с использованием Java Reflection.

Пример -

  • Пользовательская аннотация -

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface A {            
         boolean startA() default false;
    
         boolean endA() default false;
    }
    
  • Аспект-

     @Aspect
     public class AAspect {
         @Pointcut(value = "execution(* *.*(..))")
         public void allMethods() {
                 LOGGER.debug("Inside all methods");
         }
    
        @Before("allMethods() && @annotation(A)")`
        public void startAProcess(JoinPoint pjp, A a) throws Throwable {
             if (a.startA()) {
                   //Do something
        }
    }
    
  • Включить AspectJ -

    @Configuration
    @EnableAspectJAutoProxy
    public class AConfig {
    
    }
    
  • Использование в коде -

    @A(startA = true, endA = true)
    public void setUp(){
          //Do something- logic
    }
    

Ответ 3

Как уже было сказано, AOP и аннотации - лучший вариант. Я бы рекомендовал использовать готовый механизм из jcabi-aspects (я разработчик):

@Loggable(Loggable.DEBUG)
public String load(URL url) {
  return url.openConnection().getContent();
}

Все вызовы методов будут регистрироваться на SLF4J.