Spring AOP
Posted 呦,你是个老母猪呦!
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring 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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="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-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd"> <bean id="studentManagerImpl" class="com.zhangpn.serviceImpl.StudentManagerImpl"></bean>这是一个需要被切入的类 <bean id="sessionManager" class="com.zhangpn.aspect.SessionManager"></bean>这是一个切面 <aop:config> <aop:aspect id="sessionManager" ref="sessionManager">使sessionManager实例作为一个切面
<aop:pointcut expression="execution(* com.zhangpn.serviceImpl.*.addStudent(..))" id="sessionManagerPoint"/>
<aop:before method="openSession" pointcut-ref="sessionManagerPoint"/>
<aop:after-returning method="commitSession" pointcut-ref="sessionManagerPoint"/>
<aop:after-throwing method="roolbackSession" pointcut-ref="sessionManagerPoint"/>
<aop:after method="closeSession" pointcut-ref="sessionManagerPoint"/>
</aop:aspect>
</aop:config>
</beans>
1 package com.zhangpn.aspect; 2 3 import org.aspectj.lang.ProceedingJoinPoint; 4 import org.aspectj.lang.annotation.After; 5 import org.aspectj.lang.annotation.AfterReturning; 6 import org.aspectj.lang.annotation.AfterThrowing; 7 import org.aspectj.lang.annotation.Around; 8 import org.aspectj.lang.annotation.Aspect; 9 import org.aspectj.lang.annotation.Before; 10 11 @Aspect 12 public class SessionManager { 13 14 @Around("execution(* com.zhangpn.serviceImpl.StudentManagerImpl.*(..))") 15 public void aroundSession(ProceedingJoinPoint proceedingJoinPoint) { 16 System.out.println("<<<<<<<<<<<aroundSession before>>>>>>>>>>>"); 17 try { 18 proceedingJoinPoint.proceed(); 19 } catch (Throwable e) { 20 System.out.println("<<<<<<<<<<<aroundSession Throwable>>>>>>>>>>>"); 21 e.printStackTrace(); 22 } 23 System.out.println("<<<<<<<<<<<aroundSession after>>>>>>>>>>>"); 24 } 25 26 @Before("execution(* com.zhangpn.serviceImpl.StudentManagerImpl.*(..))") 27 public void openSession() { 28 System.out.println("<<<<<<<<<<<openSession>>>>>>>>>>>"); 29 } 30 31 @AfterReturning("execution(* com.zhangpn.serviceImpl.StudentManagerImpl.*(..))") 32 public void commitSession() { 33 System.out.println("<<<<<<<<<<<commitSession>>>>>>>>>>>"); 34 } 35 36 @AfterThrowing("execution(* com.zhangpn.serviceImpl.StudentManagerImpl.*(..))") 37 public void roolbackSession() { 38 System.out.println("<<<<<<<<<<<roolbackSession>>>>>>>>>>>"); 39 } 40 41 @After("execution(* com.zhangpn.serviceImpl.StudentManagerImpl.*(..))") 42 public void closeSession() { 43 System.out.println("<<<<<<<<<<<closeSession>>>>>>>>>>>"); 44 } 45 46 }
当around与其他一起使用时,执行顺序是:
<<<<<<<<<<<环绕通知 before>>>>>>>>>>>
<<<<<<<<<<<前置通知>>>>>>>>>>>
addStudent
<<<<<<<<<<<环绕通知 after>>>>>>>>>>>
<<<<<<<<<<<后置通知>>>>>>>>>>>
<<<<<<<<<<<方法成功返回执行>>>>>>>>>>>
当方法有异常抛出时:
六月 14, 2018 7:41:59 上午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh 信息: Refreshing org.sp[email protected]c5a9e: startup date [Thu Jun 14 07:41:59 CST 2018]; root of context hierarchy 六月 14, 2018 7:41:59 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [applicationContext.xml] <<<<<<<<<<<前置通知>>>>>>>>>>> Exception in thread "main" addStudent <<<<<<<<<<<后置通知>>>>>>>>>>> <<<<<<<<<<<异常通知>>>>>>>>>>> java.lang.Exception at com.zhangpn.serviceImpl.StudentManagerImpl.addStudent(StudentManagerImpl.java:10) 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:302) 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.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:43) 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.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:208) at com.sun.proxy.$Proxy8.addStudent(Unknown Source) at com.zhangpn.test.Test.main(Test.java:14)
前置通知 ---> 方法 ---> 后置通知 ---> 异常通知
当没有异常发生时:
六月 14, 2018 7:45:19 上午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh 信息: Refreshing org[email protected]1a6c5a9e: startup date [Thu Jun 14 07:45:19 CST 2018]; root of context hierarchy 六月 14, 2018 7:45:19 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [applicationContext.xml] <<<<<<<<<<<前置通知>>>>>>>>>>> addStudent <<<<<<<<<<<后置通知>>>>>>>>>>> <<<<<<<<<<<方法成功返回执行>>>>>>>>>>>
前置通知 ---> 方法 ---> 后置通知 ---> 成功执行后通知
可以看出:
初学者一直认为是:
try { // 执行前置通知; // 执行目标方法; // 执行返回通知; } catche(Exception e) { // 执行异常通知; } finally { // 执行后置通知; }
其实,并不是这样,正确的流程应该是:
try{ try{ //@Before method.invoke(..); }finally{ //@After } //@AfterReturning }catch(){ //@AfterThrowing }
先执行 前置通知,此时判断方法是否抛出异常,不管是否抛出异常,接下来都要继续执行后置通知,然后再根据是否异常决定是执行方法成功执行后通知还是异常通知。
只有了解了执行的顺序,才能更好的学好Spring AOP。
当使用环绕通知时,如果方法出现了异常:
@Around("execution(* com.zhangpn.serviceImpl.StudentManagerImpl.*(..))") public void aroundSession(ProceedingJoinPoint proceedingJoinPoint) { System.out.println("<<<<<<<<<<<环绕通知 before>>>>>>>>>>>"); try { proceedingJoinPoint.proceed(); System.out.println("<<<<<<<<<<<环绕通知 成功执行>>>>>>>>>>>"); } catch (Throwable e) { System.out.println("<<<<<<<<<<<环绕通知 Throwable>>>>>>>>>>>"); e.printStackTrace(); } System.out.println("<<<<<<<<<<<环绕通知 after>>>>>>>>>>>"); }
<<<<<<<<<<<环绕通知 before>>>>>>>>>>> addStudent <<<<<<<<<<<环绕通知 Throwable>>>>>>>>>>> java.lang.Exception at com.zhangpn.serviceImpl.StudentManagerImpl.addStudent(StudentManagerImpl.java:10) 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:302) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85) at com.zhangpn.aspect.SessionManager.aroundSession(SessionManager.java:18) 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.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:208) at com.sun.proxy.$Proxy5.addStudent(Unknown Source) at com.zhangpn.test.Test.main(Test.java:14) <<<<<<<<<<<环绕通知 after>>>>>>>>>>>
当没有抛出异常:
<<<<<<<<<<<环绕通知 before>>>>>>>>>>>
addStudent
<<<<<<<<<<<环绕通知 成功执行>>>>>>>>>>>
<<<<<<<<<<<环绕通知 after>>>>>>>>>>>
此时的执行就是按照环绕通知内try{}catch{}的顺序执行了此时才是:
try { // 执行前置通知; // 执行目标方法; // 执行返回通知; } catche(Exception e) { // 执行异常通知; } finally { // 执行后置通知; }
只不过,这里的前置通知、执行返回通知、异常通知、后置通知都是直接写入代码即可,不需要像before、after那样使用xml配置或者注解。
其实,可以看出,环绕通知很适合做事务管理。
执行目标方法前,打开session,完后执行目标方法,再之后,根据方法的执行是否顺利决定是提交事务还是回滚事务,最后关闭连接,是吧?
@Aspect public class SessionManager { @Pointcut("execution(* com.zhangpn.serviceImpl.StudentManagerImpl.*(..))") public void getExecution() { } @Before("getExecution()") public void openSession() { System.out.println("<<<<<<<<<<<前置通知>>>>>>>>>>>"); } @AfterReturning("getExecution()") public void commitSession() { System.out.println("<<<<<<<<<<<方法成功返回执行>>>>>>>>>>>"); } @AfterThrowing("getExecution()") public void roolbackSession() { System.out.println("<<<<<<<<<<<异常通知>>>>>>>>>>>"); } @After("getExecution()") public void closeSession() { System.out.println("<<<<<<<<<<<后置通知>>>>>>>>>>>"); } }
也可以和XML一样配置一个pointcut,这时,直接调用就会节省很多重复代码。
以上是关于Spring AOP的主要内容,如果未能解决你的问题,请参考以下文章