zoukankan      html  css  js  c++  java
  • Spring AOP四种实现方式Demo详解与相关知识探究

    一、前言

    在网络上看到一篇博客Spring实现AOP的4种方式,博主写的很通俗易懂,但排版实在抓狂,对于我这么一个对排版、代码格式有强迫症的人来说,实在是不能忍受~~~~(>_<)~~~~。

    我亲手实现了一遍,重新整理,加上了一些不易关注到的细节、漏掉的知识,以及自己对AOP的一些理解,写成这篇博客。

    二、AOP相关概念

    (1)AOP是什么?AOP与拦截器的区别?

    太抽象的不说,如果你知道Struts2的拦截器,拦截器就是应用的AOP的思想,它用于拦截Action以进行一些预处理或结果处理。而Spring的AOP是一种更通用的模式,可以拦截Spring管理的Bean,功能更强大,适用范围也更广,它是通过动态代理与反射机制实现的。(更详细的解释可参看博客 http://blog.csdn.net/zhangliangzi/article/details/51648032 )

    (2)使用AOP需要的一些概念。

    1.通知(Advice)

    通知定义了在切入点代码执行时间点附近需要做的工作。

    Spring支持五种类型的通知:

    Before(前)  org.apringframework.aop.MethodBeforeAdvice
    After-returning(返回后) org.springframework.aop.AfterReturningAdvice
    After-throwing(抛出后) org.springframework.aop.ThrowsAdvice
    Arround(周围) org.aopaliance.intercept.MethodInterceptor
    Introduction(引入) org.springframework.aop.IntroductionInterceptor

    2.连接点(Joinpoint)

    程序能够应用通知的一个“时机”,这些“时机”就是连接点,例如方法调用时、异常抛出时、方法返回后等等。

    3.切入点(Pointcut)

    通知定义了切面要发生的“故事”,连接点定义了“故事”发生的时机,那么切入点就定义了“故事”发生的地点,例如某个类或方法的名称,Spring中允许我们方便的用正则表达式来指定。

    4.切面(Aspect)

    通知、连接点、切入点共同组成了切面:时间、地点和要发生的“故事”。

    5.引入(Introduction)

    引入允许我们向现有的类添加新的方法和属性(Spring提供了一个方法注入的功能)。

    6.目标(Target)

    即被通知的对象,如果没有AOP,那么通知的逻辑就要写在目标对象中,有了AOP之后它可以只关注自己要做的事,解耦合!

    7.代理(proxy)

    应用通知的对象,详细内容参见设计模式里面的动态代理模式。

    8.织入(Weaving)

    把切面应用到目标对象来创建新的代理对象的过程,织入一般发生在如下几个时机:

    (1)编译时:当一个类文件被编译时进行织入,这需要特殊的编译器才可以做的到,例如AspectJ的织入编译器;

    (2)类加载时:使用特殊的ClassLoader在目标类被加载到程序之前增强类的字节代码;

    (3)运行时:切面在运行的某个时刻被织入,SpringAOP就是以这种方式织入切面的,原理应该是使用了JDK的动态代理技术。

    三、使用AOP的几种方式

    1.经典的基于代理的AOP

    2.@AspectJ注解驱动的切面

    3.纯POJO切面

    4.注入式AspectJ切面

    四、Demo详解

    在讲Demo之前,先把项目结构贴一下,我用的的一般的Java Project+Maven进行测试,用Web Project的小区别一会会说到。有一点很重要,jar依赖必须导入正确,我在测试过程中,很多bug都是因为依赖问题引起的,这里也贴一下。

    包结构:

    pom.xml:

    [html] view plain copy
     
    1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    2.     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
    3.     <modelVersion>4.0.0</modelVersion>  
    4.   
    5.     <groupId>com.springAOP</groupId>  
    6.     <artifactId>springAOP</artifactId>  
    7.     <version>0.0.1-SNAPSHOT</version>  
    8.     <packaging>jar</packaging>  
    9.   
    10.     <name>springAOP</name>  
    11.     <url>http://maven.apache.org</url>  
    12.   
    13.     <properties>  
    14.         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
    15.         <org.springframework.version>3.0.5.RELEASE</org.springframework.version>  
    16.     </properties>  
    17.   
    18.   
    19.     <dependencies>  
    20.   
    21.         <dependency>  
    22.             <groupId>junit</groupId>  
    23.             <artifactId>junit</artifactId>  
    24.             <version>3.8.1</version>  
    25.             <scope>test</scope>  
    26.         </dependency>  
    27.   
    28.         <!-- Spring -->  
    29.         <dependency>  
    30.             <groupId>org.springframework</groupId>  
    31.             <artifactId>spring-context</artifactId>  
    32.             <version>${org.springframework.version}</version>  
    33.         </dependency>  
    34.   
    35.         <!-- Spring AOP + AspectJ -->  
    36.         <dependency>  
    37.             <groupId>org.springframework</groupId>  
    38.             <artifactId>spring-aop</artifactId>  
    39.             <version>${org.springframework.version}</version>  
    40.         </dependency>  
    41.           
    42.         <dependency>  
    43.             <groupId>org.aspectj</groupId>  
    44.             <artifactId>aspectjrt</artifactId>  
    45.             <version>1.8.9</version>  
    46.         </dependency>  
    47.           
    48.         <dependency>  
    49.             <groupId>org.aspectj</groupId>  
    50.             <artifactId>aspectjweaver</artifactId>  
    51.             <version>1.8.9</version>  
    52.         </dependency>  
    53.       
    54.     </dependencies>  
    55. </project>  


    下面开始正式的讲解:

    1、经典的基于代理的AOP实现,以一个睡觉的例子实现。

    (1)可睡觉的接口,任何可以睡觉的人或机器都可以实现它。

    [java] view plain copy
     
    1. public interface Sleepable {  
    2.     public void sleep();  
    3. }  

    (2)接口实现类,“Me”可以睡觉,“Me”就实现可以睡觉的接口。

    [java] view plain copy
     
    1. public class Me implements Sleepable{  
    2.     public void sleep() {  
    3.         System.out.println(" 睡觉!不休息哪里有力气学习! ");  
    4.     }  
    5. }  

    (3)Me关注于睡觉的逻辑,但是睡觉需要其他功能辅助,比如睡前脱衣服,起床脱衣服,这里开始就需要AOP替“Me”完成!解耦!首先需要一个SleepHelper类。因为一个是切入点前执行、一个是切入点之后执行,所以实现对应接口。

    [java] view plain copy
     
    1. public class SleepHelper implements MethodBeforeAdvice, AfterReturningAdvice {  
    2.   
    3.     public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {  
    4.         System.out.println("睡觉前要脱衣服!");  
    5.     }  
    6.   
    7.     public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {  
    8.         System.out.println("起床后要穿衣服!");  
    9.     }  
    10.   
    11. }  

    (4)最关键的来了,Spring核心配置文件application.xml配置AOP。

    [html] view plain copy
     
    1. <?xml version="1.0" encoding="UTF-8"?>  
    2. <beans xmlns="http://www.springframework.org/schema/beans"  
    3. <span style="white-space:pre">    </span>xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    4. <span style="white-space:pre">    </span>xmlns:aop="http://www.springframework.org/schema/aop"  
    5. <span style="white-space:pre">    </span>xsi:schemaLocation="http://www.springframework.org/schema/beans  
    6. <span style="white-space:pre">    </span>http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
    7. <span style="white-space:pre">    </span>http://www.springframework.org/schema/aop  
    8. <span style="white-space:pre">    </span>http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">  
    9.      
    10.    <!-- 定义被代理者 -->  
    11.    <bean id="me" class="com.springAOP.bean.Me"></bean>  
    12.      
    13.    <!-- 定义通知内容,也就是切入点执行前后需要做的事情 -->  
    14.    <bean id="sleepHelper" class="com.springAOP.bean.SleepHelper"></bean>  
    15.      
    16.    <!-- 定义切入点位置 -->  
    17.    <bean id="sleepPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">  
    18.         <property name="pattern" value=".*sleep"></property>  
    19.    </bean>  
    20.      
    21.    <!-- 使切入点与通知相关联,完成切面配置 -->  
    22.    <bean id="sleepHelperAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">  
    23.         <property name="advice" ref="sleepHelper"></property>         
    24.         <property name="pointcut" ref="sleepPointcut"></property>  
    25.    </bean>  
    26.      
    27.    <!-- 设置代理 -->  
    28.    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">  
    29.         <!-- 代理的对象,有睡觉能力 -->  
    30.         <property name="target" ref="me"></property>  
    31.         <!-- 使用切面 -->  
    32.         <property name="interceptorNames" value="sleepHelperAdvisor"></property>  
    33.         <!-- 代理接口,睡觉接口 -->  
    34.         <property name="proxyInterfaces" value="com.springAOP.bean.Sleepable"></property>   
    35.    </bean>  
    36.       
    37. </beans>  

    其中:

    <beans>是Spring的配置标签,beans里面几个重要的属性:

    xmlns:

    是默认的xml文档解析格式,即spring的beans。地址是http://www.springframework.org/schema/beans;通过设置这个属性,所有在beans里面声明的属性,可以直接通过<>来使用,比如<bean>等等。一个XML文件,只能声明一个默认的语义解析的规范。例如上面的xml中就只有beans一个是默认的,其他的都需要通过特定的标签来使用,比如aop,它自己有很多的属性,如果要使用,前面就必须加上aop:xxx才可以。类似的,如果默认的xmlns配置的是aop相关的语义解析规范,那么在xml中就可以直接写config这种标签了。

    xmlns:xsi:

    是xml需要遵守的规范,通过URL可以看到,是w3的统一规范,后面通过xsi:schemaLocation来定位所有的解析文件。

    xmlns:aop:

    这个是重点,是我们这里需要使用到的一些语义规范,与面向切面AOP相关。

    xmlns:tx:

    Spring中与事务相关的配置内容。

    (5)测试类,Test,其中,通过AOP代理的方式执行Me的sleep()方法,会把执行前、执行后的操作执行,实现了AOP的效果!

    [java] view plain copy
     
    1. public class Test {  
    2.     public static void main(String[] args){  
    3.         @SuppressWarnings("resource")  
    4.         //如果是web项目,则使用注释的代码加载配置文件,这里是一般的Java项目,所以使用下面的方式  
    5.         //ApplicationContext appCtx = new ClassPathXmlApplicationContext("application.xml");  
    6.         ApplicationContext appCtx = new FileSystemXmlApplicationContext("application.xml");  
    7.         Sleepable me = (Sleepable)appCtx.getBean("proxy");  
    8.         me.sleep();  
    9.     }  
    10. }  

    执行结果:



    (6)通过org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator简化配置。

    将配置文件中设置代理的代码去掉,加上:

    [html] view plain copy
     
    1. <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>  

    然后,在Test中,直接获取me对象,执行sleep方法,就可以实现同样的功能!

    通过自动匹配,切面会自动匹配符合切入点的bean,会被自动代理,实现功能!

    2、更简单的方式,通过AspectJ提供的注解实现AOP。

    (1)同样的例子,修改后的SleepHelper:

    [java] view plain copy
     
    1. @Aspect  
    2. public class SleepHelper{  
    3.   
    4.     public SleepHelper(){  
    5.           
    6.     }  
    7.       
    8.     @Pointcut("execution(* *.sleep())")  
    9.     public void sleeppoint(){}  
    10.       
    11.     @Before("sleeppoint()")  
    12.     public void beforeSleep(){  
    13.         System.out.println("睡觉前要脱衣服!");  
    14.     }  
    15.       
    16.     @AfterReturning("sleeppoint()")  
    17.     public void afterSleep(){  
    18.         System.out.println("睡醒了要穿衣服!");  
    19.     }  
    20.       
    21. }  

    (2)在方法中,可以加上JoinPoint参数以进行相关操作,如:

    [java] view plain copy
     
    1. //当抛出异常时被调用  
    2.     public void doThrowing(JoinPoint point, Throwable ex)  
    3.     {  
    4.         System.out.println("doThrowing::method "  
    5.                 + point.getTarget().getClass().getName() + "."  
    6.                 + point.getSignature().getName() + " throw exception");  
    7.         System.out.println(ex.getMessage());  
    8.     }  

    (3)然后修改配置为:

    [html] view plain copy
     
    1.        <aop:aspectj-autoproxy />  
    2. <!-- 定义通知内容,也就是切入点执行前后需要做的事情 -->  
    3. <bean id="sleepHelper" class="com.springAOP.bean.SleepHelper"></bean>  
    4. <!-- 定义被代理者 -->  
    5. <bean id="me" class="com.springAOP.bean.Me"></bean>  


    (4)最后测试,一样的结果!

    [java] view plain copy
     
    1. public class Test {  
    2.     public static void main(String[] args){  
    3.         @SuppressWarnings("resource")  
    4.         //如果是web项目,则使用注释的代码加载配置文件,这里是一般的Java项目,所以使用下面的方式  
    5.         //ApplicationContext appCtx = new ClassPathXmlApplicationContext("application.xml");  
    6.         ApplicationContext appCtx = new FileSystemXmlApplicationContext("application.xml");  
    7.         Sleepable me = (Sleepable)appCtx.getBean("me");  
    8.         me.sleep();  
    9.     }  
    10. }  


    3、使用Spring来定义纯粹的POJO切面(名字很绕口,其实就是纯粹通过<aop:fonfig>标签配置,也是一种比较简单的方式)。

    (1)修改后的SleepHelper类,很正常的类,所以这种方式的优点就是在代码中不体现任何AOP相关配置,纯粹使用xml配置。

    [java] view plain copy
     
    1. public class SleepHelper{  
    2.   
    3.     public void beforeSleep(){  
    4.         System.out.println("睡觉前要脱衣服!");  
    5.     }  
    6.       
    7.     public void afterSleep(){  
    8.         System.out.println("睡醒了要穿衣服!");  
    9.     }  
    10.      
    11. }  

    (2)配置文件:

    [html] view plain copy
     
    1. <?xml version="1.0" encoding="UTF-8"?>  
    2. <beans xmlns="http://www.springframework.org/schema/beans"  
    3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"  
    4.     xsi:schemaLocation="http://www.springframework.org/schema/beans  
    5.     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
    6.     http://www.springframework.org/schema/aop  
    7.     http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">  
    8.   
    9.     <!-- 定义通知内容,也就是切入点执行前后需要做的事情 -->  
    10.     <bean id="sleepHelper" class="com.springAOP.bean.SleepHelper"></bean>  
    11.     <!-- 定义被代理者 -->  
    12.     <bean id="me" class="com.springAOP.bean.Me"></bean>  
    13.   
    14.     <aop:config>  
    15.         <aop:aspect ref="sleepHelper">  
    16.             <aop:before method="beforeSleep" pointcut="execution(* *.sleep(..))" />  
    17.             <aop:after method="afterSleep" pointcut="execution(* *.sleep(..))" />  
    18.         </aop:aspect>  
    19.     </aop:config>  
    20.   
    21. </beans>  

    (3)配置的另一种写法

    [html] view plain copy
     
    1. <aop:config>  
    2.     <aop:aspect ref="sleepHelper">  
    3.            <aop:pointcut id="sleepHelpers" expression="execution(* *.sleep(..))" />  
    4.            <aop:before pointcut-ref="sleepHelpers" method="beforeSleep" />  
    5.            <aop:after pointcut-ref="sleepHelpers" method="afterSleep" />            
    6.        </aop:aspect>  
    7. </aop:config>  

    五、AOP实现原理

    学东西还是要深入进去的,推荐一篇网评还不错的博文,http://www.cnblogs.com/xiaolovewei/p/7919763.html

    转载:https://blog.csdn.net/zhangliangzi/article/details/52334964

  • 相关阅读:
    《解密腾讯海量服务之道》讲座笔记
    图解 Paxos 一致性协议
    向量时钟算法简介——本质类似MVCC
    logback配置
    Protocol Buffers简明教程
    分布式调用技术 RPC VS REST
    Dubbo和Spring Cloud微服务架构对比
    Spring Boot学习笔记
    relatedTarget、fromElement、toElement相关元素
    12个不可不知的Sublime Text应用技巧和诀窍
  • 原文地址:https://www.cnblogs.com/xiaolovewei/p/8999512.html
Copyright © 2011-2022 走看看