Spring AOP: логирование и вложенные методы - PullRequest
6 голосов
/ 25 апреля 2011

Я написал простое приложение Spring2.5 для демонстрации / тестирования AOP;в частности, я хочу регистрировать вход и выход каждого метода каждого класса в определенном пакете.Это то, что у меня есть ...

(примечание: я использую контроллеры аннотаций; я опускаю детали, не связанные напрямую с aop, потому что моя базовая настройка работает нормально - я включаю только детали, связанные с aop- дайте мне знать, если вам нужно больше)


applicationContext.xml:

(...)
<bean id="loggerInterceptor" class="aspect.LoggerInterceptor" />
(...)

dispatcher-servlet.xml:

(...)
<aop:aspectj-autoproxy proxy-target-class="true" />
(...)

HomeController.java:

public class HomeController() {

    public HomeController() { }

    public ModelAndView get() {
        System.out.println("In HomeController#get()...");

        this.somePrivateMethod();
        this.somePublicMethod();

        return new ModelAndView( "home" );
    }

    private void somePrivateMethod() {
        System.out.println("In HomeController#somePrivateMethod()...");
    }

    public void somePublicMethod() {
        System.out.println("In HomeController#somePublicMethod()...");
    }
}

LoggerInterceptor.java:

public class LoggerInterceptor {

    @Pointcut("execution(* controller.*.*(..))")
    private void anyOperationInControllerPackage() {
        /* nothing to do here;
         * this just defines that we want to catch all methods
         * in the controller-package
         */
    }

    @Around("anyOperationInControllerPackage()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {

        System.out.println("Entering " + joinPoint.getSignature().getDeclaringTypeName() + "#" + joinPoint.getSignature().getName() + "() using arguments: " + Arrays.toString( joinPoint.getArgs() ) );

        try {

            Object result = joinPoint.proceed();

            System.out.println("Leaving " + joinPoint.getSignature().getDeclaringTypeName() + "#" + joinPoint.getSignature().getName() + "()." );

            return result;

        } catch (Throwable ex) {

            ex.printStackTrace();
            throw ex;

        }

    }

}

Вот что я получаю при вызове HomeController # get ():

Entering controller.HomeController#get() using arguments: []
In HomeController#get()...
In HomeController#somePrivateMethod()...
In HomeController#somePublicMethod()...
Leaving controller.HomeController#get().

Как видите, единственный перехваченный метод - это HomeController # get ().Когда #get () вызывает #somePrivateMethod () или #somePublicMethod (), перехватчик не перехватывает их.Я ожидаю, по крайней мере, что #somePublicMethod () также будет пойман (и так как я использую cglib, я также ожидал бы, что #somePrivateMethod () будет пойман).

Так что я предполагаюМой вопрос заключается в том, что мне нужно изменить / добавить, чтобы позволить (по крайней мере) всем открытым методам в пакете контроллера быть пойманными, даже когда другой метод в этом пакете вызывал их и сам был пойман первым ???

Надеюсь, это имеет смысл.: D


РЕДАКТИРОВАТЬ (25APR2011 @ 13:13)

applicationContext.xml:

(...)
<context:load-time-weaver />  <!-- added -->
<bean id="loggerInterceptor"... />
(...)

aop.xml:

<!DOCTYPE aspectj PUBLIC
    "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
    <weaver>
        <!-- only weave classes in this package -->
        <include within="controller.*" />
    </weaver>
    <aspects>
        <!-- use only this aspect for weaving -->
        <aspect name="aspect.LoggerInterceptor" />
    </aspects>
</aspectj>

В «Свойствах проекта» Netbean на вкладке «Выполнить» Iдобавил эту строку в «Параметры VM» :

-javaagent:C:\Users\bgresham\Documents\libraries\spring-framework-2.5\dist\weaving\spring-agent.jar

Как и раньше, я не получаю никаких ошибок - я просто не получаю «вложенный»-logging я ищу.

???

Ответы [ 2 ]

5 голосов
/ 26 апреля 2011

Если вы используете Spring AOP, вы должны вызывать только метод, к которому был применен аспект через ссылку, возвращаемую Spring, , а не - this, и Я не думаю, что вы можете применить pointcut к приватным методам (может быть неправильно в последней части). Это потому, что Spring AOP применяет pointcuts через прокси-объект, а не путем переписывания классов (что делает AspectJ). Преимущество этого серьезного ограничения состоит в том, что намного проще заставить его работать в контейнерах (из опыта я знаю, что Spring AOP прекрасно работает внутри Tomcat), потому что нет никаких разногласий по поводу того, какие биты куда подключены.

Лучший способ сделать это - разделить определение класса, чтобы вы никогда не вызывали метод с помощью this, но если это невозможно, вы всегда можете попробовать дать бину производную от Spring ссылку на себя:

private HomeController self;
@Required
public void setSelf(HomeController self) { this.self = self; }

public ModelAndView get() {
    System.out.println("In HomeController#get()...");

    self.somePrivateMethod();
    self.somePublicMethod();

    return new ModelAndView( "home" );
}

(Это довольно аккуратно; self - это ключевое слово во многих языках , но не Java , поэтому относительно легко запомнить, для чего вы его используете.)

3 голосов
/ 25 апреля 2011

Вы используете Spring AOP для поддержки аспекта. Spring aop будет работать только на весенних бобах. Таким образом, pointcut не работает на фактическом экземпляре класса, то есть, когда контроллер вызывает любой из своих открытых или закрытых методов. Для того, чтобы зарегистрировать все методы в контроллере, вам нужно использовать AspectJ для поддержки aop, включив либо время загрузки, либо время компиляции всех классов, которые вы хотите перехватить. Edit:

Для плетения времени загрузки вам понадобится:

aop.xml

<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">    
<aspectj>    
    <weaver options="-Xset:weaveJavaxPackages=true -verbose -showWeaveInfo -debug">    
        <include within="*"/>
    </weaver>
    <aspects>
        <!-- weave in just this aspect -->
        <aspect name="your.logger.impl.LoggingImpl"/>
    </aspects>
  </aspectj>

Это подразумевает переплетение всех ваших файлов («в пределах = *», измените, как вы хотите) с указанным аспектом / ами. Во время загрузки вы должны увидеть подробную информацию о плетении классов.

Конфигурации в пружинных конфигурациях:

<context:load-time-weaver aspectj-weaving="autodetect" 

            weaver-class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>

Обратите внимание, что класс ткачества должен находиться в пути к библиотеке сервера, а НЕ в пути к вашему приложению.

Вышеуказанные конфигурации должны делать то, что вы ожидаете.

...