Why doesn't AspectJ compile-time weaving of Sp

2019-01-10 13:06发布

问题:

Update 5: I've downloaded the latest Spring ToolsSuite IDE based on the latest Eclipse. When I import my project as a Maven project, Eclipse/STS appears to use the Maven goals for building my project. This means AspectJ finally works correctly in Eclipse.

Update 4: I have ended up just using Maven + AspectJ plugin for compile-time weaving, effectively bypassing Eclipse's mechanism.

Update 3: It seems AspectJ's Eclipse plug-in breaks Eclipse's ability to correctly Publish to Tomcat. Only by removing the AspectJ capability on a project can I get it to properly Publish again. Very annoying.

Update 2: I have this now working in Eclipse. It makes me very uncomfortable to say this, but I have no idea how I got it working from either Eclipse or Maven builds. It appears to be a compile issue rather than a run-time issue.

Update 1: It appears I've gotten this to work via Maven builds, but I have no idea how. Eclipse still doesn't work. The only thing I changed in the pom.xml was adding these (insignificant?) configuration parameters:

<source>1.6</source>
<complianceLevel>1.6</complianceLevel>
<verbose>true</verbose>
<showWeaveInfo>true</showWeaveInfo>
<outxml>true</outxml>

I'm actually worried that I have a repeat of this problem, where everything works inconsistently. I will keep this question updated as I learn more.

With regards to Eclipse, I made some progress by taking the binary aspects I wish to weave - in this case spring-aspects.jar - and copying it out of my classpath. I then add this now external jar to my Aspect Path. After doing this, Eclipse properly shows me AspectJ markers in my code. It's annoying that I can't just leave spring-aspects.jar in my Java Build Path which is maintained by Maven for me via the Maven plug-in. For some reason, however, the AspectJ plug-in doesn't see the binary aspects unless they're explicitly added to the Aspect Path.


Original Post: @Configurable is a Spring annotation that allows dependencies to be injected into objects instantiated external to Spring (for example, by Hibernate or some Factory class).

I was using this annotation previously with load-time weaving and it mostly worked. Occasionally I would boot up and nothing would get injected. This issue spawned this StackOverflow question. There weren't many answers, but most suggested that I try compile-time weaving instead due to greater reliability.

I installed the AspectJ plug-in for Eclipse and Maven. Both of these produce what appears to be properly compiled classes. I've opened up one of the classes in a text editor before AspectJ compilation and found no references to AspectJ. I opened it up after AspectJ compilation and both Eclipse and Maven generated versions have a reference to org.aspectj.weaver.MethodDeclarationLineNumber. This is why I assume it's being properly compiled. The problem is that once deployed, no dependencies get injected.

My Spring applicationContext.xml does include the following:

    <context:spring-configured />

    <context:component-scan base-package="com.myapp" />

Is the above all that's needed for classes marked @Configurable to have DI done? During the conversion from load-time weaving to compile-time weaving, I removed META-INF/aop.xml, <context:load-time-weaver /> from my applicationContext.xml, and Spring's Tomcat weaver from my context.xml.

How can I investigate this problem further? What are possible causes?

回答1:

It works for us on maven using compile time weaving, try adding the following plugins:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
    <compilerVersion>1.6</compilerVersion>
    <fork>true</fork>
    <source>1.6</source>
    <target>1.6</target>
</configuration>
</plugin>

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<executions>
    <execution>
        <id>compile</id>
        <configuration>
            <source>1.6</source>
            <target>1.6</target>
            <verbose>false</verbose>
            <outxml>true</outxml>
            <aspectLibraries>
                <aspectLibrary>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-aspects</artifactId>
                </aspectLibrary>
            </aspectLibraries>
        </configuration>
        <goals>
            <goal>compile</goal>
        </goals>
    </execution>
    <execution>
        <id>test-compile</id>
        <configuration>
            <source>1.6</source>
            <target>1.6</target>
            <verbose>false</verbose>
            <aspectLibraries>
                <aspectLibrary>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-aspects</artifactId>
                </aspectLibrary>
            </aspectLibraries>
        </configuration>
        <goals>
            <goal>test-compile</goal>
        </goals>
    </execution>
</executions>
<dependencies>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.6.4</version>
    </dependency>
</dependencies>
</plugin>

Its done as two separate execution steps to allow you to add different aspect libraries for unit testing and compilation.

You'll also need the following dependency added for the spring-aspects library:

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <scope>compile</scope>
    </dependency>


回答2:

I successfully configured load-time weaving in my app, if this is an alternative for you.

My environment:

  • JDK-1.6
  • Spring-2.5.6
  • JPA with eclipselink-1.1.0

Configuration details:

Spring XML configuration:

<context:annotation-config/>
<context:spring-configured/>
<context:load-time-weaver/>

<bean id="baseEntity" class="package.name.BaseEntity" scope="prototype">
  <property name="historyHandler" ref="historyHandler" />
</bean>

<bean id="historyHandler" class="package.name.HistoryJpaHandler" scope="prototype">
  <property name="historyDao" ref="historyDao" />
</bean>

<bean id="historyDao" class="package.name.HistoryJpaDao">
  <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

Spring annotations

@Configurable("baseEntity")
public abstract class BaseEntity

@Configurable("historyHandler")
public class HistoryJpaHandler extends SessionEventAdapter implements HistoryHandler 

Java VM Parameter

<JAVA_HOME>/bin/java -javaagent:/full/path/to/spring-agent-2.5.6.jar

Instances of historyHandler and baseEntitty are created by ecliselink. historyHandler in baseEntitty and historyDao in historyHandler is set by load-timeweaving.

You can set the VM Parameter in Eclipse run configuration or in Tomcats catalina.sh/bat.



回答3:

making a field of a @configurable class Autowired throws NullPointerException if you do not configure your spring properly for this annotation. follow these steps to make @configurable annotations work properly

This method is called AspectJ build time weaving to inject spring beans to your non-spring-made classes.

First step is to install these plugins in eclipse:

From these two update sites install whatever eclipse suggests:

http://download.eclipse.org/tools/ajdt/43/update
http://dist.springsource.org/release/AJDT/configurator/ 

After installing,right-click on project and Do:

Configure > Convert to Aspectj
Maven > Update

Next, you need to add these to your pom.xml:

Under Dependencies Add:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>4.0.2.RELEASE</version>
</dependency>

Under Plugins Add:

        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>aspectj-maven-plugin</artifactId>
            <version>1.5</version>
            <configuration>
                <showWeaveInfo>true</showWeaveInfo>
                <source>1.7</source>
                <target>1.7</target>
                <Xlint>ignore</Xlint>
                <complianceLevel>1.7</complianceLevel>
                <encoding>UTF-8</encoding>
                <verbose>false</verbose>
                <aspectLibraries>
                    <aspectLibrary>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-aspects</artifactId>
                    </aspectLibrary>
                </aspectLibraries>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>compile</goal>
                        <goal>test-compile</goal>
                    </goals>
                </execution>
            </executions>
            <dependencies>
                <dependency>
                    <groupId>org.aspectj</groupId>
                    <artifactId>aspectjrt</artifactId>
                    <version>1.7.0</version>
                </dependency>
                <dependency>
                    <groupId>org.aspectj</groupId>
                    <artifactId>aspectjtools</artifactId>
                    <version>1.7.0</version>
                </dependency>
            </dependencies>
        </plugin>

Important: DO NOT use any <pluginManagment> tag under <build> tag. your pom.xml needs to be something like this:

<project ....>
    ....
    <dependencies>
        <dependency> 
                    ....
        </dependency>
                ....
    </dependencies>
    <build>
        <plugins>
            <plugin>
                            ....
            </plugin>
                        ....
        </plugins>
    </build>
</project>

finally add <context:spring-configured /> to your spring application context config file.

Now you can annotate a POJO class as @Configurable and inject spring beans in it using @Autowired annotation. this way whenever you make a new instance of that POJO it will be configured (e.g. injected with dependencies) automatically.



回答4:

As far as your Eclipse classpath issues are concerned, you might find this useful.

The m2eclipse plugin has an optional AJDT integration. The integration reads the aspectLibraries section of the aspectj-maven-plugin's configuration, and contributes the jars to Eclipse's Aspect Path.