Как использовать AOP с AspectJ для регистрации?

Я хотел бы добавить сообщения "trace" ко всем моим публичным методам следующим образом:

public void foo (s: String, n: int) {// log - это log4j logger или любая другая библиотека
  log.trace(String.format("Введите foo с s: %s, n: %d", s, n))
  ...
  log.trace("Выход из foo") 
}

Теперь я хотел бы добавить все эти log.trace к моим методам автоматически с AOP (и инструментарий байтового кода). Я думаю о AspectJ, Имеет ли это смысл? Вы знаете какой-нибудь open-source, который делает именно это?

5 ответов

Решение

Я создал простой аспект для захвата выполнения открытых методов. Ядром этого кода AspectJ является определение pointcut:

pointcut publicMethodExecuted(): execution(public * *(..));

Здесь мы собираем все открытые методы с любым типом возврата, в любом пакете и любом классе, с любым количеством параметров.

Выполнение совета может быть визуализировано в фрагменте кода ниже:

after(): publicMethodExecuted() {
    System.out.printf("Enters on method: %s. \n", thisJoinPoint.getSignature());

    Object[] arguments = thisJoinPoint.getArgs();
    for (int i =0; i < arguments.length; i++){
        Object argument = arguments[i];
        if (argument != null){
            System.out.printf("With argument of type %s and value %s. \n", argument.getClass().toString(), argument);
        }
    }

    System.out.printf("Exits method: %s. \n", thisJoinPoint.getSignature());
}

Этот совет использует thisJoinPoint для получения сигнатуры метода и аргументов. И это все. Вот код аспекта:

public aspect LogAspect {

pointcut publicMethodExecuted(): execution(public * *(..));

after(): publicMethodExecuted() {
    System.out.printf("Enters on method: %s. \n", thisJoinPoint.getSignature());

    Object[] arguments = thisJoinPoint.getArgs();
    for (int i =0; i < arguments.length; i++){
        Object argument = arguments[i];
        if (argument != null){
            System.out.printf("With argument of type %s and value %s. \n", argument.getClass().toString(), argument);
        }
    }
    System.out.printf("Exits method: %s. \n", thisJoinPoint.getSignature());
}

Для более сложных примеров я бы рекомендовал книгу AspectJ: In Action.

@Loggable аннотация и аспект AspectJ из jcabi-aspect являются готовым механизмом для вас (я разработчик):

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

Для регистрации входа и выхода в соответствии с требованиями вопроса:

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

Все логи идут на SLF4J. Проверьте этот пост для более подробной информации.

Вы можете использовать различные pointcut, чтобы сделать ваше требование. Эта документация поможет вам.

Прямое решение

Вы можете попробовать этот открытый исходный код http://code.google.com/p/perfspy/. PerfSpy - это средство ведения журналов, мониторинга производительности и проверки кода. Он использует ApsectJ для обхода кода вашего приложения во время выполнения и регистрирует время выполнения каждого метода и его входные параметры и значения. Он имеет приложение пользовательского интерфейса, в котором вы можете просматривать вызовы методов и их входные и возвращаемые значения в виде деревьев. С его помощью вы можете обнаружить узкие места в производительности и понять сложный поток кода.

Вот моя простая реализация для регистрации входов, выходов и регистрации исключений из методов

аннотирование

package test;

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

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface Audit {

}

истребитель-перехватчик

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.logging.Level;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;


@Aspect
public class ExceptionInterceptor {

    private static final java.util.logging.Logger LOGGER = java.util.logging.Logger.getLogger(ExceptionInterceptor.class.getName());

    @Around("execution(* * (..))"
            + " && @annotation(test.Audit)"
    )
    public Object intercept(final ProceedingJoinPoint point) throws Throwable {
        final Method method
                = MethodSignature.class.cast(point.getSignature()).getMethod();
        String mName = method.getName();
        String cName = method.getDeclaringClass().getSimpleName();
        LOGGER.log(Level.INFO, "Entering {0}:{1}", new Object[]{cName, mName});
        Object out = null;
        try {
            out = point.proceed();
        } catch (Throwable t) {
            logExceptions(t, point);
        }
        LOGGER.log(Level.INFO, "Exiting {0}:{1}", new Object[]{cName, mName});
        return out;
    }

    private void logExceptions(Throwable t, final ProceedingJoinPoint point) {
        final Method method
                = MethodSignature.class.cast(point.getSignature()).getMethod();
        String mName = method.getName();
        String cName = method.getDeclaringClass().getSimpleName();
        Object[] params = point.getArgs();
        StringBuilder sb = new StringBuilder();
        sb.append("Exception caught for [");
        sb.append(cName);
        sb.append(".");
        sb.append(mName);
        for (int i = 0; i < params.length; i++) {
            Object param = params[i];

            sb.append("\n");
            sb.append("  [Arg=").append(i);
            if (param != null) {
                String type = param.getClass().getSimpleName();

                sb.append(", ").append(type);

                // Handle Object Array (Policy Override)
                if (param instanceof Object[]) {
                    sb.append("=").append(Arrays.toString((Object[]) param));
                } else {
                    sb.append("=").append(param.toString());
                }
            } else {
                sb.append(", null");
            }
            sb.append("]");
            sb.append("\n");
        }
        LOGGER.log(Level.SEVERE, sb.toString(), t);

    }
}

Как это использовать

@Audit  
public void testMethod(Int a,int b, String c){
}

Maven зависимости Compile

    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.8.7</version>
    </dependency> 

ткачество

        <plugin>
            <groupId>com.jcabi</groupId>
            <artifactId>jcabi-maven-plugin</artifactId>
            <executions>
                <execution>
                    <phase>compile</phase>
                    <goals>
                        <goal>ajc</goal>
                    </goals>
                </execution>
            </executions>
        </plugin> 

Попробуй добавить -javaagent:<path o>aspectjweaver-1.8.7.jar к вашим аргументам в конфигурациях запуска.

Другие вопросы по тегам