Michal Vrtiak

About Michal Vrtiak

Michal is a freelancer currently located in Prague, Czech Republic with huge passion for Java platform. He is very enthusiastic about Dependency Injection, IntelliJ IDEA and loves to use both Spring and Java EE.

Spring – Adding AOP support

I heard a story about one senior (and quite highly paid) softwaree engineer. He was given task to log every method in every controller in project he was working on. Engineer rewrote all controller methods, so from code like this:

    @RequestMapping(method = RequestMethod.GET)
    public String showEmployees(Model model) {
        List<Employee> employees = employeeDao.list();
        model.addAttribute('employees', employees);

        return 'employees/list';
    }

he made following code:

    @RequestMapping(method = RequestMethod.GET)
    public String showEmployees(Model model) {
 LOGGER.log('Invoking method showEmployees');

        List<Employee> employees = employeeDao.list();
        model.addAttribute('employees', employees);

 LOGGER.log('Returning from method showEmployees');
        return 'employees/list';
    }

What’s wrong with this code? Well:

  • It takes lot of time to alter every method with such code
  • It is error prone – you can introduce typos or forget to add logging somewhere
  • It is mixing cross-cutting concerns. That means you are adding same kind of repetetive, boilerplate and unrelated code to places where it doesn’t belong.
  • For example, what is the responsibility of showEmployees method? It is invoking service, getting employees and putting them to model. Logging really isn’t it’s responsibility, so why to mix those concerns?

    If engineer I mentioned knew about Aspect Oriented Programming he would save lot of time and made code better and more readable. Spring supports something called “Aspects” that are made exactly for such a problems. Aspects allow us to define common functionality in one place. Before we write any code, there is some terminology to understand. This terminology is quite huge and I am not going to write it here, but I encourage you to read Spring’s official reference page on AOP if you wish to know more. You should at least understand what is Advice, Join Point, Pointcut, Aspect and Weaving.

    OK let’s add Aspect for logging controller methods, exactly what should’ve done engineer from the story in the beginning.

    We must first add dependencies to pom.xml on AspectJ library:

            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjrt</artifactId>
                <version>1.6.11</version>
            </dependency>
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjtools</artifactId>
                <version>1.6.11</version>
            </dependency>

    Also check if you have dependency on Spring’s AOP (but if you follow this tutorial from the very beginning you already have it):

      <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-aop</artifactId>
       <version>3.1.0.RELEASE</version>
      </dependency>

    Now let’s write Aspect’s code. Create package org.timesheet.aspects and add ControllerLoggingAspect class:

    package org.timesheet.aspects;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    
    import java.util.Arrays;
    
    /**
     * Will log every invokation of @RequestMapping annotated methods
     * in @Controller annotated beans.
     */
    @Aspect
    public class ControllerLoggingAspect {
    
        @Pointcut('within(@org.springframework.stereotype.Controller *)')
        public void controller() {}
    
        @Pointcut('execution(* *(..))')
        public void methodPointcut() {}
    
        @Pointcut('within(@org.springframework.web.bind.annotation.RequestMapping *)')
        public void requestMapping() {}
    
        @Before('controller() && methodPointcut() && requestMapping()')
        public void aroundControllerMethod(JoinPoint joinPoint) throws Throwable {
            System.out.println('Invoked: ' + niceName(joinPoint));
        }
    
        @AfterReturning('controller() && methodPointcut() && requestMapping()')
        public void afterControllerMethod(JoinPoint joinPoint) {
            System.out.println('Finished: ' + niceName(joinPoint));
        }
    
        private String niceName(JoinPoint joinPoint) {
            return joinPoint.getTarget().getClass()
                    + '#' + joinPoint.getSignature().getName()
                    + '\n\targs:' + Arrays.toString(joinPoint.getArgs());
        }
    
    }

    This code says, that @Before and @AfterReturning from controller method we will log information about it’s invokation (name and arguments). This advices execute when all three pointcuts are matching. controller() pointcut marks matching join point (that matches stereotype Controller) at which advice should be woven. methodPointcut() marks that we’re dealing with method call and requestMapping() pointcut marks methods annotated with @RequestMapping.

    To make it work, we’ll add aop.xml Spring configuration file under src/main/resources:

    <?xml version='1.0' encoding='UTF-8'?>
    <beans xmlns='http://www.springframework.org/schema/beans'
           xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:aop='http://www.springframework.org/schema/aop'
           xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd'>
    
        <!-- AOP support -->
        <bean id='controllerAspect' class='org.timesheet.aspects.ControllerLoggingAspect' />
        <aop:aspectj-autoproxy>
            <aop:include name='controllerAspect' />
        </aop:aspectj-autoproxy>
    
    </beans>

    And then we’ll import it in timesheet-servlet.xml Spring config:

    <import resource='classpath:aop.xml' />

    This was the last part of tutorial. I hope you have now better understanding of what Spring is and how does it help to solve your problems. Remember that we’ve covered only tiny piece of Spring in this tutorial. There is still much more to explore!

    Reference: Part 6 – Adding AOP support from our JCG partner Michal Vrtiak at the vrtoonjava 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 "Spring – Adding AOP support"

    1. Gaurav says:

      Hi I tried the same approach but it seems to give me a thread dump everytime . I am trying to wire up aop via spring mvc configuration annotation wise like below

      @Configuration
      @EnableWebMvc
      @EnableAspectJAutoProxy
      @ComponentScan({“com.pumpkinsafari.api”})
      public class WebConfig extends WebMvcConfigurerAdapter {

      /** The Constant DD_MM_YYYY. */
      private static final String DD_MM_YYYY = “yyyy-MM-dd”;

      /** The Constant DATE_FORMAT. */
      private static final DateFormat DATE_FORMAT = new SimpleDateFormat(DD_MM_YYYY);

      /**
      * Instantiates a new web config.
      */
      public WebConfig() {
      super();
      }

      @Bean
      public RestControllerAspect controllerAspect(){
      return new RestControllerAspect();
      }

      // beans

      /**
      * Xstream marshaller.
      *
      * @return the x stream marshaller
      */
      public XStreamMarshaller xstreamMarshaller() {
      final XStreamMarshaller xStreamMarshaller = new XStreamMarshaller();
      xStreamMarshaller.setAutodetectAnnotations(true);
      xStreamMarshaller.setAnnotatedClasses(new Class[] { Principal.class, Customer.class, Role.class,
      Privilege.class, SocialUser.class, SearchRequest.class });
      xStreamMarshaller.getXStream().addDefaultImplementation(java.sql.Timestamp.class, java.util.Date.class);

      return xStreamMarshaller;
      }

      /**
      * Marshalling http message converter.
      *
      * @return the marshalling http message converter
      */
      public MarshallingHttpMessageConverter marshallingHttpMessageConverter() {
      final MarshallingHttpMessageConverter marshallingHttpMessageConverter = new MarshallingHttpMessageConverter();
      final XStreamMarshaller xstreamMarshaller = xstreamMarshaller();
      marshallingHttpMessageConverter.setMarshaller(xstreamMarshaller);
      marshallingHttpMessageConverter.setUnmarshaller(xstreamMarshaller);

      return marshallingHttpMessageConverter;
      }

      // template

      /*
      * (non-Javadoc)
      *
      * @see
      * org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
      * #configureMessageConverters(java.util.List)
      */
      @Override
      public void configureMessageConverters(final List<HttpMessageConverter> messageConverters) {
      messageConverters.add(marshallingHttpMessageConverter());

      final ClassLoader classLoader = getClass().getClassLoader();
      if (ClassUtils.isPresent(“com.fasterxml.jackson.databind.ObjectMapper”, classLoader)) {
      MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
      jackson2HttpMessageConverter.getObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
      // Register date format for marshalling unmarshalling dates
      jackson2HttpMessageConverter.getObjectMapper().setDateFormat(DATE_FORMAT);
      messageConverters.add(jackson2HttpMessageConverter);
      } else if (ClassUtils.isPresent(“org.codehaus.jackson.map.ObjectMapper”, classLoader)) {
      MappingJacksonHttpMessageConverter jacksonHttpMessageConverter = new MappingJacksonHttpMessageConverter();
      jacksonHttpMessageConverter.getObjectMapper().disable(
      DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES);
      // Register date format for marshalling unmarshalling dates
      jacksonHttpMessageConverter.getObjectMapper().setDateFormat(DATE_FORMAT);
      messageConverters.add(jacksonHttpMessageConverter);
      }

      super.configureMessageConverters(messageConverters);
      }

      }

      can u help

    Leave a Reply


    6 × six =



    Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use
    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

    15,153 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
    Get tutored by the Geeks! JCG Academy is a fact... Join Now
    Hello. Add your message here.