I'm an Agile and Lean Strategist specialised in coaching and managing the transformation of IT departments, from startups to enterprise-scale organisations, to highly efficient, productive and energised environments. I also have experience as senior development manager and architecture governance in large enterprises, especially in the finance sector. Marco is a DZone MVB and is not an employee of DZone and has posted 26 posts at DZone. You can read more from them at their website. View Full User Profile

Inject Spring beans in AspectJ

07.20.2011
| 6013 views |
  • submit to reddit

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

Published at DZone with permission of Marco Tedone, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Richard Vowles replied on Sat, 2011/07/23 - 8:45pm

@Resource seems to be a strange annotation to use here? Wouldn't @Inject have been more appropriate? And wouldn't the Authenticator have been better as an interface? So you could more easily test and mock it? There should only be one instance of the Authenticator in any particular class path (if you are using modular maven) so it would make some sense IMHO. I like the way StickyCode does it.

Rolf C Stadheim replied on Sun, 2011/07/24 - 8:32am

Strange... I was just grappling with how to combine Spring, AspectJ and maven, and here it is, a tailor-made article answering my questions! Thanks!

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.

 

 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.