Inject Spring beans in AspectJ
AspectJ is the most powerful AOP framework in the Java space; Spring is the most powerful enterprise development framework in the Java space. It's not surprise that combining the two should lead to wonderful things...In this article I'm going to show a very simple, yet complete, example where I have injected an AspectJ aspect with a Spring bean.
Setting the Maven project
First, let's have a look at the Maven pom.xml showing all required dependencies and plugins:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>uk.co.jemos.experiments</groupId>
<artifactId>aspectj-test</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>aspectj-test</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>3.0.5.RELEASE</spring.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.3.1</version>
<executions>
<execution>
<id>waves-sources</id>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.1</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>uk.co.jemos.experiments.Main</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>install</phase> <!-- bind to the packaging phase -->
<goals>
<goal>assembly</goal>
</goals>
<configuration>
<descriptors>
<descriptor>src/main/assembly/executable.xml</descriptor>
<descriptor>src/main/assembly/project.xml</descriptor>
</descriptors>
<outputDirectory>${project.build.directory}/executable</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.10</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</project>
The business classes
The example builds on the one shown in the AspectJ in Action book 3rd Edition. It is very simple.
There is the business service which is in charge of delivering messages. The implementation looks like:
package uk.co.jemos.experiments;
import org.springframework.stereotype.Component;
@Component(MessageCommunicator.BEAN_NAME)
public class MessageCommunicator {
public static final String BEAN_NAME = "uk.co.jemos.experiments.MessageCommunicator";
public void deliver(String message) {
System.out.println(message);
}
public void deliver(String person, String message) {
System.out.println(person + ", " + message);
}
}
I said it was very simple! The only thing to notice is that I have defined it as a Spring component.
Then there is the main class:
/**
*
*/
package uk.co.jemos.experiments;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author mtedone
*
*/
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"classpath:appCtx.xml");
MessageCommunicator communicator = ctx
.getBean(MessageCommunicator.class);
communicator.deliver("Hello World");
}
}
The main class loads a Spring application context, it retrieves a MessageCommunicator bean and it invokes a business method on it.
The Spring configuration file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="uk.co.jemos.experiments" />
</beans>Execution without aspects
Now, if I was to execute the code as it is, the output would be something like:
16-Jul-2011 18:50:19 org.springframework.context.support.AbstractApplicationContext prepareRefresh INFO: Refreshing [...] Spring stuff... Hello World
Adding an authentication aspect
Let's say that we want the user of the MessageCommunicator class to be authenticated each time a deliver(...) method is invoked. First I create the Authentication service:
package uk.co.jemos.experiments;
import org.springframework.stereotype.Component;
@Component(Authenticator.BEAN_NAME)
public class Authenticator {
public static final String BEAN_NAME = "uk.co.jemos.experiments.Authenticator";
public void authenticate() {
System.out.println("User is authenticated");
}
}
Nothing glamorous...Again the Authenticator has been declared as a Bean and that is important since we want to inject our Aspect with such Authenticator through Spring.
Then I create the AspectJ aspect:
package uk.co.jemos.experiments;
import javax.annotation.Resource;
public aspect SecurityAspect {
@Resource(name = Authenticator.BEAN_NAME)
private Authenticator authenticator;
pointcut secureAccess() : execution(* MessageCommunicator.deliver(..));
before() : secureAccess() {
if (null == authenticator) {
throw new IllegalStateException("Authenticator should not be null");
}
System.out.println("Checking and authenticating user...");
authenticator.authenticate();
}
}
Here we start seeing few interesting things, mainly that the aspect has been injected with an Authenticator bean.
The revisited Spring context looks now like the following:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="uk.co.jemos.experiments" />
<bean id="securityAspect" class="uk.co.jemos.experiments.SecurityAspect" factory-method="aspectOf" />
</beans>
All I have done is to declare the aspect as a bean: this is possible because every aspect declares a static aspectOf method which returns an instance of the aspect itself. This is the doorway to injecting AspectJ aspects with Spring beans.
Running the modified version
If I run the Main class now, I get the following output:
16-Jul-2011 18:57:22 org.springframework.context.support.AbstractApplicationContext prepareRefresh INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@41ac1fe4: startup date [Sat Jul 16 18:57:22 BST 2011]; root of context hierarchy 16-Jul-2011 18:57:22 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions INFO: Loading XML bean definitions from class path resource [appCtx.xml] 16-Jul-2011 18:57:22 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@66100363: defining beans [uk.co.jemos.experiments.Authenticator,uk.co.jemos.experiments.MessageCommunicator,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,securityAspect]; root of factory hierarchy Checking and authenticating user... User is authenticated Hello World
As you can see the aspect has kicked into action and the Authenticator object was indeed not null at the time of execution, which means that the Spring injection has worked.
From http://tedone.typepad.com/blog/2011/07/inject-beans-in-aspectj.html
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)






Comments
Richard Vowles replied on Sat, 2011/07/23 - 8:45pm
Rolf C Stadheim replied on Sun, 2011/07/24 - 8:32am
Marco Tedone replied on Wed, 2011/07/27 - 2:43pm
in response to:
Richard Vowles
I prefer JSR-250 but one could use @Inject or @Autowire. As for the Authenticator, obviously it would be better to have an interface; the point of this article was to show how to inject Spring beans into AspectJ aspects, not how to follow OOP best practices :-)
I felt that writing an interface for an "HelloWorld" example would have been overkill.