天天看点

Logging and Spring AOP

Problem

Normally, we log down the values of parameters of a method and the value of return of a method. For example,

public class MyClass {

      public YYY getSomething(int id, Object obj) {

             log.debug ("para[id]="+id+",para[obj]="+obj);

             // do something, then return object YYY

             log.debug("return value : "+YYY);

             return YYY;

     }

}

We can log down the relative information with spring AOP. It is not necessary to log down something in each methods.

Configuration

prepare the spring configuration as follows.

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"

    xmlns:aop="http://www.springframework.org/schema/aop"

    xsi:schemaLocation="http://www.springframework.org/schema/beans

     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

     http://www.springframework.org/schema/context

     http://www.springframework.org/schema/context/spring-context-3.0.xsd

     http://www.springframework.org/schema/aop

     http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <context:component-scan base-package="com.spring.test" />

    <aop:aspectj-autoproxy />

    <bean id="personAopImpl" class="com.spring.test.aop.PersonAopImpl" />

    <bean id="personAopProxy" class="com.spring.test.aop.PersonAopProxy" />

</beans>

Reference Library

add the following reference library files to your project. you can get them from spring v3.0.2 dist package and its dependency package.

com.springsource.javax.annotation-1.0.0.jar

com.springsource.org.aopalliance-1.0.0.jar

com.springsource.org.apache.commons.logging-1.1.1.jar

com.springsource.org.aspectj.tools-1.6.6.RELEASE.jar

com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

org.springframework.aop-3.0.2.RELEASE.jar

org.springframework.asm-3.0.2.RELEASE.jar

org.springframework.beans-3.0.2.RELEASE.jar

org.springframework.context-3.0.2.RELEASE.jar

org.springframework.context.support-3.0.2.RELEASE.jar

org.springframework.core-3.0.2.RELEASE.jar

org.springframework.expression-3.0.2.RELEASE.jar

Source Code

// there are two POJO classes

package com.spring.test.aop;

import java.lang.reflect.Field;

import java.lang.reflect.Modifier;

import java.util.LinkedHashMap;

import java.util.Map;

public class Address {

    private static final long serialVersionUID = -872400794860227364L;

    private int floor;

    private String unit;

    public Address() {

        super();

    }

    public Address(int floor, String unit) {

        super();

        this.floor = floor;

        this.unit = unit;

    }

    public int getFloor() {

        return floor;

    }

    public void setFloor(int floor) {

        this.floor = floor;

    }

    public String getUnit() {

        return unit;

    }

    public void setUnit(String unit) {

        this.unit = unit;

    }

    public String toString() {

        Map<String, Object> map = new LinkedHashMap<String, Object>();

        try {

            Field[] fields = this.getClass().getDeclaredFields();

            if (fields != null) {

                for (Field f : fields) {

                    if (Modifier.isPrivate(f.getModifiers())

                            || Modifier.isProtected(f.getModifiers())) {

                        f.setAccessible(true);

                    } else {

                        continue;

                    }

                    map.put(f.getName(), f.get(this));

                }

            }

        } catch (Exception e) {

        }

        return map.toString();

    }

}

=====================================================

package com.spring.test.aop;

import java.lang.reflect.Field;

import java.lang.reflect.Modifier;

import java.util.LinkedHashMap;

import java.util.List;

import java.util.Map;

public class PersonInfo {

    private static final long serialVersionUID = 1552674486660927122L;

    private int id;

    private String name;

    private List<String> mobilePhone;

    private List<Address> addressList;

    public int getId() {

        return id;

    }

    public void setId(int id) {

        this.id = id;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    public List<String> getMobilePhone() {

        return mobilePhone;

    }

    public void setMobilePhone(List<String> mobilePhone) {

        this.mobilePhone = mobilePhone;

    }

    public List<Address> getAddressList() {

        return addressList;

    }

    public void setAddressList(List<Address> addressList) {

        this.addressList = addressList;

    }

    public String toString() {

        Map<String, Object> map = new LinkedHashMap<String, Object>();

        try {

            Field[] fields = this.getClass().getDeclaredFields();

            if (fields != null) {

                for (Field f : fields) {

                    if (Modifier.isPrivate(f.getModifiers())

                            || Modifier.isProtected(f.getModifiers())) {

                        f.setAccessible(true);

                    } else {

                        continue;

                    }

                    map.put(f.getName(), f.get(this));

                }

            }

        } catch (Exception e) {

        }

        return map.toString();

    }

}

=======================================================

// interface class

package com.spring.test.aop;

public interface IPersonAop {

    public void sayHello() throws Throwable;

    public void sayHello2() throws Throwable;

    public boolean createPersonInfo(PersonInfo info, int id);

    public PersonInfo getPersonInfo(int id);

}

=======================================================

// implement class

package com.spring.test.aop;

import java.util.ArrayList;

import java.util.List;

import org.springframework.stereotype.Service;

@Service

public class PersonAopImpl implements IPersonAop {

    @Override

    public void sayHello() throws Throwable {

        System.out.println("Hello !!");

        throw new Exception("throw out exception in sayHello().");

    }

    @Override

    public void sayHello2() throws Throwable {

        System.out.println("Hello2 !!");

    }

    @Override

    public boolean createPersonInfo(PersonInfo info, int id) {

        info.setId(id);

        System.out.println("creating person information " + info);

        return true;

    }

    @Override

    public PersonInfo getPersonInfo(int id) {

        Address addr1 = new Address(15, "-101");

        Address addr2 = new Address(19, "-204");

        List<Address> addr = new ArrayList<Address>();

        addr.add(addr1);

        addr.add(addr2);

        PersonInfo info = new PersonInfo();

        info.setAddressList(addr);

        info.setName("testname2");

        List<String> mobile = new ArrayList<String>();

        mobile.add("999999");

        mobile.add("888888");

        info.setMobilePhone(mobile);

        info.setId(id);

        return info;

    }

}

========================================================

// Junit class

package com.spring.test.junit;

import java.util.ArrayList;

import java.util.List;

import org.junit.AfterClass;

import org.junit.BeforeClass;

import org.junit.Test;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.spring.test.aop.Address;

import com.spring.test.aop.IPersonAop;

import com.spring.test.aop.PersonInfo;

public class AopTest {

    @BeforeClass

    public static void setUpBeforeClass() throws Exception {

    }

    @AfterClass

    public static void tearDownAfterClass() throws Exception {

    }

    @Test

    public void test() {

        ApplicationContext ctx = new ClassPathXmlApplicationContext(

                "spring.xml");

        IPersonAop aop = (IPersonAop) ctx.getBean("personAopImpl");

        try {

            aop.sayHello2();

            aop.sayHello();

        } catch (Throwable e) {

        }

        Address addr1 = new Address(12, "-01");

        Address addr2 = new Address(13, "-04");

        List<Address> addr = new ArrayList<Address>();

        addr.add(addr1);

        addr.add(addr2);

        PersonInfo info = new PersonInfo();

        info.setAddressList(addr);

        info.setName("testname");

        List<String> mobile = new ArrayList<String>();

        mobile.add("222222");

        mobile.add("333333");

        info.setMobilePhone(mobile);

        aop.createPersonInfo(info, 40);

        aop.getPersonInfo(18);

    }

}

=================================================================

// this is proxy class, it is important

package com.spring.test.aop;

import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.annotation.AfterReturning;

import org.aspectj.lang.annotation.AfterThrowing;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

@Aspect

public class PersonAopProxy {

    @Before("execution (* com.spring.test.aop..*.*(..))")

    public void logBeforeMethodRunning(JoinPoint joinPoint) {

        StringBuilder sb = new StringBuilder();

        sb.append("--- Begin to call "

                + joinPoint.getTarget().getClass().getName() + "."

                + joinPoint.getSignature().getName() + "() ");

        Object[] objs = joinPoint.getArgs();

        if (objs == null || objs.length == 0)

            sb.append("Without Parameter");

        else {

            int i = 0;

            while (i < objs.length) {

                sb.append(" para" + (i + 1) + "["

                        + objs[i].getClass().getSimpleName() + "]=" + objs[i]);

                i++;

            }

        }

        System.out.println(sb.toString());

    }

    @AfterReturning(pointcut = "execution (* com.spring.test.aop..*.*(..))", returning = "result")

    public void logAfterMethodRunning(JoinPoint joinPoint, Object result)

            throws Throwable {

        StringBuilder sb = new StringBuilder();

        sb.append("--- End to call "

                + joinPoint.getTarget().getClass().getName() + "."

                + joinPoint.getSignature().getName() + "() ");

        Object[] objs = joinPoint.getArgs();

        if (objs == null || objs.length == 0)

            sb.append("Without Parameter");

        else {

            int i = 0;

            while (i < objs.length) {

                sb.append(" para" + (i + 1) + "["

                        + objs[i].getClass().getSimpleName() + "]=" + objs[i]);

                i++;

            }

        }

        sb.append(" <return> " + result);

        System.out.println(sb.toString());

    }

    @AfterThrowing(pointcut = "execution (* com.spring.test.aop..*.*(..))", throwing = "e")

    public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {

        StringBuilder sb = new StringBuilder();

        sb.append("--- End to call "

                + joinPoint.getTarget().getClass().getName() + "."

                + joinPoint.getSignature().getName() + "() ");

        Object[] objs = joinPoint.getArgs();

        if (objs == null || objs.length == 0)

            sb.append("Without Parameter");

        else {

            int i = 0;

            while (i < objs.length) {

                sb.append(" para" + (i + 1) + "["

                        + objs[i].getClass().getSimpleName() + "]=" + objs[i]);

                i++;

            }

        }

        sb.append(" <Exception> " + e);

        System.out.println(sb.toString());

    }

}

Before Advices

To create a before advice to handle crosscutting concerns before particular program execution points,you use the @Before annotation and include the pointcut expression as the annotation value.

@Before("execution(* *.*(..))")

This pointcut expression (* com.spring.test.aop..*.*(..) matches the any method execution under package com.spring.test.aop and its sub-package.The preceding wildcard in this expression matches any modifier (public, protected, and private) and any return type. The two dots in the argument list match any number of arguments.

To register this aspect, you just declare a bean instance of it in the IoC container. The aspect bean may even be anonymous if there’s no reference from other beans.

<bean id="personAopProxy" class="com.spring.test.aop.PersonAopProxy" />

After Advices

An after advice is executed after a join point finishes, whenever it returns a result or throws an exception

abnormally.

@After("execution(* *.*(..))")

After Returning Advices

An after advice is executed regardless of whether a join point returns normally. If you want to perform logging only when a join point returns, you should replace the after advice with an after returning advice.

@AfterReturning("execution(* *.*(..))")

or

@AfterReturning(pointcut = "execution(* *.*(..))", returning = "result")

public void logAfterReturning(JoinPoint joinPoint, Object result)

After Throwing Advices

An after throwing advice is executed only when an exception is thrown by a join point.

@AfterThrowing("execution(* *.*(..))")

or

@AfterThrowing(pointcut = "execution(* *.*(..))", throwing = "e")

public void logAfterThrowing(JoinPoint joinPoint, Throwable e)

Around Advices

It is the most powerful of all the advice types. It gains full control of a join point, so you can combine all the actions of the preceding advices into one single advice. You can even control when, and whether, to proceed with the original join point execution.

The following around advice is the combination of the before, after returning, and after throwing advices you created before. Note that for an around advice, the argument type of the join point must be ProceedingJoinPoint. It’s a subinterface of JoinPoint that allows you to control when to proceed with the original join point.

@Around("execution(* *.*(..))")

public Object logAround(ProceedingJoinPoint joinPoint ) throws Throwable {

       log.info("The method " + joinPoint.getSignature().getName()

                   + "() begins with " + Arrays.toString(joinPoint.getArgs()));

       try {

                Object result = joinPoint.proceed();

                log.info("The method " + joinPoint.getSignature().getName()+ "() ends with " + result);

                return result;

        } catch (IllegalArgumentException e) {

                log.error("Illegal argument " + Arrays.toString(joinPoint.getArgs())

                              + " in "+joinPoint.getSignature().getName() + "()");

            throw e;

        }

}

Reference Book

Apress.Spring.Enterprise.Recipes.A.Problem.Solution.Approach, written by Gary Mak and Josh Long

继续阅读