配置实现IOC功能时,我们采用了配置文件xml和注解两类方式实现。实现AOP功能时我们也可以使用两种方式。前面我们介绍了AOP基于注解的实现方式,本文我将采用基于配置文件的方式完成从原始对象bean、切面bean、切点及通知配置的方法。
用基于 XML 的配置声明切面
除了使用 AspectJ 注解声明切面, Spring 也支持在 Bean 配置文件中声明切面. 这种声明是通过 aop schema 中的 XML 元素完成的.
正常情况下, 基于注解的声明要优先于基于 XML 的声明. 通过 AspectJ 注解, 切面可以与 AspectJ 兼容, 而基于 XML 的配置则是 Spring 专有的. 由于 AspectJ 得到越来越多的 AOP 框架支持, 所以以注解风格编写的切面将会有更多重用的机会.
好,我们还是用例子说话。我们重新构建一个包,建立核心业务的类和切面类:
核心类:加减乘除计算
切面1:数据验证
切面2:输出日志
package com.happBKs.spring.aopbasic.aop2; public class AtithmeticCalculateImpl implements AtithmeticCalculate { public int add(int a, int b) { int result=a+b; return result; } public int sub(int a, int b) { int result=a-b; return result; } public int mul(int a, int b) { int result=a*b; return result; } public int div(int a, int b) { int result=a/b; return result; } }
package com.happBKs.spring.aopbasic.aop2; public class AtithmeticCalculateImpl implements AtithmeticCalculate { public int add(int a, int b) { int result=a+b; return result; } public int sub(int a, int b) { int result=a-b; return result; } public int mul(int a, int b) { int result=a*b; return result; } public int div(int a, int b) { int result=a/b; return result; } }
package com.happBKs.spring.aopbasic.aop2; import java.util.Arrays; import java.util.List; import org.aspectj.lang.JoinPoint; public class DataValidateAspect { public boolean beforeMethod(JoinPoint joinPoint) { String methodName=joinPoint.getSignature().getName(); List<Object> args=Arrays.asList(joinPoint.getArgs()); System.out.println("data validate---begin to "+methodName+" with "+args); if((Integer)(args.get(0))>0&&(Integer)(args.get(1))>0) { System.out.println("data is OK"); return true; } else { System.out.println("data is bad"); return false; } } }
package com.happBKs.spring.aopbasic.aop2; import java.util.Arrays; import java.util.List; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; public class LoggingAspect { /* * 在com.happBKs.spring.aopbasic.aop1.AtithmeticCalculate接口的每一个实现类的每一个方法开始之前执行 */ public void beforeMethod(JoinPoint joinPoint) { String methodName=joinPoint.getSignature().getName(); List<Object> args=Arrays.asList(joinPoint.getArgs()); System.out.println("begin to "+methodName+" with "+args); } /* * 在目标方法执行之后(无论是否发生异常),执行该通知 */ public void afterMethod(JoinPoint joinPoint) { String methodName=joinPoint.getSignature().getName(); System.out.println("end to "+methodName); } /* * 在目标方法正常执行之后执行的代码 * 返回通知是可以访问目标方法的返回值的 */ public void afterReturning(JoinPoint joinPoint, Object resultParam) { String methodName=joinPoint.getSignature().getName(); System.out.println("after "+methodName+" with returning result "+resultParam); } /* * 在目标方法出现异常时出现代码 * 可以访问到异常对象,且可以指定在特定异常时才执行代码 */ public void afterThrowing(JoinPoint joinPoint, Exception exThrowing) { String methodName=joinPoint.getSignature().getName(); System.out.println("after exception of "+methodName+" we find the exception is "+exThrowing); } /* * 回环通知需要携带ProceedingJoinPoint类型参数 * 回环通知类似于动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行目标方法 * 且回环通知必须有返回值,返回值即为目标方法的返回值 */ public Object roundingMethod(ProceedingJoinPoint pjp) { Object result=null; String methodName=pjp.getSignature().getName(); try { //前置通知 System.out.println("Around: Begin Method"+methodName+" executed with "+Arrays.asList(pjp.getArgs())); result= pjp.proceed(); //返回通知 System.out.println("Around: Return Method"+methodName+" with result"+result); } catch (Throwable e) { // TODO Auto-generated catch block e.printStackTrace(); //异常通知 System.out.println("Around: Exception in "+methodName+":"+e ); } //后置通知 System.out.println("Around: end Method"+methodName); return 100; } }
上面的这些类,我们不再需要加入各种注解,所有的切面、通知、切点等我们都在配置文件中进行配置:
新建配置文件applicationContext-xml.xml。(加入beans和aop命名空间)
<?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-4.1.xsd"> <!-- 配置bean --> <bean id="atithmeticCalculate" class="com.happBKs.spring.aopbasic.aop2.AtithmeticCalculateImpl"></bean> <!-- 配置切面bean --> <bean id="dataValidateAspect" class="com.happBKs.spring.aopbasic.aop2.DataValidateAspect"></bean> <bean id="loggingAspect" class="com.happBKs.spring.aopbasic.aop2.LoggingAspect"></bean> <!-- 配置AOP --> <aop:config> <!-- 配置切点表达式 --> <aop:pointcut id="pointCutData" expression="execution(* com.happBKs.spring.aopbasic.aop2.AtithmeticCalculateImpl.*(..))" /> <!-- 配置切面及通知 --> <aop:aspect ref="dataValidateAspect" order="2"> <aop:before method="beforeMethod" pointcut-ref="pointCutData" /> </aop:aspect> <aop:aspect ref="loggingAspect" order="1"> <aop:before method="beforeMethod" pointcut-ref="pointCutData" /> <aop:after method="afterMethod" pointcut-ref="pointCutData"/> <aop:after-throwing method="afterThrowing" pointcut-ref="pointCutData" throwing="exThrowing"/> <aop:after-returning method="afterReturning" pointcut-ref="pointCutData" returning="resultParam"/> <aop:around method="roundingMethod" pointcut-ref="pointCutData" /> </aop:aspect> </aop:config> </beans>
这里的步骤是:
1,首先把所有原始对象的bean配置好
2,然后把所有切面bean配置好,配置切面的bean 的id与切面类的映射。
3,配置aop
(1)配置切点:关系到哪些方法执行时会调用相关切面的通知。切点先配置好,一会儿留着引用。
(2)配置每一个切面:
a。配置切面bean的id与order切面优先级的映射,以及切面内的各个通知。
b。配置各个切面内部的通知与切点的映射,切点以引用方式给出。
测试代码:
package com.happBKs.spring.aopbasic; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.happBKs.spring.aopbasic.aop2.AtithmeticCalculate; public class TestSpringAOP2 { @Test public void testSpringAOP() { //1. 创建spring 的 IOC 容器 ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext-xml.xml"); //2. 从IOC容器获取bean实例 AtithmeticCalculate atithmeticCalculate = (AtithmeticCalculate)ac.getBean(AtithmeticCalculate.class); //考察一下代理对象是否生成 System.out.println(atithmeticCalculate.getClass().getName()); //3. 使用bean System.out.println("Example 1:"); int result=atithmeticCalculate.add(10, 5); System.out.println(result); System.out.println("\r\nExample 2:"); int result2=atithmeticCalculate.div(10, 0); System.out.println(result2); } }
运行结果:
com.sun.proxy.$Proxy4
Example 1:
begin to add with [10, 5]
Around: Begin Methodadd executed with [10, 5]
data validate---begin to add with [10, 5]
data is OK
Around: Return Methodadd with result15
Around: end Methodadd
after add with returning result 100
end to add
100
Example 2:
begin to div with [10, 0]
Around: Begin Methoddiv executed with [10, 0]
data validate---begin to div with [10, 0]
data is bad
java.lang.ArithmeticException: / by zero
at com.happBKs.spring.aopbasic.aop2.AtithmeticCalculateImpl.div(AtithmeticCalculateImpl.java:24)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85)
at com.happBKs.spring.aopbasic.aop2.LoggingAspect.roundingMethod(LoggingAspect.java:76)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:68)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:52)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:58)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:43)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy4.div(Unknown Source)
at com.happBKs.spring.aopbasic.TestSpringAOP2.testSpringAOP(TestSpringAOP2.java:24)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Around: Exception in div:java.lang.ArithmeticException: / by zero
Around: end Methoddiv
after div with returning result 100
end to div
100