Java and Spring development

@AspectJ examples with pointcuts based on annotations

with 7 comments

This example demonstrates how to trace methods, constructors and fields annotated with @Inject and that’s inside a class annotated with @Component or a specialization of it like @Service. The aspect has three advices that combines different pointcuts. All the pointcuts uses primary marker interfaces (annotations) to determine where to place the adviced code. The annotations used in the pointcuts have target set to either method or type.

The example consist of a Demo class, a TraceAspect, a Spring configuration file and a JUnit test. The test starts the Spring container and the container injects all fields, methods and constructors in the Demo and DemoTest classes annotated with @Inject. It’s this dependency injection that the aspect will monitor and trace. The last part is when the test method calls the toString() method on the demo object and the toString() method reads the value in the text field. This get operation on the field is also monitored and traced by the aspect.

The Demo class

The Demo class has a text field that’s impossible to advice when the field gets set, since the Spring container injects the value with reflection and it’s no places in the code that sets this field. But it’s possible to advice the toString() method that reads this field. The constructor and method annotated with @Inject are adviced after they have returned successfully.

package demo;

import javax.inject.Inject;
import javax.inject.Named;

import org.springframework.stereotype.Service;

@Service
public class Demo {

	@Inject
	private String text;

	private final Integer integerOne;

	private Integer integerTwo;

	@Inject
	@Named("integerOne")
	public Demo(final Integer integerOne) {
		this.integerOne = integerOne;
	}

	@Inject
	@Named("integerTwo")
	public void setintegerTwo(final Integer integerTwo) {
		this.integerTwo = integerTwo;
	}

	@Override
	public String toString() {
		return "Demo [integerOne=" + integerOne + ", integerTwo=" + integerTwo
				+ ", text=" + text + "]";
	}
}

A screenshot from STS with AspectJ nature enabled:

adviced-demo-class

The first two orange arrows shows the joinpoint for two afterReturn advices, while the last one show the joinpoint for an around advice.

The TraceAspect class

Briefly summarized, the aspect below traces all methods and constructors in the demo package annotated with @Inject and that’s inside a type annotated with @Component or a specialization of it. Further, it demonstrates how to declare a pointcut with or without @args(..). The last advice is an around advice that traces all String fields annotated with @Inject in the demo package. It also prints out the return value.

package demo;

import javax.inject.Inject;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.junit.Test;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

/**
 * An example aspect that shows how to write pointcuts for annotated fields, methods and
 * types and how to advice those aspects.
 *
 * <p>
 * <ul>
 * 	<li>annotation target set to method</li>
 *
<ul>
	<li>{@link Inject @Inject}, {@link Test @Test}</li>
</ul>
 *  <li>annotation target set to type</li>
 *
<ul>
	<li>{@link Component @Component} or a specialization of it</li>
</ul>
 *  	({@link Service @Service}, {@link Repository @Repository})
 * </ul>
 *
 * @author Espen Berntsen
 * @see Demo
 */
@Aspect
public class TraceAspect {

	// Trace injected methods and constructors -->
	@Pointcut("within(@(@org.springframework.stereotype.Component *) *)")
	public void beanAnnotatedWithComponentOrASpecializationOfIt() {}

	@Pointcut("execution(@javax.inject.Inject * demo.*.*(..))")
	public void methodAnnotatedWithInjectAndInDemoPackage() {}

	@Pointcut("execution(@javax.inject.Inject demo.*.new(Integer)) && args(integer)")
	public void constructorAnnotatedWithInjectAndIndemoPackage(
			Integer integer) {}

	// With args(integer).
	@AfterReturning("beanAnnotatedWithComponentOrASpecializationOfIt() &&  " +
			"constructorAnnotatedWithInjectAndIndemoPackage(integer)")
	public void afterReturningFromConstructorInSpringBeanWithIntegerParameter(
			JoinPoint joinPoint, Integer integer) {
		System.out.println("Executed @Injected constructor: "
				+ joinPoint.getSignature() + " with the integer: " + integer);

	}

	// Using joinPoint.getArgs() to show arguments.
	@AfterReturning("beanAnnotatedWithComponentOrASpecializationOfIt() &&  " +
			"methodAnnotatedWithInjectAndInDemoPackage()")
	public void afterReturningFromMethodInSpringBean(JoinPoint joinPoint) {
		System.out.print("Executed the @Injected method: "
				+ joinPoint.getSignature() + " with value(s): ");

		for (Object object : joinPoint.getArgs()) {
			System.out.print(object);
		}
		System.out.println();
	}
	// Trace injected methods and constructors <--

	// Trace get on String fields annotated with @Inject -->
	@Pointcut("get(@javax.inject.Inject java.lang.String demo..*.*)")
	public void getStringFieldAnnotatedWithInjectInTheDemoPackage() {}

	@Pointcut("!withincode(@org.junit.Test * demo..*(..))")
	public void notInTestMethod() {}

	@Pointcut("getStringFieldAnnotatedWithInjectInTheDemoPackage() && notInTestMethod()")
	public void getStringFieldAnnotatedWithInjectInTheDemoPackageAndNotInTestMethod() {}

	@Around("getStringFieldAnnotatedWithInjectInTheDemoPackageAndNotInTestMethod()")
	public String aroundGetStringFieldAnnotatedWithInjectInTheDemoPackageAndNotInTestMethod(
			ProceedingJoinPoint joinPoint) throws Throwable {

		String fieldValue = (String) joinPoint.proceed();
		System.out.println("Get on @Inject String field: "
				+ joinPoint.getSignature() + " with value: " + fieldValue);
		return fieldValue;
	}
	// Trace get on String fields annotated with @Inject <--
}

The Spring configuration file

The configuration file below contains a component-scan element that finds the Demo class because it’s annotated with @Service. The three other beans are injected into the demo bean’s field, constructor and method marked with @Inject. This is enabled with the component-scan or annotation-config element in the context namespace. Since it’s two integers, the method and constructor in the Demo class also have an @Named(..) annotation with a value equal to the bean id that’s injected.

<?xml version="1.0" encoding="UTF-8"?>
xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	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/context
		http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<context:component-scan base-package="demo" />

	<bean id="a" class="java.lang.String" factory-method="valueOf">
		<constructor-arg value="test" />
	</bean>

	<bean id="integerOne" class="java.lang.Integer" factory-method="valueOf">
		<constructor-arg value="1" />
	</bean>

	<bean id="integerTwo" class="java.lang.Integer" factory-method="valueOf">
		<constructor-arg value="2" />
	</bean>
</beans>

The test class

Finally, the test class that instantiates the Spring container executes the toString() method on the demo object. If you’re not familiar with Spring, you can replace the Spring configuration with a factory method in the test class instead as long as you’re setting the @Inject field, constructor and method.

package demo;

import javax.inject.Inject;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * Should have been a main method, but I use the
 * {@link Test} annotation to demonstrate withincode designator
 * {@link TraceAspect#beanAnnotatedWithComponentOrASpecializationOfIt()}
 *
 * @author Espen Berntsen
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(&quot;config.xml&quot;)
public class DemoTest {

	@Inject
	private Demo demo;

	@Test
	public void testToString() {
		System.out.println(&quot;demo.toString(): &quot; + demo.toString());
	}
}

The console output after executing the test class:

Executed @Injected constructor: demo.Demo(Integer) with the integer: 1
Executed the @Injected method: void demo.Demo.setintegerTwo(Integer) with value(s): 2
Get on @Inject String field: String demo.Demo.text with value: test
demo.toString(): Demo [integerOne=1, integerTwo=2, text=test]

Summary

Annotations are very good pointcuts and are frequently used by application containers to add enterprise features. See also this cheat sheet for more information about how to write aspects with @AspectJ style.

The code used in this article was developed in my work time at Redpill Linpro.


Redpill Linpro is the leading provider of Professional Open Source services and products in the Nordic region.

Written by Espen

March 18, 2010 at 22:28

Posted in Aspects

Tagged with , , , ,

7 Responses

Subscribe to comments with RSS.

  1. I’ve got a exception that says ‘.. contains unsupported pointcut primitive get’, any solution to this error?

    lian

    June 22, 2010 at 04:35

    • Are you using Spring AOP with AspectJ syntax? Spring AOP doesn’t support field pointcuts.

      If you’re weaving with AspectJ, post your aspect on stackoverflow.com and tag it with aspectj and I will take a look at it.

      Espen

      June 22, 2010 at 11:28

  2. I just copied your codes and tried to run it, however, I found I got that exception. So I wonder how did you manage to compile them. I am new to aspectj.
    thanks for your reply

    lian

    June 22, 2010 at 17:39

    • I use the AJDT Eclipse plugin .

      Then you can just: right click on your project –> Configure –> Convert to AspectJ project.

      This enables compile-time weaving and tool support like this image illustrates.

      Espen

      June 22, 2010 at 18:02

  3. […] good example by the same […]

  4. Thanks, good examples!

    Andrei

    August 4, 2014 at 18:35

  5. […] have written a relevant blog post that explains with an example most of your problems and a picture of how elegant the AJDT Eclipse […]


Leave a comment