开发者

Why Spring AOP is not weaving external jars at runtime?

开发者 https://www.devze.com 2023-03-05 10:20 出处:网络
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

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) contai开发者_JAVA技巧ns 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>


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


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.


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


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>


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.


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

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号