`
56553655
  • 浏览: 204239 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

黄晓童SPRING学习笔记:对AOP的深入探究

阅读更多

Spring的定义
对DI的初步理解
对AOP的初步理解
对DI的深入探究
对AOP的深入探究
Spring的事务管理
Spring MVC

 下面我将论述一下Spring的AOP。首先,介绍一下AOP的作用:把交叉事务(散布在程序中多个地点的函数)和业务逻辑代码分离开,同时,有些事情也是要被动执行的,比如经典的登录,当用户没有登录的时候,系统会提示登录,而不是在展示页面的时候程序主动的去判断用户有没有登录。这也正是Spring的好处之一。像登录这种代码在系统中是会被用到多次的,代码重用的主要面向对象技术就是委托和继承。而反复继承同一个基类可能会形成很脆弱的对象关系。切面正是这样的一种取代二者的方式。
 接下来我介绍几个AOP的术语,这些术语很重要。
通知:通知定义了切面要完成的工作、以及何时执行这个工作。
连接点:连接点是在程序的执行过程中能够插入切面的一个点,比如方法被调用时、异常抛出时等等。
切入点:一个切面不是要通知程序里所有的连接点的。切入点缩小了切面通知连接点的范围。
切面:通知和切入点的结合。
目标:就是被通知的对象。

 接下来我来创建一个典型的spring切面。因此,首先引入一个场景:观众观看演奏者表演。所以,先定义一个观众类:

public class Audience 
{
  //观众观看表演之前就座
  public void takeSeats()
 {}
  
 //观看表演之前关闭手机
  public void turnOffCellPhones() 
 {}
  
 //表演之后,如果表演成功就鼓掌
  public void applaud() 
 {}
  
 //观看之后,如果表演不好就要求退票
  public void demandRefund() 
 {}
}

 

接下来在xml文件中加入如下定义:

<bean  id="audience"  class="com.springinaction.springidol.Audience"  />

 
 这样,场景就描述完了。当表演者表演的时候,可以先调用观众就座和关闭手机的方法,然后演奏者执行表演方法,如果中途发生异常,就执行观众退票,如果成功就鼓掌。
 当然这是可行的,但是仔细一想会发现多少有点别扭。这不仅将观众类耦合到表演者类里面,而且表演者在表演的过程中,还要举着牌子,负责提示观众关手机,或者鼓掌。因此,这种设计明显是不合理的。
 下面我们就用AOP来改造这个场景,让它变得合理。首先,介绍一下spring的aop中的5种通知类型:执行前通知、返回后通知、抛出后通知、周围通知、引入通知。在这个场景里,会用到前三个通知。就座和关闭手机应该是执行前通知,鼓掌应该是返回后通知,退票应该是抛出后通知。这段通知的代码如下(该通知依赖于观众类):

public class AudienceAdvice implements MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice
{
  //执行前通知
  public void before(Method method, Object[] args, Object target) throws Throwable
  {
    audience.takeSeats();     //就座
    audience.turnOffCellPhones();     //关闭手机
  }
  
  //返回后通知
  public void afterReturning(Object rtn, Method method, Object[] args, Object target)  
 {
    audience.applaud();    //鼓掌
  }
  
 //抛出后通知
  public void afterThrowing(Throwable throwable) 
  {
    audience.demandRefund();    //退票
  }

  //观众类
  private Audience audience; 
  public void setAudience(Audience audience)
  {
    this.audience = audience;
  }
}

 
 上面类中的每一个方法都是实现三个通知接口之后要求实现的方法。这没什么好说的。只是大家注意一下方法的参数即可。

下面我们以周围通知的方式来实现一下这个场景:

public class AudienceAroundAdvice implements MethodInterceptor
{
  public Object invoke(MethodInvocation invocation) throws Throwable
  {

    try {
      audience.takeSeats();  //就座
      audience.turnOffCellPhones();  //关闭手机
      Object returnValue = invocation.proceed(); //调用目标方法    
      audience.applaud();  //鼓掌    
      return returnValue;
    } 
    catch (PerformanceException  throwable) {
      audience.demandRefund();  //退票
      throw throwable;
    }
  }
  
  //观众类
  private Audience audience;
  public void setAudience(Audience audience) {
    this.audience = audience;
  }
}

 

 周围通知的一个最大的优势就是能够在一个方法中定义前通知和后通知,而且还很简洁。现在我们已经了解了好几种创建通知的方法了,但是还没有提到如何创建切点,没有切点,也就没有切面了。下面我们就来说说切点,spring提供了多种类型的切点。两种最常用的就是正则表达式切点和AspectJ表达式切点。首先来看正则表达式切点。
 还记得前面提到的表演者的类吗?每个表演者都实现了一个perform方法。现在我们要做的就是,在每个表演者表演的时候(执行perform方法时),插入上述定义的观众的行为。

切点的定义如下:该正则表达式匹配所有类中含有名为“perform”的方法。

<bean id="audiencePointcut" 
     class="org.springframework.aop.support.JdkRegexpMethodPointcut">
    <property name="pattern"  value=".*perform" />
</bean>

 

然后,我们需要把上述切点与通知相关联,定义如下bean:

<bean id="audienceAdvisor"
      class="org.springframework.aop.support.DefaultPointcutAdvisor">
    <property name="advice" ref="audienceAdvice" />
    <property name="pointcut" ref="audiencePointcut" />
</bean>

 

 这样,一个完整的切面就这样被定义了。但是在spring中,切面是以代理的方式实现的。无论使用哪种方式定义切点,都是需要代理bean的。这些东西我们下面再说,现在,先介绍一下如何定义AspectJ切点:

<bean id="audienceAdvisor"
      class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
    <property name="advice" ref="audienceAdvice" />
    <property name="expression" value="execution(* *.perform(..))" />
</bean>

 

execution(* *.perform(..)):
execution:执行方法时;
第一个星号:任何返回类型;
第二个星号:任意类;
perform :代表perform方法;
(..) :任意参数设置。

 现在,我们就来说一些有关代理目标bean的东西。现在,我们来为一个名为Duke的诗人表演者定义一个代理bean:

原Duke  bean的定义:

<bean id="dukeTarget" 
      class="com.springinaction.springidol.PoeticJuggler"  autowire="constructor">
    <constructor-arg  ref="sonnet29" />
</bean>

Duke  bean的代理的定义:

<bean id="duke"  class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target" ref=" dukeTarget " />
 <property name="interceptorNames"  value=" audienceAdvisor " />
 <property name="proxyInterfaces"  value ="com.alibaba.performer" />
</bean>

 

interceptorNames属性指明哪个通知者要应用于被代理的bean。
proxyInterfaces属性指明代理应该实现哪个接口。
 上述的两个属性其实都是String的数组。在该例中,spring会将单个值转换成数组的。如果有多个值可以这样做:

<property name="interceptorNames" >
 <list>
 <value> audienceAdvisor</value>
 </list>
</property>

 

 当然,如果现在又有一个叫steve的人来表演萨克斯的话,我们就会发现,上述代码还要重写一遍,而其中只有target属性不同。这样,我们就需要抽象ProxyFactoryBean:

<bean id="audienceProxyBase"
      class="org.springframework.aop.framework.ProxyFactoryBean"  abstract=”true”>
    <property name="proxyInterfaces"  value="com.alibaba.Performer" />
    <property name="interceptorNames" value="audienceAdvisor" />
</bean>

<bean id="duck"  parent=" audienceProxyBase ">
    <property name="target"  ref="duckTarget" />
</bean>

 

 这样,我们就介绍完了spring最经典的基于代理方式实现的aop。下面我们来看一下@AspectJ注解驱动的切面。我们仍以那个观众观看表演的情景来做例子。

@Aspect         //该类已经不是一个传统的POJO了,而是一个切面
public class Audience 
{
  @Pointcut("execution(* *.performance(..))")     //定义一个切面
  public void performance(){}

  @Before("performance()")          //前通知
  public void takeSeats() {
    System.out.println("The audience is taking their seats.");
  }
  
  @Before("performance()")
  public void turnOffCellPhones() {
    System.out.println("The audience is turning off their cellphones");
  }
  
  @AfterReturn("performance()")          //返回后通知
  public void applaud() {
    System.out.println("CLAP CLAP CLAP CLAP CLAP");
  }
  
  @AfterThrowing("performance()")        //抛出后通知
  public void demandRefund() {
    System.out.println("Boo! We want our money back!");
  }
}

 
 当然,不要忘了在xml配置文件里加上<aop:aspectj-autoproxy/>,这个标识是告诉spring去扫描被@Aspect标记的切面了。周围通知的写法也很类似,@Around("performance()")即可。上面的代码没什么好说的,读起来很简单,就像读报纸一样,这里就不废话了。下面说说另一种切面:纯粹的POJO切面。这种切面更加直观,他可以将任何一个java类变成一个切面。就拿我们最初的那个观众类举例,他就是一个很普普通通的类。但是通过如下的xml配置之后,这个观众类就是一个切面了,但是原来的java类没有任何代码上的改变。

<bean  id="audience"  class="com.springinaction.springidol.Audience" />

<aop:config>
 <aop:aspect ref="audience">
 <aop:pointcut  id="performance"  expression="execution(* *.perform(..))" />
 
 <aop:before  method="takeSeats"  pointcut-ref="performance" />
       <aop:before  method="turnOffCellPhones"  pointcut-ref="performance" />
        <aop:after-returning  method="applaud"  pointcut-ref="performance" />
 <aop:after-throwing  method="demandRefund"  pointcut-ref="performance" />
 </aop:aspect>
</aop:config>

 

分享到:
评论

相关推荐

    spring-aop-5.0.10.RELEASE-API文档-中文版.zip

    Maven坐标:org.springframework:spring-aop:5.0.10.RELEASE; 标签:spring、aop、springframework、jar包、java、API文档、中文版; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览...

    spring aop 学习笔记

    本学习笔记将深入探讨Spring AOP的核心概念、工作原理以及实际应用。 1. **核心概念** - **切面(Aspect)**:切面是关注点的模块化,包含业务逻辑之外的横切关注点,如日志、事务管理。 - **连接点(Join Point...

    Spring技术内幕:深入解析Spring架构与设计原理

    《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》从源代码的角度对Spring的内核和各个主要功能模块的架构、设计和实现原理进行了深入剖析。你不仅能从本书中参透Spring框架的出色架构和设计思想,还能从...

    AspectJ in Action: Enterprise AOP with Spring Applications

    - **避免过度使用AOP:**过度使用AOP可能会导致系统复杂度增加,应谨慎考虑是否真正需要引入AOP。 - **注意性能影响:**虽然AOP可以提高代码的可维护性和可扩展性,但过度使用可能会对性能产生负面影响。 #### 代码...

    Spring技术内幕:深入解析Spring架构与设计原理(第2版) .pdf

    3. **数据访问抽象层**:Spring提供了对各种持久化技术的支持,如JDBC、Hibernate等,并提供了一套统一的数据访问异常层次结构。 4. **事务管理**:Spring提供了声明式事务管理功能,使得事务管理变得更加简单易用。...

    第四章:Spring AOP API 设计模式1

    10. **代理模式(Proxy)**:Spring AOP的核心就是代理模式,它创建了目标对象的代理,代理对象在调用实际目标方法之前/之后执行额外的逻辑(如日志、事务管理等),提供了对对象行为的扩展。 11. **模板方法模式...

    Spring基础:Spring AOP简单使用

    - **XML配置**:在Spring的配置文件中,可以使用&lt;aop:config&gt;标签来定义切面,&lt;aop:pointcut&gt;定义切点,&lt;aop:advisor&gt;定义通知,&lt;aop:aspect&gt;将切点和通知关联起来。 - **注解配置**:Spring 2.5引入了基于注解的...

    Spring技术内幕:深入解析Spring架构与设计原理[汇编].pdf

    * Spring AOP:Spring AOP是Spring的面向方面编程框架,提供了面向方面的编程能力。 * Spring JDBC:Spring JDBC是Spring的数据库访问框架,提供了对数据库的访问能力。 4. Spring在云计算中的应用 Spring在...

    spring-aop-5.2.0.RELEASE-API文档-中文版.zip

    Maven坐标:org.springframework:spring-aop:5.2.0.RELEASE; 标签:springframework、spring、aop、中文文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 ...

    Spring技术内幕:深入解析 Spring架构与设计原理.pdf

    本书从源代码的角度对Spring的内核和各个主要功能模块的架构、设计和实现原理进行了深入剖析。你不仅能从本书中参透Spring框架的优秀架构和设计思想,还能从Spring优雅的实现源码中一窥Java语言的精髓。本书在开篇...

    Spring技术内幕:深入解析Spring架构与设计原理(第1部分)

     深入解析Spring架构原理与设计思想,探究Spring成功的奥秘。  揭开Spring源代码的神秘面纱,展示系统阅读开源软件源代码的方法和秘诀。  如果你正在思考下面这些问题,也许《Spring技术内幕:深入解析Spring架构...

    hualinux spring 3.15:Spring AOP.pdf

    此外,需要在Spring的Bean配置文件中添加&lt;aop:aspectj-autoproxy&gt;元素以启用AspectJ注解的支持。 5. 声明AspectJ切面 要在Spring中声明AspectJ切面,需要在IOC容器中将切面声明为Bean实例。一旦在Spring IOC容器中...

    Spring AOP面向方面编程原理:AOP概念

    3. **灵活的通知模型**:Spring AOP提供了多种类型的通知,包括around、before、after returning、after throwing等,使得开发者可以根据实际需求选择最适合的通知类型。 4. **丰富的切入点表达式语言**:Spring ...

    Spring技术内幕:深入解析Spring架构与设计原理(第2版)

    《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》从源代码的角度对Spring的内核和各个主要功能模块的架构、设计和实现原理进行了深入剖析。你不仅能从《Spring技术内幕:深入解析Spring架构与设计原理...

    Spring学习笔记(15)----使用Spring的注解方式实现AOP

    在本篇Spring学习笔记中,我们将深入探讨如何利用Spring框架的注解方式来实现面向切面编程(AOP)。AOP是一种编程范式,它允许我们定义横切关注点,如日志、事务管理等,然后将这些关注点模块化并插入到应用程序的多...

    day39-Spring 06-Spring的AOP:带有切点的切面

    在"day39-Spring 06-Spring的AOP:带有切点的切面"这个主题中,我们将深入探讨如何在Spring中实现带有切点的切面,以及它们如何与源码和工具结合使用。 首先,理解AOP的基本概念非常重要。AOP的核心是切面(Aspect...

    基于java的企业级应用开发:Spring AOP简介.ppt

    **Spring AOP 简介** 面向切面编程(AOP),全称为 Aspect-Oriented Programming,是一种编程范式,旨在解决传统面向对象编程(OOP)中的代码重复和分散问题。在OOP中,诸如事务管理、日志记录等功能往往会分散在多...

    Spring的Hello World:理解AOP

    1. **事务管理**:Spring AOP常用于事务管理,自动在每个数据库操作前后处理事务的开启、提交、回滚等。 2. **日志记录**:可以在方法调用前后记录日志,无需在每个需要的日志的地方重复代码。 3. **性能监控**:...

    spring-aop-6.0.2.jar

    spring-aop-6.0.2.jar

    spring-aop-5.3.10-API文档-中文版.zip

    Maven坐标:org.springframework:spring-aop:5.3.10; 标签:springframework、spring、aop、中文文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化...

Global site tag (gtag.js) - Google Analytics