Почему Spring AOP не плетет внешние банки во время выполнения? - PullRequest
7 голосов
/ 11 мая 2011

У меня есть Java-приложение, построенное на Spring 3. У этого проекта есть еще один jar в качестве зависимости.

Эта зависимость содержит класс @org.aspectj.lang.annotation.Aspect (скажем, com.aspectprovider.aspects.MyAspect). Есть @Before совет по созданию метода из классов, который реализует интерфейс Foo. Что-то вроде:

@Before("execution(* com.project.Foo.save(..))")

Интерфейс Foo может находиться внутри "проекта" или в другом банке. Это не имеет значения для этого примера.

Мой проект содержит классы, которые реализуют Foo. Конечно, я хочу, чтобы это были классы.

Файл конфигурации контекста приложения My Spring (applicationContext.xml) содержит строку:

<aop:aspectj-autoproxy />

Я также объявляю аспект как бин и добавляю некоторые свойства:

<bean id="myAspect" class="com.aspectprovider.aspects.MyAspect"
  factory-method="aspectOf" >
  <property name="someproperty" value="somevalue" />
</bean>

Журналирование через якорь Я вижу, что MyAspect создается, а свойства вводятся. Но метод сохранения не перехватывается. Это проблема.

Если я скопирую классы аспектов из jar-файла в приложение с Spring, это сработает. Когда эти аспекты содержатся во внешних банках, метод save не перехватывается. Любые подсказки?

edit: как я вызываю метод сохранения Foo:

//in a JSF managed bean
@Inject
private Foo myFoo;  //there's a implementation of Foo in a package that spring is looking at. So it is injected correctly.

public String someAction() {
    myFoo.save("something"); //the @Before advice is only called if the class containing the aspect is not in an external jar
}


//in a class with a main method
void main(String[] ars) {
    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    //right after the previous line, I can see in the log that MyAspect is instantiated.
    Foo myFoo = ac.getBean(Foo.class);
    myFoo.save("something"); //the @Before advice is only called if the class containing the aspect is not in an external jar
}

В основном мой applicationContext.xml имеет следующие строки:

<context:annotation-config />
<context:component-scan base-package="com.project" />
<context:component-scan base-package="com.aspectprovider.aspects" />
<aop:aspectj-autoproxy />
<bean id="myAspect" class="com.aspectprovider.aspects.MyAspect" factory-method="aspectOf" >
    <property name="someproperty" value="somevalue" />
</bean>

Не думаю, что нужно ставить что-то вроде

<context:component-scan  base-package="com.project">
    <context:include-filter type="aspectj" expression="com.aspectprovider.aspects.*" />
</context:component-scan>

Ответы [ 6 ]

6 голосов
/ 07 июля 2011

У меня такая же проблема.Я решил эту проблему с упаковкой Maven.Отметьте aspectj-maven-plugin и опцию weaveDependency

http://mojo.codehaus.org/aspectj-maven-plugin/weaveJars.html

3 голосов
/ 11 мая 2011

Учитывая, что он отлично работает, когда классы упакованы вместе с приложением и весной, я могу только думать, что это будет проблема загрузки классов.

Если он работает нормально, когда ваше приложение включено в приложение, тогда, когда AOP сканирует все классы, которые он должен будет отслеживать, он ссылается на правильный загрузчик классов со всеми правильными банками.Но теперь, когда вы удаляете его и устанавливаете его в JAR-файл, он сканирует под загрузчиком классов все другие сторонние jar-файлы.

Я не уверен на 100%, как это отображается, но это может быть что-то вроде этого:

Bootstrap Classloader <- Third Party Classloader  <- Application Class Loader (with all your classes)
                              \                         \
                               aspectj.jar               spring.jar

Если его aspect.jar сканирует только под своим загрузчиком классов, он не будетвозможность видеть «все ваши классы».Один из способов подтвердить это - получить дамп кучи вашего приложения.Запустите его на Eclipse MAT, посмотрите проводник Class Loader и найдите классы аспектов.Если они не находятся в том же загрузчике классов, что и ваше приложение, вам придется искать способ заставить tomcat сообщать сторонним библиотекам о классах приложения.

2 голосов
/ 11 мая 2011

Вы можете попробовать аспектJ LTW вместо прокси Spring AOP.Для этого добавьте aop.xml в ваш META-INF

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

И это пружинная часть конфигурации:

Подробнее см. Здесь: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-aj-ltw

0 голосов
/ 02 марта 2012

взгляните на ApectWerks, он ткает время загрузки: http://aspectwerkz.codehaus.org/weaving.html

0 голосов
/ 04 июня 2011

Я закончил тем, что объявил аспекты в весеннем конфиге applicationContext xml и удалил аннотации.

До сих пор работал плагин aspectj для maven, но каждый раз я менял класс в eclipse,Мне пришлось запустить $ mvn compile (потому что Eclipse не знает аспектов и собирал классы без них), и это ужасно сказать любому, кто будет использовать MyAspect.

Тогда ятолько что создал файл конфигурации и задокументировал: чтобы использовать MyAspect, просто импортируйте эти правила конфигурации в конфигурацию контекста вашей пружины.

0 голосов
/ 11 мая 2011

Из AspectJ в книге действий: аспекты, используемые с AOP на основе прокси (объявлены с использованием синтаксиса @AspectJ или XML), являются бинами Spring и не должны использовать подход aspectOf () для создания экземпляров.Объявите это нормально и посмотрите, получится ли:

<bean id="myAspect" class="com.project.MyAspect">
  <property name="someproperty" value="somevalue" />
</bean>
...