Java and Spring development

Posts Tagged ‘Advice

@AspectJ cheat sheet

with 22 comments

This cheat sheet uses AspectJ’s @AspectJ style. It’s also possible to use the original AspectJ syntax like this example demonstrates, but I prefer to use standard Java classes with the AspectJ logic inside annotations.

Pointcuts

The definition of a pointcut from the AspectJ homepage:

A pointcut is a program element that picks out join points and exposes data from the execution context of those join points. Pointcuts are used primarily by advice. They can be composed with boolean operators to build up other pointcuts.

A pointcut example:

Pointcut designators

A method pointcut:

@Pointcut("[method designator](* aspects.trace.demo.*.*(..))")
public void traceMethodsInDemoPackage() {}
  • call – The pointcut will find all methods that calls a method in the demo package.
  • execution – The pointcut will find all methods in the demo package.
  • withincode – All the statements inside the methods in the demo package.

A type pointcut:

@Pointcut("[type designator](*..*Test)")
public void inTestClass() {}
  • within – all statements inside the a class that ends with Test.

A field pointcut:

@Pointcut("[field designator](private org.springframework.jdbc.core.JdbcTemplate " +
       "integration.db.*.jdbcTemplate)")
public void jdbcTemplateGetField() {}
  • get – all reads to jdbcTemplate fields of type JdbcTemplate in the integration.db package. Includes all methods on this field if it’s an object.
  • set – when you set the jdbcTemplate field of type JdbcTemplate in the integration.db package to a new value.

Signature pointcuts

This chapter explains more advanced signature pointcuts than illustrated on the image above.

Support for sub packages is provided with “..”:

@Pointcut("within(*..*Test)")
public void inTestClass() {}

To find the joinpoints inside a type that ends with Test inside the ..demo package:

@Pointcut("within(com.redpill.linpro.demo..*Test)")
public void inDemoProjectTestClass() {}

All statements inside all classes except test classes in the ..demo package with the “!” before the designator:

@Pointcut("!within(com.redpill.linpro.demo..*Test)")
public void notInDemoProjectTestClass() {}

All methods in the Service class or a subtype of it:

@Pointcut("execution(void *..service.Service+.*(..))")
public void servicePointcut() {}

All getCoffeeType methods in a class that begins with CoffeeTypeRepository:

@Pointcut("execution(CoffeeType integration.db.CoffeeTypeRepository*.getCoffeeType(CoffeeTypeName))" +
    " && args(coffeeTypeName)")
public void getCoffeeTypePointcut(CoffeeTypeName coffeeTypeName) {}

Note that the pointcut also contains args(coffeeTypeName) and that the Java method has a method with a CoffeeTypeName as input parameter. An advice that advices this pointcut must also have this input parameter.

This field pointcut finds all the places in the integration package where a field named jdbcTemplate gets a new value of type JdbcOperations or a subtype of it like JdbTemplate.

@Pointcut("set(private org.springframework.jdbc.core.JdbcOperations+ " +
    "integration..*.jdbcTemplate)")
public void jdbcTemplateSetField() {}

Annotation pointcuts

A pointcut can declare an annotation before the signature pattern.

An example of an annotation used as a marker interface:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PerformanceLogable {}

All it does is to provide meta information to methods.

A pointcut that finds all methods marked with the @PerformanceLogable on the classpath:

@Pointcut("execution(@aspects.log.performance.PerformanceLogable * *(..))")
public void performanceLogableMethod() {}

The @Transactional annotation supports both method and type target. Which means it can be used with both method and type designators. The pointcut below is the same as the performanceLogableMethod pointcut, except it finds methods with Spring’s @Transactional annotation.

@Pointcut("execution(@org.springframework.transaction.annotation.Transactional * *(..))")
public void transactionalMethod() {}

A pointcut that finds all constructors marked with @Inject and have an integer as input parameter:

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

The @Service annotation has target set to type and can therefore only annotate types. The pointcut below will find all statements in all types marked with @Service.

@Pointcut("within(@org.springframework.stereotype.Service *)")
public void serviceBean() {}

The joinpoint will be all statements inside a type marked with the @Component annotation or a specialization of it:

@Pointcut("within(@(@org.springframework.stereotype.Component *) *)")
public void beanAnnotatedWithComponentOrASpecializationOfIt() {}

Finds all statements that’s not inside a method marked with @Test:

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

Finds all methods with one or more parameters marked with the @MyParamAnnotation:

@Pointcut("execution(public * *(.., @aspects.MyParamAnnotation (*), ..))")
public void methodWithAnnotationOnAtLeastOneParameter() {}

For a full example with explanations, see this post.

A pointcut with a runtime condition and a required public static method:

@Pointcut("execution(* *.actionPerformed(java.awt.event.ActionEvent)) " +
		"&& args(actionEvent) && if()")
public static boolean button1Pointcut(ActionEvent actionEvent) {
	return (actionEvent.getSource() == j1);
}

More information can be found in this post

Combining pointcuts

Instead of having a large pointcut, it’s a much better approach to combine several pointcuts into one.

@Pointcut("traceMethodsInDemoPackage() && notInTestClass() && notSetMethodsInTraceDemoPackage()")
public void filteredTraceMethodsInDemoPackage() {}

Advices

The definition from the AspectJ homepage:

A piece of advice brings together a pointcut and a body of code to define aspect implementation that runs at join points picked out by the pointcut.

An advice can be executed before, after, after returning, after throwing or around the joinpoint.

The before advice below is executed before the target method specified in filteredTraceMethodsInDemoPackage pointcut:

@Before("filteredTraceMethodsInDemoPackage()")
public void beforeTraceMethods(JoinPoint joinPoint) {
	// trace logic ..
}

This after advice is executed after the target method.

@After("filteredTraceMethodsInDemoPackage()")
public void afterTraceMethods(JoinPoint joinPoint) {
	// trace logic ..
}

The afterThrowing advice will be executed if the method that matches the pointcut throws an exception. You can also declare the Exception and handle it like this:

@AfterThrowing(value="serviceMethodAfterExpcetionFromIntegrationLayerPointcut()", throwing="e")
public void serviceMethodAfterExceptionFromIntegrationLayer(JoinPoint joinPoint,
                RuntimeException e) {
	StringBuilder arguments = generateArgumentsString(joinPoint.getArgs());

	logger.error("Error in service " + joinPoint.getSignature() + " with the arguments: " +
			arguments, e);
}

The AfterReturning advice will only be executed if the adviced method returns successfully.

@AfterReturning("transactionalMethod()")
public void afterTransactionalMethod(JoinPoint joinPoint) {
	transactionService.commit();
}

The around advice is quite powerful, but also consumes more resources and should only be used if you can’t make it work with other advices. The example below simulates some logic before and after the adviced method. The ProceedingJoinPoint extends the JoinPoint class and is required to call the adviced method with the proceed() method.

@Around("performanceLogablePointcut()")
public void aroundPerformanceLogableMethod(ProceedingJoinPoint point) {

    // Pre processing work..

    point.proceed();

    // Post processing work..
}

Note: You don’t need an around advice to monitor the execution time for a method. It’s just the simplest option and therefore used in this example.

The advice below combines two pointcuts and the last one has an integer object as input parameter. This requires the Java method to also have an integer parameter. It enables the advice logic to access the input parameter directly and in a type safe manner. A less elegant alternative is to access the parameter with the joinPoint’s getArgs() method.

@AfterReturning("beanAnnotatedWithComponentOrASpecializationOfIt() &&  " +
	"constructorAnnotatedWithInjectAndIndemoPackage(integer)")
public void afterReturningFromConstructorInSpringBeanWithIntegerParameter(
		JoinPoint joinPoint, Integer integer) {

	// Advice logic..
}

Summary

This cheat sheet consist of many simple pointcuts. Often you have to combine them to add value to your application. This example article combines pointcuts with a class marked with @Service and a constructor and method marked with @Inject. It also filters away the test method with the @Test annotation.

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 20, 2010 at 18:28

Posted in Aspects

Tagged with , , ,

@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 , , , ,