About Xavier Padro

Xavier is a software developer working in a consulting firm based in Barcelona. He is specialized in web application development with experience in both frontend and backend. He is interested in everything related to Java and the Spring framework

Applying aspect oriented programming

1.Introduction

The main target of the aspect oriented programming is the separation of cross-cutting concerns. When we talk about cross-cutting concerns we are referring to generic functionality that is used in several places in our system or application. These concepts are, among others:
 
 
 
 

  • Logging
  • Transaction management
  • Error handling
  • Monitoring
  • Security

The way to achieve this separation is by modularizing these concepts. This will allow us to keep our business logic classes clean, containing only the code for which the class was designed. If we don’t modularize these concerns, it will lead to code tangling (the class contains different concerns) and code scattering (the same concern will be spread across the system).

In this example, we have a Spring MVC application that access the requested data (clients and orders) and shows a page with its information. We can take a look at the different layers:

aop1

In the above graphic, we can appreciate that there are functionalities spread across different classes (monitoring is implemented in every service), and some classes contain different concerns (for example, the class ClientController contains logging and exception handling). In order to fix that, we will write aspects to implement our cross-cutting concerns. The goal is to implement the following model:

aop2

Each class contains only the business logic related code, while the aspects will be responsible of intercepting the code in order to inject the cross-cutting concerns.

Let’s see this with an example.

  • Source code can be found at github.

2.Checking controller code

ClientController:

@Controller
public class ClientController {
    @Autowired
    private ClientService clientService;
    private static Logger mainLogger = LoggerFactory.getLogger("generic");
    private static Logger errorLogger = LoggerFactory.getLogger("errors");

    @RequestMapping("/getClients")
    public String getClients(Model model, @RequestParam("id") int id) {
        mainLogger.debug("Executing getClients request");
        
        try {
            Client client = clientService.getClient(id);
            model.addAttribute("client", client);
        } catch (DataAccessException e) {
            errorLogger.error("error in ClientController", e);
            NotificationUtils.sendNotification(e);
            return "errorPage";
        }
        
        return "showClient";
    }
}

The objective of this controller consists in retrieving a client and returning a view showing its information but, as you can see, this code contains additional logic. In one hand, it handles exceptions that may be thrown by the service and it redirects to an error page. On the other hand, it generates logging information and notification sending in case of error. All this code is generic to all controllers in this application (and probably to other classes).

It’s true that we could have used the @ControllerAdvice annotation to centralize exception handling, but the target of this post is to see how to accomplish it with Spring AOP.

The same happens with the order controller. I won’t include it here because I don’t want to make the post unnecessary long. If you want to check it out, you can get the source code included in the previous link.

3.Checking services code

ClientService:

@Service("clientService")
public class ClientServiceImpl implements ClientService {
    @Autowired
    private ClientRepository clientRepository;
    private static Logger mainLogger = LoggerFactory.getLogger("generic");
    private static Logger monitorLogger = LoggerFactory.getLogger("monitoring");
    
    @Override
    @Transactional(readOnly = true)
    public Client getClient(int id) {
        mainLogger.debug("Accessing client service");
        long startTime = System.currentTimeMillis();
        Client client = clientRepository.getClient(id);
        long totalTime = System.currentTimeMillis() - startTime;
        monitorLogger.info("Invocation time {}ms ", totalTime);
        
        return client;
    }
}

In addition to the service invocation, it also contains logging generation and monitoring of the execution time in each invocation.

We could also use aspects to modularize transaction management if we needed to use programmatic transaction management, but it’s not the case in this example.

4.Data access layer

ClientRepositoryImpl:

@Repository
public class ClientRepositoryImpl implements ClientRepository {
    private JdbcTemplate template;
    private RowMapper<Client> rowMapper = new ClientRowMapper();
    private static final String SEARCH = "select * from clients where clientId = ?";
    private static final String COLUMN_ID = "clientId";
    private static final String COLUMN_NAME = "name";
    
    public ClientRepositoryImpl() {}
    
    public ClientRepositoryImpl(DataSource dataSource) {
        this.template = new JdbcTemplate(dataSource);
    }
    
    public Client getClient(int id) {
        return template.queryForObject(SEARCH, rowMapper, id);
    }
    
    private class ClientRowMapper implements RowMapper<Client> {
        public Client mapRow(ResultSet rs, int i) throws SQLException {
            Client client = new Client();
            client.setClientId(rs.getInt(COLUMN_ID));
            client.setName(rs.getString(COLUMN_NAME));
            
            return client;
        }
    }
}

This code does not contain any cross-cutting concern but I’ve included it to show all the sample application layers.

5.Activating AOP

To configure AOP, it is necessary to import the following dependencies:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>3.2.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.6.8</version>
</dependency>

In the Spring configuration file, we need to add the following tags:

<context:component-scan base-package="xpadro.spring.mvc.aop"/>
<aop:aspectj-autoproxy/>

The component-scan tag will search within the base package in order to find our aspects. To use auto scan, you not only need to define the aspect class with @Aspect annotation, but also you will need to include @Component annotation. If you don’t include @Component you will need to define the aspect in the xml configuration file.

6.Centralizing error handling

We’ll write an aspect with an @Around advice. This advice will intercept every method annotated with @RequestMapping annotation and will be responsible of invoking it, catching exceptions thrown by the service.

@Component
@Aspect
public class CentralExceptionHandler {
    private static Logger errorLogger = LoggerFactory.getLogger("errors");
    
    @Around("@annotation(org.springframework.web.bind.annotation.RequestMapping) && target(controller)")
    public String handleException(ProceedingJoinPoint jp, Object controller) throws Throwable {
        String view = null;
        
        try {
            view = (String) jp.proceed();
        } catch (DataAccessException e) {
            errorLogger.error("error in {}", controller.getClass().getSimpleName(), e);
            NotificationUtils.sendNotification(e);
            return "errorPage";
        }
        
        return view;
    }
}

The @Target annotation allows us to reference the intercepted class. Now we have the exception handling handled by the aspect so we can get rid of this logic in our controllers.

@Controller
public class ClientController {
    @Autowired
    private ClientService clientService;
    private static Logger mainLogger = LoggerFactory.getLogger("generic");
    //private static Logger errorLogger = LoggerFactory.getLogger("errors");
    
    @RequestMapping("/getClients")
    public String getClients(Model model, @RequestParam("id") int id) {
        mainLogger.debug("Executing getClients request");
        
        //try {
            Client client = clientService.getClient(id);
            model.addAttribute("client", client);
        //} catch (DataAccessException e) {
            //errorLogger.error("error in ClientController", e);
            //NotificationUtils.sendNotification(e);
            //return "errorPage";
        //}
        
        return "showClient";
    }	
}

Just a note, you could have intercepted exceptions thrown by the controller with the following advice:

@AfterThrowing(pointcut="@annotation(org.springframework.web.bind.annotation.RequestMapping)", throwing="e")

But be aware that this advice will not prevent the exception from propagating.

7.Centralizing logging

The logging aspect will have two advices, one for controller logging and another one for service logging:

@Aspect
@Component
public class CentralLoggingHandler {
    private static Logger mainLogger = LoggerFactory.getLogger("generic");
    
    @Before("@annotation(org.springframework.web.bind.annotation.RequestMapping) && @annotation(mapping)")
    public void logControllerAccess(RequestMapping mapping) {
        mainLogger.debug("Executing {} request", mapping.value()[0]);
    }
    
    @Before("execution(* xpadro.spring.mvc.*..*Service+.*(..)) && target(service)")
    public void logServiceAccess(Object service) {
        mainLogger.debug("Accessing {}", service.getClass().getSimpleName());
    }
}

8.Finally, the monitoring concern

We will write another aspect for monitoring concern. The advice is as follows:

@Aspect
@Component
public class CentralMonitoringHandler {
    private static Logger monitorLogger = LoggerFactory.getLogger("monitoring");
    
    @Around("execution(* xpadro.spring.mvc.*..*Service+.*(..)) && target(service)")
    public Object logServiceAccess(ProceedingJoinPoint jp, Object service) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = jp.proceed();
        long totalTime = System.currentTimeMillis() - startTime;
        monitorLogger.info("{}|Invocation time {}ms ", service.getClass().getSimpleName(), totalTime);
        
        return result;
    }
}

9.Checking the final code

After we have modularized all the cross-cutting concerns, our controllers and services contain only the business logic:

@Controller
public class ClientController {
    @Autowired
    private ClientService clientService;
    
    @RequestMapping("/getClients")
    public String getClients(Model model, @RequestParam("id") int id) {
    	Client client = clientService.getClient(id);
    	model.addAttribute("client", client);
    	
    	return "showClient";
    }	
}


@Service("clientService")
public class ClientServiceImpl implements ClientService {
    @Autowired
    private ClientRepository clientRepository;
    
    @Override
    @Transactional(readOnly = true)
    public Client getClient(int id) {
        return clientRepository.getClient(id);
    }
}

10.Conclusion

We have seen how to apply aspect oriented programming to keep our code clean and focused to the logic for which it was designed. Before using AOP, just take into account its known limitations.
 

Reference: Applying aspect oriented programming from our JCG partner Xavier Padro at the Xavier Padró’s Blog blog.
Related Whitepaper:

Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions

Get ready to program in a whole new way!

Functional Programming in Java will help you quickly get on top of the new, essential Java 8 language features and the functional style that will change and improve your code. This short, targeted book will help you make the paradigm shift from the old imperative way to a less error-prone, more elegant, and concise coding style that’s also a breeze to parallelize. You’ll explore the syntax and semantics of lambda expressions, method and constructor references, and functional interfaces. You’ll design and write applications better using the new standards in Java 8 and the JDK.

Get it Now!  

One Response to "Applying aspect oriented programming"

  1. David says:

    Hi Xavier,

    In my opinion, the title should be “Applying Spring AOP”. You could explain the @Around, @Before and so on annotations and pointcut patterns or sintaxis, couln’t you?

    Best regards,

    David

Leave a Reply


× four = 4



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books