鲁春利的工作笔记,好记性不如烂笔头
基于XML配置方式声明切面
基于Schema的AOP从Spring2.0之后通过“aop”命名空间来定义切面、切入点及声明通知。
前面我们用到了<aop:aspectj-autoproxy/>标签,Spring在aop的命名空间里面还提供了其他的配置元素:
<aop:advisor> 定义一个AOP通知者<aop:after> 后置通知
<aop:after-returning> 返回通知
<aop:after-throwing> 异常通知
<aop:around> 环绕通知
<aop:aspect> 定义一个切面
<aop:before> 前置通知
<aop:config> AOP*配置元素,其他元素都位于该元素之下
<aop:pointcut> 定义一个切点,可重用
接口
package com.invicme.apps.aop;/** * * @author lucl * * 数学计算接口类 * */public interface ArithmeticCalculate { public int add (int i, int j); public int div (int i, int j); public String validateNum (String level, int i);}
实现类
package com.invicme.apps.aop.schema;import org.apache.log4j.Logger;import com.invicme.apps.aop.ArithmeticCalculate;/** * * @author lucl * * 数学计算实现类 * */public class ArithmeticCalculateImpl implements ArithmeticCalculate { private static final Logger logger = Logger.getLogger(ArithmeticCalculateImpl.class); private int i = 0; private int j = 0; public ArithmeticCalculateImpl () { this(0, 0); } public ArithmeticCalculateImpl (int i, int j) { this.i = i; this.j = j; } @Override public int add(int i, int j) { logger.info("The method add was invoke with args [" + i + ", " + j + "]"); int sum = i + j; logger.info("The method add ends with result [" + sum + "]"); return sum; } @Override public int div(int i, int j) { logger.info("The method div was invoke with args [" + i + ", " + j + "]"); int result = i / j; logger.info("The method div ends with result [" + result + "]"); return result; } @Override public String validateNum(String level, int i) { logger.info("The method validateNum was invoke with args [" + level + ", " + i + "]"); String result = this.getMsg(i); logger.info("The method validateNum ends with result [" + result + "]"); return result; } private String getMsg (int i) { if (i > 0) { return "正数"; } return "负数"; }}
切面POJO
package com.invicme.apps.aop.schema;import java.util.Arrays;import java.util.List;import org.apache.log4j.Logger;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut;/** * * @author lucl * */public class LogAdapter { private static final Logger logger = Logger.getLogger(LogAdapter.class); /** * Pointcut * 定义Pointcut,Pointcut的名称为poincutMethod(),此方法没有返回值和参数 * 该方法就是一个标识,不进行调用 */ public void poincutMethod () { // ...... } /** * 前置通知,在目标方法执行之前被调用(JoinPoint参数不是必须的,传入是为了获取目标对象的相关属性) */ public void beforeAdvice (JoinPoint joinPoint) { // 目标对象 Object target = joinPoint.getTarget(); // 目标方法 String methodName = joinPoint.getSignature().getName(); // 方法参数 List<Object> asList = Arrays.asList(joinPoint.getArgs()); /** * target.getClass().getName() : 获取的是全路径名(包名+类名) * target.getClass().getSimpleName() : 获取类名 */ logger.info("[<aop:before>]" + target.getClass().getSimpleName() + "@" + methodName + " was invoke with args " + asList + "."); } /** * 后置通知(在目标方法执行之后被执行,无论该方法是否抛出异常) */ public void afterAdvice (JoinPoint joinPoint) { // 目标对象 Object target = joinPoint.getTarget(); // 目标方法 String methodName = joinPoint.getSignature().getName(); logger.info("[<aop:after>]" + target.getClass().getSimpleName() + "@" + methodName + " ends."); } /** * 返回通知(在方法正常执行后执行,若出现异常不会被执行) * 返回通知可以获取到目标方法的返回值 */ public void afterReturningAdvice (JoinPoint joinPoint, Object result) { // 目标对象 Object target = joinPoint.getTarget(); // 目标方法 String methodName = joinPoint.getSignature().getName(); logger.info("[<aop:after-returning>]" + target.getClass().getSimpleName() + "@" + methodName + " ends with result " + result + "."); } /** * 异常通知(当目标方法出现异常时会被执行,可以访问到异常,也可以通过指定异常类型, * 如Exception ex,也可以为NullPointerException ex则只有空指针异常才会被执行) */ public void afterThrowingAdvice (JoinPoint joinPoint, Exception ex) { // 目标对象 Object target = joinPoint.getTarget(); // 目标方法 String methodName = joinPoint.getSignature().getName(); logger.info("[<aop:after-throwing>]" + target.getClass().getSimpleName() + "@" + methodName + " occurs exception : " + ex + "."); } /** * 环绕通知 * 说明: * 环绕通知需要携带ProceedingJoinPoint类型的参数 * 环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型参数可以决定是否执行目标方法 * 环绕通知必须有返回值,且返回值为目标方法的返回值 */ public Object aroundAdvice (ProceedingJoinPoint joinPoint) { // 目标对象 Object target = joinPoint.getTarget(); // 目标方法 String methodName = joinPoint.getSignature().getName(); // 参数 List<Object> asList = Arrays.asList(joinPoint.getArgs()); // Object result = null; // 执行目标方法 try { // 前置通知 result = joinPoint.proceed(); // 返回通知 } catch (Throwable e) { throw new RuntimeException(e); // 异常通知 // 或者 // 直接throw出去(否则程序会执行到最后的return result,而result为null,目标方法可能需要类型转换,当试图将null转化为特定类型时,出错) } // 后置通知 logger.info("[<aop:around>]" + target.getClass().getSimpleName() + "@" + methodName + " was invoke with args " + asList + ", ends with " + result + " ."); // 注意这里的result实际上是目标方法的返回值,如果出现问题返回值不匹配会出现错误 return result; }}
Spring配置文件spring-context-aop-schema.xml
<?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 业务类 --> <bean id="calculate" class="com.invicme.apps.aop.schema.ArithmeticCalculateImpl" /> <!-- 切入的日志类 --> <bean id="logAdapter" class="com.invicme.apps.aop.schema.LogAdapter" /> <!-- proxy-target-class true : 表示声明的切面使用CGLib动态代理技术; false : 默认值,使用JDK动态代理技术。 一个配置文件可以有多个<aop:config>,不同的<aop:config>采用不同的代理技术。 --> <aop:config proxy-target-class="false"> <!-- 声明一个切点 --> <aop:pointcut id="logPointcut" expression="execution(public * com.invicme.apps.aop.schema.ArithmeticCalculateImpl.*(..))"/> <!-- 定义切面 --> <aop:aspect id="logAspect" ref="logAdapter"> <!-- 在<aop:aspect>中也可以定义<aop:pointcut>,但建议在外层定义,在通知处引用 --> <!-- <aop:before> pointcut和pointcut-ref:二者选一,指定切入点; method:指定前置通知实现方法名,如果是多态需要加上参数类型,多个用“,”隔开,如beforeAdvice(java.lang.String); arg-names:指定通知实现方法的参数名字,多个用“,”分隔,可选;如arg-names="param"表示beforeAdvice(String param)。 --> <aop:before method="beforeAdvice" pointcut-ref="logPointcut"/> <aop:after method="afterAdvice" pointcut-ref="logPointcut"/> <aop:after-returning method="afterReturningAdvice" pointcut-ref="logPointcut" returning="result"/> <aop:after-throwing method="afterThrowingAdvice" pointcut-ref="logPointcut" throwing="ex"/> <aop:around method="aroundAdvice" pointcut-ref="logPointcut"/> </aop:aspect> </aop:config></beans>
测试类
package com.test.apps.spring.aop;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import com.invicme.apps.aop.ArithmeticCalculate;/** * * @author lucl * */public class TestSpringAopBySchema { @Test public void testSpringAopBySchema() { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/spring-context-aop-schema.xml"); ArithmeticCalculate calculate = context.getBean("calculate", ArithmeticCalculate.class); calculate.add(1, 2); System.out.println("----------------------------------------------------------------"); try { Thread.sleep(1 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } calculate.div(1, 0); }}
运行结果:
本文出自 “闷葫芦的世界” 博客,请务必保留此出处http://luchunli.blog.51cto.com/2368057/1812343