为什么 Spring AOP 不在运行时编织外部 jar?
我有一个基于 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我有同样的问题。我用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 optionweaveDependency
http://mojo.codehaus.org/aspectj-maven-plugin/weaveJars.html
考虑到当类与应用程序和 spring 一起打包时它工作得很好,我只能认为这将是一个类加载问题。
如果它在您的应用程序中捆绑时工作正常,那么当 AOP 扫描它必须监视的所有类时,它就会引用带有所有正确 jar 的正确类加载器。但现在,当您删除它并将其设置到 JAR 中时,它会在类加载器下与所有其他第三方 jar 一起进行扫描。
我不是 100% 确定它是如何映射的,但它可能是这样的:
如果它的方面.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:
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.
您可以尝试aspectJ LTW而不是Spring AOP代理。为此,将 aop.xml 添加到您的 META-INF
这是配置的 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
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
来自《行动手册》中的 AspectJ:
与基于代理的 AOP 一起使用的方面(使用 @AspectJ 声明
或基于 XML 的语法)是 Spring bean,不应使用aspectOf() 方法
到实例化。
正常声明并查看是否有效:
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:
我最终在 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 useMyAspect
.Then I just created a config file and documented: to use
MyAspect
, just import this config rules to your spring's context configuration.看看 ApectWerks,它进行加载时编织:
http://aspectwerkz.codehaus.org/weaving.html
take a look at ApectWerks, it does load-time weaving:
http://aspectwerkz.codehaus.org/weaving.html