Advanced AspectJ Part I : Instanciation model

In this serie of articles we’ll talk about advanced features in AspectJ. This will exclude for sure popular AspectJ construct like method execution or within join points.

This first article deals with the instanciation model of AspectJ

As support for our talk, we’ll create a logger injection using annotation and AspectJ.

This entire serie of articles on AspectJ focuses on the code using code-style aspect rather than @AspectJ annotation. We want to keep as close as possible to the original AspectJ semantics

I Instanciation model

A Singleton

 
Similar to a Java class, any aspect in AspectJ needs to be instanciated at runtime to work. But unlike Java classes, aspects are not created using the new keyword. They are created instead at the runtime by the JVM based on their instanciation model declared in the .aj source file.

By default, a declared an aspect is a singleton e.g. there is only one instance of this aspect per classloader (and not per JVM). This instance will be destroyed only when the class loader is garbaged.

   public aspect MyAspect {
      ...
   }

or

   public aspect MyAspect issingleton() {
      ...
   }

If your aspect contains private attributes holding data relative to class instances, your aspect is statefull at it’s high time to look at other alternative instanciation models
 

B Per object instance

It is possible to create one instance of the aspect per object being advised. For this the syntax is:

  • public|abstract|final aspect xxxx perthis(Pointcut)
  • public|abstract|final aspect xxxx pertarget(Pointcut)

where PointCut references a declared pointcut in this aspect.

Example is better than words, let’s see how it works:

   import org.springframework.transaction.annotation.Transactional;

   public aspect PerObjectAspect perthis(transactionalMethod()) {
      
      pointcut transactionalMethod() : execution(@Transactional * *(..));
      ...
      ...
   }

The above aspect will create as many instances as there are objects executing join point selected by transactionalMethod() pointcut e.g. objects executing a @Transactional method.

The semantic difference between perthis() and pertarget() is simple:

  • perthis() creates an instance for every executing object (“this”) at the pointcuts selected by the join point
  • pertarget() creates an instance for every target object (“this”) at the pointcuts selected by the join point

In the above example, for the pointcut execution(@Transactional * *(..)), the executing object is the same as the target object. This is the class instance executing the @Transactional method.

If we change the point cut to call(@Transactional * *(..)), the executing object is the caller and the target object is the one executing the @Transactional method.

The lifecycle of the aspect instance is directly linked to the lifecycle of the advise object. If this object is garbaged by the GC, the aspect instance will be destroyed as well.

 

C Per control flow instance

Sometime it is necessary to create an aspect instance only for some particular control flow ((method calls stack). For this the syntax is:

  • public|abstract|final aspect xxxx percflow(Pointcut)
  • public|abstract|final aspect xxxx percflowbelow(Pointcut)

percflow(Pointcut) will create an aspect instance for each control flow selected by the Pointcut.

percflowbelow(Pointcut) is similar to perflow(Pointcut), excluding the original method call.

 

D Per type instance

The last instanciation model is per type (class, interface or even aspect). It is very similar to the per object instanciation except that now one aspect instance is created per type.

   public aspect PerTypeAspect pertypewithin(com.project.*) {
      ...
   }

The above declaration will create one aspect instance per type (class, interface or aspect itself) in the com.project package and all its sub-packages.

II Accessing the instance

Since more than one instance of the aspect can exist at a time, we need a way to access them. The AspectJ language provide a usefull static method aspectOf() on each aspect to access the created instance.

For example, to access the singleton instance of the MySingletonAspect, we just need to call MySingletonAspect.aspectOf()

Below is a table summerizing the usage of this method:

 

III Injecting a logger

We would like to inject automatically a logger into each class declaring an attribute annotated with @InjectedLogger. It will save us from typing

private static final Logger logger = Logger.getLogger(MyClass.class);

For this, we define the @InjectedLogger annotation.

1) InjectedLogger
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface InjectedLogger
{
	/**
	 * Name of the logger. By default empty When empty, 
	 * the logger name will be replaced by the canonical class 
	 * name of the field annotated with @InjectedLogger
	 */
	String loggerName() default "";
}

When used with no attribute, the injected logger name is set by default to the canonical class name of the field annotated with @InjectedLogger. If a loggerName attribute is provided, the injected logger will use this name.

A test class to exemplify the usage of this annotation:

2) TestInjectedLogger
public class TestInjectedLogger
{

	@InjectedLogger
	private static Logger staticLogger;

	public void staticLogger()
	{
		TestInjectedLogger.staticLogger.info("Static logger called : " + TestInjectedLogger.staticLogger);
	}

	public static void main(String[] args)
	{

		TestInjectedLogger testInjectedLogger = new TestInjectedLogger();
		testInjectedLogger.staticLogger();

	}
}

Finally the aspect implementing the feature:

3) LoggerInjectionAspect
public aspect LoggerInjectionAspect perthis(accessLogger())
{
	private Logger logger = null;
	
	pointcut accessLogger(): getLogger(InjectedLogger) || setLogger(InjectedLogger,Logger);
	
	pointcut getLogger(InjectedLogger annot)
	: get(@InjectedLogger * *) && @annotation(annot);
	
	pointcut setLogger(InjectedLogger annot,Logger logger)
	: set(@InjectedLogger * *) && args(logger) && @annotation(annot);
	
	// Around getLogger() advice
	Logger around(InjectedLogger annot): getLogger(annot)
	{
		if(this.logger == null)
		{
			retrieveLogger(annot,thisJoinPointStaticPart);
		}
		return this.logger;
	}

	// Around setLogger() advice	
	Object around(InjectedLogger annot,Logger arg): setLogger(annot,arg)
	{
		if(this.logger == null)
		{
			retrieveLogger(annot,thisJoinPointStaticPart);
		}
		return proceed(annot,this.logger);
	}
	
	private void retrieveLogger(InjectedLogger annot,StaticPart jpStatic)
	{
		if(StringUtils.isNotBlank(annot.loggerName()))
		{
			this.logger = Logger.getLogger(annot.loggerName());
		}
		else
		{
			this.logger = Logger.getLogger(jpStatic.getSignature().getDeclaringTypeName());	
		}
	}
}

First the aspect declares a private attribute logger (line 32). Similar to a class, an aspect can have attributes and methods.

Then we declare an abstract poincut accessLogger() whose definition relies on the concrete poincuts getLogger() & setLogger() (line 5)

The pointcut getLogger() is triggered when any attribute annotated with @InjectedLogger is accessed (field reference)

The pointcut setLogger() is triggered when any attribute annotated with @InjectedLogger is set

We also define a private method retrieveLogger() to effectively retrieve the Logger instance from Log4J Logger factory using the canonical class name or provided loggerName (line 32).

The advice on getLogger() will return the private logger instance if it is not null or call first the retrieveLogger() method to initialize it.

The advice on setLogger() also initializes the logger attribute when necessary. It then assigns it to the target attribute annotated with @InjectedLogger. Exemple below:

public class TestInjectedLogger
{
	@InjectedLogger
	private static Logger staticLogger;

	...
	...
	TestInjectedLogger.staticLogger = Logger.getLogger("testLogger");
	...
	...

In the code above, the advice on setLogger() will intercept the assignment at line 8 to substitute its own instance of logger. Consequently, the staticLogger attribute is pointing to the logger “com…TestInjectedLogger” instead of “testLogger”.

Since the aspect has a private attribute storing the real logger for the target class (statefull aspect), the instanciation model should meet this requirement. At line 1 we define the instanciation model as perthis(accessLogger()) so there will be one instance of LoggerInjectionAspect for each object having an @InjectedLogger annotated attribute.

While running the class TestInjectedLogger, we got the following output:

INFO [01:05:34,033] com.test.TestInjectedLogger@staticLogger: Static logger called : org.apache.log4j.Logger@1b3f829

2 Comments

  1. Pingback: Advanced AspectJ part III : non-kinded pointcuts « Yet Another Java Blog

  2. Pingback: Advanced AspectJ part V : integration with Spring « Yet Another Java Blog

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.