为什么 Spring AOP 不在运行时编织外部 jar?

发布于 2024-11-06 10:58:31 字数 2444 浏览 1 评论 0原文

我有一个基于 Spring 3 构建的 java 应用程序。该项目有另一个 jar 作为依赖项。

此依赖项包含一个 @org.aspectj.lang.annotation.Aspect 类(例如,com.aspectprovider.aspects.MyAspect)。有一个 @Before 建议从实现接口 Foo 的类中编织一个方法。类似于:

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

Foo 接口可以位于“项目”内部或另一个 jar 中。对于这个例子来说并不重要。

我的项目包含实现 Foo 的类。当然,这些是我想要编织的类。

我的 Spring 应用程序上下文配置文件 (applicationContext.xml) 包含以下行:

<aop:aspectj-autoproxy />

我还将切面声明为 bean,并注入一些属性:

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

通过日志记录我可以看到 MyAspect被实例化并注入属性。但是save方法没有被拦截。这就是问题所在。

如果我将方面类从 jar 复制到具有 Spring 的应用程序,它就可以工作。当这些方面包含在外部 jar 中时,方法 save 不会被拦截。有什么线索吗?

编辑:我如何调用 Foo 的 save 方法:

//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>

I have a java application build upon Spring 3. This project has another jar as a dependency.

This dependency contains a @org.aspectj.lang.annotation.Aspect class (lets say, com.aspectprovider.aspects.MyAspect). There's a @Before advice to weave a method from classes that implements the interface Foo. Something like:

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

The Foo interface can be inside the "project" or in another jar. It doesn't matter for this example.

My project contains classes that implements Foo. Those are the classes that I want it to be weaved, of course.

My Spring application context configuration file (applicationContext.xml) contains the line:

<aop:aspectj-autoproxy />

I also declare the aspect as a bean, and inject some properties:

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

Trough logging I can see that MyAspect is instantiated and the properties are injected. But the method save is not intercepted. This is the problem.

If I copy the aspect classes from the jar to the application that has Spring, it works. When those aspects are contained in external jars, the method save is not intercepted. Any clues?

edit: how I am calling Foo's save method:

//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
}

Basically, my applicationContext.xml has the following lines:

<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>

I don't think I need to put anything like

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

蓬勃野心 2024-11-13 10:58:31

我有同样的问题。我用maven打包解决了这个问题。检查 aspectj-maven-plugin 和选项 weaveDependency

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

I have the same problem. I solved this problem packaging with maven. Check the aspectj-maven-plugin and option weaveDependency

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

溺孤伤于心 2024-11-13 10:58:31

考虑到当类与应用程序和 spring 一起打包时它工作得很好,我只能认为这将是一个类加载问题。

如果它在您的应用程序中捆绑时工作正常,那么当 AOP 扫描它必须监视的所有类时,它就会引用带有所有正确 jar 的正确类加载器。但现在,当您删除它并将其设置到 JAR 中时,它会在类加载器下与所有其他第三方 jar 一起进行扫描。

我不是 100% 确定它是如何映射的,但它可能是这样的:

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

如果它的方面.jar 仅在其类加载器下扫描,那么它将无法看到“所有类”。您可以尝试确认这一点的一种方法是获取应用程序的堆转储。针对 Eclipse MAT 运行它,查看类加载器资源管理器并查找方面类。如果它们不与您的应用程序驻留在同一类加载器下,您将不得不寻找一种方法让 tomcat 告诉应用程序类的第三方库。

Considering it works perfectly fine when the classes are packaged with the application and spring I can only think it would be a classloading issue.

If it works fine when your bundled in your app then then when AOP scans all the classes that it will have to monitor then it is referencing the right classloader with all the right jars. But now when you remove it and set it into a JAR it is scanning under the classloader with all of the other third party jars.

I am not 100% sure how it is mapped out but it could be something like this:

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

If its aspect.jar is scanning under only its classloader then it will not be able to see 'all your classes'. One way you can try confirming this is to get a heap dump of your app. Run it against Eclipse MAT, Check out Class Loader explorer and look for the aspect classes. If they do not reside under the same classloader as your application you would have to look at a way to have tomcat tell the third party libraries of the application classes.

彻夜缠绵 2024-11-13 10:58:31

您可以尝试aspectJ 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>

这是配置的 spring 部分:

有关详细信息,请参阅此处: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-aj-ltw

You might try aspectJ LTW instead of Spring AOP proxy. To do this add a aop.xml to your 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>

And this is the spring portion of the config:

See here for details : http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-aj-ltw

美煞众生 2024-11-13 10:58:31

来自《行动手册》中的 AspectJ:
与基于代理的 AOP 一起使用的方面(使用 @AspectJ 声明
或基于 XML 的语法)是 Spring bean,不应使用aspectOf() 方法
到实例化。

正常声明并查看是否有效:

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

From AspectJ in action book:
aspects used with the proxy-based AOP (declared using @AspectJ
or XML-based syntax) are Spring beans and shouldn’t use the aspectOf() approach
to instantiation.

Declare it normally and see if it works out:

<bean id="myAspect" class="com.project.MyAspect">
  <property name="someproperty" value="somevalue" />
</bean>
月朦胧 2024-11-13 10:58:31

我最终在 spring 的 applicationContext xml 配置中声明了各个方面并删除了注释。

到目前为止,工作的是使用maven的aspectj插件,但是每次我在eclipse中更改一个类时,我都必须运行 $ mvncompile (因为eclipse不知道方面,并且正在编译没有它们的类),这对于任何将使用 MyAspect 的人来说都是一件可怕的事情。

然后我刚刚创建了一个配置文件并记录:要使用 MyAspect,只需将此配置规则导入到 spring 的上下文配置中即可。

I've ended up declaring the aspects in the spring's applicationContext xml config and removing the annotations.

What was working so far was using the aspectj plugin for maven, but everytime I changed a class in eclipse, I had to run $ mvn compile (because eclipse doesn't know the aspects, and was compiling the classes without them), and that's an awful thing to say to anybody that will use MyAspect.

Then I just created a config file and documented: to use MyAspect, just import this config rules to your spring's context configuration.

半边脸i 2024-11-13 10:58:31

看看 ApectWerks,它进行加载时编织:
http://aspectwerkz.codehaus.org/weaving.html

take a look at ApectWerks, it does load-time weaving:
http://aspectwerkz.codehaus.org/weaving.html

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文