Inject Spring beans in AspectJ
Join the DZone community and get the full member experience.
Join For FreeAspectJ 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
Opinions expressed by DZone contributors are their own.
Comments