Implement Logging as an Aspect Using Spring's AOP

Implement Logging as an Aspect Using Spring's AOP Framework

Aspect-oriented programming (AOP) enables you to write code for a crosscutting functionality and then apply it declaratively to existing code. Logging is a prime candidate for implementation as an aspect, and Spring offers the AOP framework to get it done. 
bject-oriented programming (OOP) brought about the concept of encapsulation, inheritance, and polymorphism in order to create a hierarchy of objects that models a common set of behaviors. So, a particular set of objects is modeled to depict a particular pattern of behavior. But OOP does not explain how to handle a common behavior that extends across unrelated objects. Hence, the same set of code (i.e., redundant code) is scattered throughout the system.

Take the case of logging, for example. Logging code is scattered horizontally across multiple object hierarchies most of the time, and it has nothing to do with the functionalities the object hierarchies represent. In other words, OOP best depicts the top-down (vertical) object relationship but it falls short in handling left-to-right (horizontal) object relationship. Aspect-oriented programming (AOP) facilitates the latter by referring horizontal object relationships as crosscutting concerns and separating them from core functionalities.

The AOP technique helps you add design and run-time behavior to an object model in a non-obtrusive manner by using static and dynamic crosscutting. By using AOP, you can write code for a crosscutting functionality such as logging and then apply it declaratively (via annotations or XML declaration) to already existing code. Also, you can inject the code into plain old Java objects (POJOs) for third-party software for which you don’t have either the source code or the license to modify it. In this way, AOP provides for a flexible and loosely coupled architecture.

 

Logging as an Aspect
Logging has a lot of characteristics that make it a prime candidate for implementation as an aspect. The following are two of the notable characteristics:
  • Logging code is often duplicated across an application, leading to a lot of redundant code across multiple components in the application. Even if the logging logic is abstracted to a separate module so that different components have a single method call, that single method call is duplicated in multiple places.
  • Logging logic does not provide any business functionality; it's not related to the domain of a business application.

Spreading the logging across multiple components introduces some complexity to the code. Figure 1 illustrates this complexity. The business objects on the left encapsulate the business functionality. Not only do they perform business tasks, they also take care of logging functionality (and other such crosscutting concerns, such security, transaction, caching, etc.).

Click to enlarge

Figure 1 . Complexity of Spreading the Logging Across Multiple Components

AOP makes it possible to modularize these crosscutting services and then apply them declaratively to the components that they should affect. This allows the other core business components to focus on their main tasks and forget about any system services with which they might be involved. Think of an aspect as a blanket that covers many components of an application (see Figure 2 ). The core functionalities of the application lie with the various components, and you apply the aspects to them declaratively without them knowing those AOP identifies the places in code where the aspects are to be injected; these places are called pointcuts. Aspects are injected into the pointcuts during compile-time or runtime, depending on the AOP framework you use. In fact, AOP allows the introduction of new functionalities into objects without the objects knowing about them. This very effective concept provides flexibility, scalability, and maintainability (among other benefits) to the system. functionalities really exist.

Click to enlarge

Figure 2 . Logging Module Provides a Blanket to Other Services

The example in this article uses Spring as the AOP framework.



Spring as an AOP Framework
Spring uses a proxy-based approach to tie aspects into the object model. That is, Spring intercepts all method invocations to the aspected objects and then proxies the method invocations to the intended objects. So, an application makes a method call to a business object, Spring intercepts that call, applies the weaved crosscutting aspect, and then makes a method call to the intended business object. It is just like the EJB framework, where the EJB Container intercepts calls to the EJB component, applies the intended crosscutting behavior (like security check, transaction, etc.), and then invokes the intended EJB object. It defines and applies the aspects using a XML configuration file.

 

Configuring Logging as AOP
Listing 1 shows the configuration file that Spring uses to gel together components and services:

Listing 1.
 Example XML Configuration File for Spring

<bean id="maintenanceServiceBean" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>IMaintenanceService</value> </property> <property name="target"> <ref bean="maintenanceServiceImpl"/> </property> <property name="interceptorNames"> <list> <value>loggingInterceptor</value> </list> </property> </bean> <bean id=" maintenanceServiceImpl " class="MaintenanceService"> <bean id=" loggingInterceptor" class="LoggingInterceptor"/>

Spring's 'built-in' AOP infrastructure is defined by the org.springframework.aop.* packages. Spring supports the concept of a FactoryBean, a special type of bean that is returned as a factory result rather than just a plain newInstance() call on the class provided. That means the example given in Listing 1 will not produce an instance of ProxyFactoryBean, but Spring consults with the ProxyFactoryBean object and asks for the maintenanceServiceBean object. Why is all of this important? Because this FactoryBean concept is how Spring wraps beans and then creates a proxy for the bean (using some internal tool such as dynamic proxies, CGLIB, etc.) that executes some advice on method calls when the pointcut says the method is a joinpoint (assuming a pointcut is defined). A joinpoint is a point during the execution of a program such as method invocation or an exception being thrown. That means in this example when the container is asked for a maintenanceServiceBean object, it will return an object that intercepts calls to the target maintenanceServiceImpl object, giving LoggingInterceptor a shot at handling method calls first. Once LoggingInterceptor is finished, control is returned to maintenanceServiceImpl to perform the original task.

The interceptorNames property is how the proxy factory bean finds out which advisors or advice objects need to be applied to the proxied bean. Order is important in this property's list—the order in which the advisors or advice objects are entered in the list defines the order they will be invoked when entering and exiting the bean. So in Listing 1, the loggingInterceptor is the advised object, which needs to be applied to the proxied maintenanceServiceBean.

Listing 2 shows the code for the LoggingInterceptor class:


Listing 2.
 The LoggingInterceptor Class

import java.lang.reflect.Method; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.aop.AfterReturningAdvice; import org.springframework.aop.MethodBeforeAdvice; import org.springframework.aop.ThrowsAdvice; public class LoggingInterceptor implements MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice{ private static Log log = null ; public LoggingInterceptor(){ } public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { log = LogFactory.getLog(arg2.getClass()); log.info("Beginning method: " +arg0.getName()); } public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable { log = LogFactory.getLog(arg3.getClass()); log.info("Ending method: " +arg1.getName()); } public void afterThrowing(Method m, Object[] args, Object target, Throwable ex) { log = LogFactory.getLog(target.getClass()); log.info("Exception in method: " +m.getName()+" Exception is: " +ex.getMessage()); } }

The LoggingInterceptor class implements three interfaces of the Spring AOP framework: MethodBeforeAdvice , AfterReturningAdvice , and ThrowsAdvice . The MethodBeforeAdvice interface is implemented by advice, which needs to be fired before the target method is invoked. The interface has a void before (Method arg0, Object [] arg1, Object arg2) throws Throwable method, which needs to be implemented. The code inside the before method gets executed before the target method is executed. So, Listing 2 includes the appropriate code to log the method call.

Similarly, the AfterReturningAdvice interface is implemented by advice, which needs to be fired after the target method is executed. This interface has a void afterReturning (Object arg0, Method arg1, Object [] arg2, Object arg3) throws Throwable method, which gets executed after the target method is executed.

Lastly, the ThrowsAdvice interface is implemented by advices that need to be fired when the target method throws an exception. It has a void afterThrowing(Method m, Object[] args, Object target, Throwable ex) method that gets executed when the target method throws an exception.

Since the general logging functionality involves tracing entry into a method, exit from the method, and exception, the LoggingInterceptor class implements the above three interfaces to carry out these tasks.



Configuration for Selective Logging
Listing 1 shows a good configuration for weaving logging into each and every method call (that is defined in the bean interface) in the targeted bean. What if you wanted to trace selective method calls? This scenario is more appropriate when crosscutting concerns are applied selectively, like security, transaction, audit-trail, etc.

Listing 3 shows the configuration for selective logging:


Listing 3.
 XML Configuration File for Selective Logging

<bean id="maintenanceServiceBean" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>IMaintenanceService</value> </property> <property name="target"> <ref bean="maintenanceServiceImpl"/> </property> <property name="interceptorNames"> <list> <value> theLogger </value> </list> </property> </bean> <bean id="theLogger" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref local=" loggingInterceptor "/> </property> <property name="patterns"> <value>.*getList.*,.*getCounts.*</value> </property> </bean> <bean id=" maintenanceServiceImpl " class="MaintenanceService"> <bean id=" loggingInterceptor" class="LoggingInterceptor"/>

The difference between the configurations in Listing 3 and Listing 1 is that in Listing 3 the logging interceptor (theLogger) points to a Spring class called RegexpMethodPointcutAdvisor instead of pointing to its implemented class, as was the case in Listing 1. This means that theLogger is a class for regexp method pointcuts that holds an advice (in this case, loggingInterceptor, which implements the actual logging mechanism as an advice), and it can be configured using "pattern" and "patterns" pass-through properties. These properties actually allow the advice to be applied on a selected public method (or methods) or on each public method of the intercepted bean.

The "pattern" property takes one pattern of a method name, like .*getList.*. The pattern should be a fully qualified method name. If * is supplied, then the advice applies on all the public methods of the intercepted bean. The "patterns" property is actually an array of patterns (e.g., .*getList.*,.*getCounts.*). The patterns should be comma-separated.

 

A Powerful and Flexible Concept
Implementing logging as an aspect with Spring's AOP framework is a powerful and flexible concept. Since Spring is lightweight, the logging implementation is simply a POJO—not tightly coupled with any framework. This makes it reusable in any system whether container-based or another lightweight framework.

Along the same lines as logging, you can implement other crosscutting concerns. For example, suppose you need to implement an audit trail such that an audit trail component is invoked whenever there is a call to the database. For this simple purpose, the audit trail service just needs to implement the MethodBeforeAdvice interface of Spring and provide the necessary implementation for the before () method.

 

Gopal Sharma is a manager with Capgemini India. He has a wide range of experience with Java technologies and has assumed many project roles, from developer to architect.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值