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 }
Aspect注解方式

当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的主要内容,如果未能解决你的问题,请参考以下文章

JAVA之AOP

Spring AOP

Spring源码高级笔记之——Spring AOP应用

2018.12.24 Spring中的aop演示

Spring框架 AOP

Spring的AOP面向切面编程