Aop是Aspect-Oriented Programming(面向方面编程或面向切面编程)的简称。在Spring平台功能中,AOP是一个核心模块,Spring将AOP框架与IoC容器紧密集成,从而为使用AOP提供最大便利。
1、Spring AOP相关术语
- 通知(Advice)
在AOP中,切面的工作成为通知,通知定义了切面是什么以及何时使用。
Spring切面可以应用5种类型的通知:
Before—在方法调用前调用通知
After—在方法调用后调用通知,不论方法执行成功与否
After-returning—在方法调用成功执行后调用通知
After-throwing—在方法调用抛出异常后调用通知
Around—通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为
- 连接点(Joinpoint)
连接点是在应用执行过程中能够无插入切面的一个点。这个点可以是调用方法时、抛出异常时。切面代码可以插入到应用的正常流程中。
- 切点(Pointcut)
如果通知定义了切面“什么”和”何时“,那么切点就定义了”何处“。切点定义会匹配通知所要植入的一个或多个连接点。
- 切面(Aspect)
切面是通知和切点的结合。通知和切点共同定义了切面的全部内容——它是什么,在何时何地完成功能。
2、
Spring提供的4种实现AOP的方式:
- 经典的基于代理的AOP
- @AspectJ注解驱动的切面
- 纯POJO切面
- 注入式AspectJ切
纯POJO切面的配置AOP
1)首先实现一个简单的Spring Bean类,用于测试切面
2)然后实现一个待切入的切面实现类,该类中定义了将要切入的通知方法class AopTestClass{ public AopTestClass() { } public void testNormalAdvice( ) throws Exception { System.out.println(" Test normal advice!!!"); // throw new Exception("exception happen!!"); } public void testAroundAdvice(){ System.out.println(" Test around advice!!!"); } }
3)在Bean xml配置文件中,配置相关切面。注意:首先要引入aop命名空间class PrintAspectTrace{ public void before_(){ System.out.println("This before !"); } public void after_return_(){ System.out.println("This after_return_ !"); } public void after_throw_(Exception ex){ System.out.println("This after_throw_ !" +ex.getMessage()); } public void after_(){ System.out.println("This after_ !"); } public void around_(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("around --- before"); pjp.proceed(); System.out.println("around --- after"); } }
4)切面应用测试<bean id="testAop" class="com.xl.AopTestClass"/> <!-- 定义aop测试bean --> <bean id="traceAspect" class="com.xl.PrintAspectTrace" /> <!-- 定义aop切面bean --> <aop:config> <!-- 配置aop切面--> <aop:aspect ref="traceAspect"> <!-- 定义切点为AopTestClass类的testNormalAdvice方法--> <aop:pointcut id="pt" expression="execution(* com.xl.AopTestClass.testNormalAdvice(..) )"/> <!-- 配置aop前置通知--> <aop:before method="before_" pointcut-ref="pt" /> <aop:after-returning method="after_return_" pointcut-ref="pt" /> <aop:after-throwing method="after_throw_" throwing="ex" pointcut-ref="pt"/> <!-- 配置aop切面后置通知--> <aop:after method="after_" pointcut-ref="pt"/> </aop:aspect> <aop:aspect ref="traceAspect"> <aop:pointcut id="pt1" expression="execution(* com.xl.AopTestClass.testNormalAdvice(..) )"/> <!-- 配置aop切面环绕通知--> <aop:around method="around_" pointcut-ref="pt1"/> </aop:aspect> </aop:config>
5)结果AopTestClass aopTestClass = (AopTestClass) context.getBean("testAop"); try { aopTestClass.testNormalAdvice(); } catch (Exception e) { // e.printStackTrace(); }
This before !
around --- before
Test normal advice!!!
around --- after
This after_return_ !
This after_ !
AspectJ注解驱动的切面
1)同样的实现一个普通POJO bean,作为设置切面的目标
2)实现切面类,并且在该类中通过 @Aspect注解来声明切点以及通知class AopTestClass1{ public AopTestClass1() { } public void testNormalAdvice () throws Exception{ System.out.println(" Test normal advice!!!"); // testAroundAdvice(); // throw new Exception("EX HAPPENED!!") ; } public void testAroundAdvice(){ System.out.println(" Test around advice!!!"); } public void testNormalAdviceWithArgs(String normalArg){ System.out.println(" Test normal advice with arg!!!"+normalArg); } }
3) 在Bean xml配置文件中,配置相关bean,注意:需要声明 < aop :aspectj-autoproxy />,这样将会在Spring容器中添加自动AOP代理创建bean,并处理 @Aspect注解。@Aspect class PrintAspectTrace1{ @Pointcut("execution( * com.xl.AopTestClass1.testNormalAdvice())") public void test(){ } @Before(value = "test()") public void before_(){ System.out.println("This before_ !" ); } @AfterReturning( pointcut = "test()") public void after_return_(){ System.out.println("This after_return_ !"); } @AfterThrowing(pointcut = "test()" ,throwing ="ex" ) public void after_throw_(Exception ex){ System.out.println("This after_throw_ !" +ex.getMessage()); } @After("test()" ) public void after_(){ System.out.println("This after_ !"); } @Around("test()") public void around_(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("around --- before"); try{ pjp.proceed(); } catch ( Exception e){ // System.out.println("around --- catch exception " +e); throw e; } System.out.println("around --- after"); } @Pointcut("execution( * com.xl.AopTestClass1.testAroundAdvice())") public void test1(){ } @Before(value = "test1()") public void before_test_Around(){ System.out.println("This before_test_Around !" ); } } @Aspect class PrintAspectTrace2{ @Pointcut("execution( * com.xl.AopTestClass1.testNormalAdviceWithArgs(..))&&args(normalArg,..)&&target(bean)") public void test(Object bean,String normalArg){ } @Before("test(bean,normalArg)") public void before_(Object bean,String normalArg){ System.out.println("This before_ !" +normalArg +bean.toString()); } @Around("test(bean,normalArg)") public void around_(ProceedingJoinPoint pjp,Object bean,String normalArg) throws Throwable{ System.out.println("around --- before"+normalArg+bean.toString()); pjp.proceed(new Object[]{ normalArg }); System.out.println("around --- after"+normalArg+bean.toString()); // throw new Exception("exception happen!!") ; } }
<bean id="testAop" class="com.xl.AopTestClass1"/> <!-- 定义aop测试bean --> <aop:aspectj-autoproxy /> <bean class="com.xl.PrintAspectTrace1"/><!-- 任然需要定义切面bean --> <bean class="com.xl.PrintAspectTrace2" />
4)切面应用示例
AopTestClass1 aopTestClass = (AopTestClass1) context.getBean("aopTest"); try { aopTestClass.testNormalAdvice(); } catch (Exception e) { e.printStackTrace(); } aopTestClass.testAroundAdvice(); aopTestClass.testNormalAdviceWithArgs("ha ~ ha !");
5)结果
around --- before
This before_ !
Test normal advice!!!
around --- after
This after_ !
This after_return_ !
This test_Around !
Test around advice!!!
around --- beforeha ~ ha !com.xl.AopTestClass1@398e54e8
This before_ !ha ~ ha !com.xl.AopTestClass1@398e54e8
Test normal advice with arg!!!ha ~ ha !
around --- afterha ~ ha !com.xl.AopTestClass1@398e54e8