Search code examples
spring-bootaopaspectjspring-aop

Inject A Spring bean into AspectJ with additional pointcuts


I have a spring boot project which already uses Spring AOP. For a new feature there is requirement to use cflow pointcut, hence AspectJ has to be integrated.

I am successfully able to Compile Time Weave aspect into my project and can see the aspect running. I know loadtime weaving should be used when the project has lombok but i manage to run it using the following blog.

Here is the plugin in pom.xml for compile time weaving.

           <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.14.0</version>
                <configuration>
                    <complianceLevel>11</complianceLevel>
                    <source>11</source>
                    <target>11</target>
                    <showWeaveInfo>true</showWeaveInfo>
                    <verbose>true</verbose>
                    <Xlint>ignore</Xlint>
                    <encoding>UTF-8</encoding>
                </configuration>
                <executions>
                    <execution>
                        <id>default-compile</id>
                        <phase>process-classes</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                        <configuration>
                            <sources/>
                            <excludes>
                                <exclude>**/*.java</exclude>
                            </excludes>
                            <forceAjcCompile>true</forceAjcCompile>
                            <weaveDirectories>
                                <weaveDirectory>${project.build.directory}/classes</weaveDirectory>
                            </weaveDirectories>
                        </configuration>
                    </execution>
                    <execution>
                        <id>default-testCompile</id>
                        <phase>process-test-classes</phase>
                        <goals>
                            <goal>test-compile</goal>
                        </goals>
                        <configuration>
                            <sources/>
                            <excludes>
                                <exclude>**/*.java</exclude>
                            </excludes>
                            <forceAjcCompile>true</forceAjcCompile>
                            <weaveDirectories>
                                <weaveDirectory>${project.build.directory}/test-classes</weaveDirectory>
                            </weaveDirectories>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

The role of the aspect is to query a database with intercepted jointpoint. For this I am trying to inject the Spring bean annotated with @Repository into the aspect. As aspect is not a spring component I can not @Autowire repository in aspect class.

Repository Class

@Repository
public interface SampleRepository extends JpaRepository<Sample, String> {
}

Aspect Class

@Aspect
@Slf4j
public class SampleAspect {
}

Code already tried is

  1. Making the aspect a spring component using @Configurable annotation over Aspect . Not working.
@Aspect
@Configurable
@Slf4j
public class SampleAspect {
}
  1. Creating a bean of Aspect using aspectOf function and setting the repository bean in it. Not working.
    @Bean
    public SampleAspect sampleAspect(){
        SampleAspect sampleAspect = Aspects.aspectOf(SampleAspect.class);
        sampleAspect.setRepository(sampleRepository);
        return sampleAspect;
    }

In both of the above steps I am getting the error unsupported jointpoint cflow.

Initialization of bean failed; nested exception is org.aspectj.weaver.tools.UnsupportedPointcutPrimitiveException: contains unsupported pointcut primitive 'cflow'

Am I missing something here ? I need to inject a bean into plain old aspectj.


Solution

  • Making the POJO class you want to @Autowire a dependency into @Configurable is correct, also if that class happens to be a native AspectJ aspect. Making the native aspect a @Bean or a @Component is, however, a wrong and counter-productive approach, because that registers the aspect a second time as a Spring AOP aspect, which also explains the UnsupportedPointcutPrimitiveException ... cflow problem.

    Missing in this picture is the information that for @Configurable, to work, you need AnnotationBeanConfigurerAspect from spring-aspects on the classpath. This is true both for the load-time and compile-time weaving scenarios. I.e., you want something like this in your POM:

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>${spring.version}</version>
    </dependency>
    

    In the AspectJ Maven Plugin configuration, you then also need to declare spring-aspects as an aspect library:

    <aspectLibraries>
      <aspectLibrary>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
      </aspectLibrary>
    </aspectLibraries>
    

    For more details, see this related answer.

    Slightly off topic: I find the approach to use Maven Compiler and AspectJ Maven within a single module in subsequent phases, working on the same output directory, questionable, even though in your case it seems to work. I recommend to build an unwoven module version with Maven Compiler and Lombok in module A and then in module B weave aspects into the classes from A. All other modules should depend on B and not the intermediary A. In order to avoid A to be a transitive dependency, having the same classes in two different versions on the classpath, probably it makes sense to give A provided scope when declaring it a dependency of B.