Audit logging and AOP

We have logging in our sample code and for that we use slf4j, which we covered in the previous chapter. Logging is more or less the decision of the developer and supports technical levels of operation. There, we also touched on a few sentence audit loggings. This type of logging is usually explicitly required in a functional requirement.

Generally, AOP is separating the different aspects of code functionality into separate code fragments, and implementing them independent of each other. This is very much the single responsibility principle. This time, it is implemented in a way that not only the different functionalities are implemented separately but also how we connect them together is defined separately. What is executed before and after what other parts are encoded separately gets to the Spring configuration. We have seen something similar already. The dependencies that a class needs to properly operate are defined in a separate segment (XML or Java code). It is not a surprise that in the case of AOP, the same is done using Spring. Aspects are configured in the configuration file or class.

A typical aspect is audit logging, and we will use this as an example. There are many topics that can be implemented using aspects, and some of them are even worth implementing that way.

We do not want to implement the audit logging code in each business method or class that needs it. Instead, we implement a general aspect and configure the wiring such that whenever a bean method that needs audit logging is invoked, Spring invokes the audit logging.

There are other important terminologies that we should understand for AOP and especially how AOP can be configured in Spring.

The first and most important is the aspect. This is the functionality that we want to implement, in our example, the audit logging.

Join point is the point in execution when an aspect is invoked. When using a full-scale aspect solution in Java that modifies the byte code of the generated class, a join point can be almost anything. It can be access to a field, read or write; it can be the invocation of a method or exception throwing. In the case of Spring, the class byte code is not modified; thus, Spring is not able to identify the access of a field or an exception throwing. Using Spring, a join point is always used when a method is invoked.

An advice is how the aspect is invoked at the join point. It can be before advice, after advice, or around advice. When the advice is before, the aspect is invoked before the method is called. When the advice is after, the aspect is invoked after the method is invoked. Around means that the aspect is invoked before the method call, and the aspect also has an argument to call the method and still perform some actions after the method is called. This way, the around advice is very similar to servlet filters.

The before advice is called before the method call, and after it returns, the framework will invoke the method. There is no way for the aspect to prevent the invocation of the original method. The only exception is when the aspect, well, throws an exception.

The after advice is also affected by exceptions. There can be an after returning advice that is invoked when the method is returning. The after throwing is invoked only if the method were throwing an exception. After finally is invoked in the case of an exception or return.

Pointcut is a special string expression that identifies join points. A pointcut expression may match zero, one, or more join points. When the aspect is associated with a pointcut expression, the framework will know the join points and when and where to invoke the aspect. In other words, pointcut is the string that tells when and for which method to invoke the aspect.

Even though Spring implementation of AOP does not use AspectJ and does not modify the byte code that was created for the classes, it supports the pointcut expression language. Although this expression language provides more features than what Spring implements, it is a well-established and widely used and accepted expression language to describe pointcuts, and it just would not make sense to invent something new.

Introduction is adding methods or fields to a type that already exists and doing it during runtime. Spring allows this AOP functionality to add an interface to an existing type and add an implementation of the interface in the form of an advice class. In our example, we do not use this functionality.

Target object is the object that is being advised by the aspect. This is the bean that contains the method around the aspect, that is, before or after the aspect is invoked.

That was just a condensed set of definitions, almost like in a math book. If you did not get the point just reading it, don't worry. I did not understand it either. That is why we have the following example, after which all we just covered will make more sense:

package packt.java9.by.example.mybusiness.productinformation; 

import ...

@Configuration
@Aspect
public class SpringConfigurationAspect {
private static Logger log =
LoggerFactory.getLogger("AUDIT_LOG");

@Around("execution(* byId(..))")
public ProductInformation byIdQueryLogging(
ProceedingJoinPoint jp)
throws Throwable {
log.info("byId query is about to run");
ProductInformation pi =
(ProductInformation) jp.proceed(jp.getArgs());
log.info("byId query was executed");
return pi;
}

@Around("execution(* url(..))")
public String urlCreationLogging(ProceedingJoinPoint jp)
throws Throwable {
log.info("url is to be created");
String url = (String) jp.proceed(jp.getArgs());
log.info("url created was "+url);
return url;
}
}

The class is annotated with the @Configuration annotation so that Spring knows that this class contains the configuration. The @Aspect annotation denotes that this configuration may also contain aspect definitions. The @Around annotation on the methods gives the type of advice, and the argument string for the annotation is the pointcut expression. If the type of advice is different, one of the annotations, @Before, @After, @AfterReturning, or @AfterThrowing, should be used.

In our example, we use the @Around aspect to demonstrate the most complex scenario. We log the execution of the target method before and after the execution of the method, and we also call the original method through the ProceedingJoinPoint object. Because the two objects return different types and we want to log differently, we define two aspect methods.

The argument of the advice annotation is the pointcut string. In this case, it is a simple one. The first one, execution(* byId(..)), says that the aspect should be invoked for any execution of any method that has the name byId and has any arguments. The second is very similar, except the name of the method is different. These are simple pointcut expressions, but in a large application that heavily uses AOP, they can be very complex.

The pointcut expression syntax in Spring mainly follows the syntax used by AspectJ. The expression uses the notion of point cut designator (PCD) that is usually execution. It is followed by the pattern that defines which method to intercept. The general format is as follows:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)

Except for the return type part, all other parts are optional. For example, we can write the following:

execution(public * *(..))

This will intercept all public methods. The following expression intercepts all methods that have a name starting with set:

execution(* set*(..))

We can use the * character as a joker in the same way as we can use it on the command line in Windows or Unix shell. The argument matching definition is a bit more complex. (..) means any arguments, () means no arguments, and (*) means exactly one argument of any type. The last one can also be used when there are more arguments; for example, (*,Integer) means that there are two arguments, the second being an Integer, and we just do not care what the type of the first one is.

Pointcut expressions can be more complex, joining together match expressions with the && (and) and || (or) logical operators, or using the ! (negation) unary operator.

Using the @Pointcut() annotation, the configuration can define pointcuts putting the annotations on methods. For example, consider the following:

@Pointcut("execution(* packt.java.9.by.example.service.*.*(..))")  
public void businessService() {}

It will define a join point for any method that is implemented in any class in the packt.java.9.by.example.service package. This merely defines the pointcut expression and assigns it to the name businessService, which is given by the name of the method. Later, we can refer to this expression in aspect annotations, for example:

@After("businessService()")

Note that the use of the method is purely for its name. This method is not invoked by Spring. It is only used to borrow its name to the expression that is defined on it using the @Pointcut annotation. There is a need for something, such as a method, to put this annotation on, and since methods have names, why not use it: Spring does it. When it scans the configuration classes and sees the annotation, it assigns it in its internal structures to the name of the method, and when that name (along with the parenthesis, to confuse the novice programmer mimicking a method call) is used, it looks up the expression for that name.

AspectJ defines other designators. Spring AOP recognizes some of them, but it throws IllegalArgumentException because Spring implements only method execution pointcuts. AspectJ, on the other hand, can also intercept object creation for which the PCD is initialization, as an example. Some other PCDs, in addition to execution, can limit an execution PCD. For example, the PCD, within, can be used to limit the aspect to join points belonging to classes within certain packages, or the @target PCD can be used to limit the matching to methods in objects that have the annotation given between ( and ) after the keyword @target in the pointcut expression.

There is a PCD that Spring uses that does not exist in AspectJ. This is a bean. You can define a pointcut expression that contains bean(name pattern) to limit the join point to method executions that are in the named bean. The pattern can be the entire name or it can be, as almost any PCD expression matching, * as a joker character.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.20.68