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

时间:2011-05-10 21:07:59

标签: java spring aspectj spring-aop

我在Spring 3上构建了一个java应用程序。这个项目有另一个jar作为依赖项。

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

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

Foo接口可以位于“项目”内或另一个jar中。这个例子无关紧要。

我的项目包含实现Foo的类。当然,那些是我希望它编织的类。

My 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并注入了属性。但是方法保存没有被截获。这就是问题所在。

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

编辑:我如何调用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 个答案:

答案 0 :(得分:6)

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

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

答案 1 :(得分:3)

考虑到当应用程序和spring打包类时,它的工作完全正常,我只能认为这是一个类加载问题。

如果你的应用程序捆绑在一起工作正常,那么当AOP扫描它必须监视的所有类时,它就会引用带有所有正确jar的正确的类加载器。但现在当你删除它并将其设置为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 :(得分:2)

您可以尝试使用aspectJ LTW而不是Spring AOP代理。为此,请向META-INF

添加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="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

答案 3 :(得分:0)

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

正常声明并查看它是否成功:

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

答案 4 :(得分:0)

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

到目前为止工作的是为maven使用aspectj插件,但每次我在eclipse中更改一个类时,我必须运行$ mvn compile(因为eclipse不知道方面,并且正在编译类而没有对于任何会使用MyAspect的人来说,这是一件非常糟糕的事情。

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

答案 5 :(得分:0)

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