一、初识AOP


  
关于AOP的学习可以参看帮助文档:
spring-3.2.0.M2\docs\reference\html目录下index.html的相关章节

  
   1、AOP:Aspect-Oriented
Programming。AOP是OOP的补充,是GOF的延续。说到AOP,我们就不得不来提一下软件的纵向和横向问题。从纵向结构来看
就是我们软件系统的各个模块,它主要负责处理我们的核心业务(例如商品订购、购物车查看);而从横向结构来看,我们几乎每个系统又包含一些公共模块(例如
权限、日志模块等)。这些公共模块分布于我们各个核心业务之中(例如订购和查看商品明细的过程都需要检查用户权限、记录系统日志等)。这样一来不仅在开发
过程中要处处关注公共模块的处理而且开发后维护起来也是十分麻烦。而有了AOP之后将应用程序中的商业逻辑同对其提供支持的通用服务进行分离,使得开发人
员可以更多的关注核心业务开发。

 
2、AOP术语

   
切面(aspect):用来切插业务方法的类。

  连接点(joinpoint):是切面类和业务类的连接点,其实就是封装了业务方法的一些基本属性,作为通知的参数来解析。
  通知(advice):在切面类中,声明对业务方法做额外处理的方法。
  切入点(pointcut):业务类中指定的方法,作为切面切入的点。其实就是指定某个方法作为切面切的地方。
  目标对象(target object):被代理对象。
  AOP代理(aop proxy):代理对象。
  通知:
  前置通知(before advice):在切入点之前执行。
  后置通知(after returning advice):在切入点执行完成后,执行通知。
  环绕通知(around advice):包围切入点,调用方法前后完成自定义行为。
  异常通知(after throwing advice):在切入点抛出异常后,执行通知。
  
AOP是基于代理模式,了解了jdk动态代理和cglib的用法,对我们学习大有裨益。

二、Spring AOP环境
  要在项目中使用Spring AOP 则需要在项目中导入除了spring
jar包之外,还有aspectjrt.jar,aspectjweaver.jar,aopalliance.jar
,spring-aop-3.2.0.M2.jar和cglib.jar 。

   
好了,前提工作准备完成,Spring
提供了很多的实现AOP的方式:Spring
接口方式,schema配置方式和注解
等,好了废话不多说了,开始spring
aop学习之旅,这篇先以Spring接口的方式学起!

三、Spring接口方式实现AOP步骤
 
  
利用Spring AOP接口实现AOP,主要是为了指定自定义通知来供spring AOP机制识别。主要接口:前置通知
MethodBeforeAdvice
,后置通知:AfterReturningAdvice,环绕通知:MethodInterceptor,异常通知:ThrowsAdvice
。见例子代码:
 
步骤一、业务接口的编写
 //
代理类接口,也是业务类接口<br>
 // 利用接口的方式,spring aop 将默认通过jdk
动态代理来实现代理类<br>
 // 不利用接口,则spring aop 将通过cglib 来实现代理类

public interface IBaseBusiness {

// 用作代理的切入点方法
     
public String delete(String obj)

//
这方法不被切面切
    
public String add(String obj);
  
   
//
这方法切不切呢?可以设置
   
public String modify(String obj);
}

步骤二、业务类:

//业务类,也是目标对象

public class BaseBusiness implements IBaseBusiness {
    //切入点
     
public String delete(String obj) {
       
System.out.println("==========调用切入点:" + obj +
"说:你敢删除我!===========\n");
       
return obj + ":瞄~";
    }

public
String add(String obj) {
       
System.out.println("================这个方法不能被切。。。==============
\n");
       
return obj + ":瞄~ 嘿嘿!";
    }

public
String modify(String obj) {
       
System.out.println("=================这个也设置加入切吧====================\n");

return obj + ":瞄改瞄啊!";
    }

}

步骤三、通知类:
   
1、前置通知:

public class BaseBeforeAdvice implements MethodBeforeAdvice {

// method : 切入的方法 <br>
    
//args :切入方法的参数 <br>
    
// target :目标对象
   
   
@Override
    public void
before(Method method, Object[] args, Object target) throws
Throwable {
       
System.out.println("===========进入beforeAdvice()============
\n");

System.out.print("准备在" + target + "对象上用");
       
System.out.print(method + "方法进行对 '");
       
System.out.print(args[0] + "'进行删除!\n\n");

System.out.println("要进入切入点方法了 \n");
    }

}

 2、后置通知:

public class BaseAfterReturnAdvice implements AfterReturningAdvice
{
    
//returnValue :切入点执行完方法的返回值,但不能修改
<br>
    
// method :切入点方法 <br>
    // args
:切入点方法的参数数组 <br>
    
// target :目标对象

@Override
    public void
afterReturning(Object returnValue, Method method, Object[] args,
Object target) throws Throwable {
       
System.out.println("==========进入afterReturning()===========
\n");
       
System.out.println("切入点方法执行完了 \n");

System.out.print(args[0] + "在");
       
System.out.print(target + "对象上被");
       
System.out.print(method + "方法删除了");
       
System.out.print("只留下:" + returnValue + "\n\n");
    }

}

  3、环绕通知:

public class BaseAroundAdvice implements MethodInterceptor {

// invocation :连接点

@Override
    public
Object invoke(MethodInvocation invocation) throws Throwable {
       
System.out.println("===========进入around环绕方法!=========== \n");

// 调用目标方法之前执行的动作
       
System.out.println("调用方法之前: 执行!\n");

// 调用方法的参数
       
Object[] args = invocation.getArguments();
       
// 调用的方法
       
Method method = invocation.getMethod();
       
// 获取目标对象
       
Object target = invocation.getThis();
       
// 执行完方法的返回值:调用proceed()方法,就会触发切入点方法执行
       
Object returnValue = invocation.proceed();

System.out.println("===========结束进入around环绕方法!===========
\n");

System.out.println("输出:" + args[0] + ";" + method + ";" + target +
";" + returnValue + "\n");

System.out.println("调用方法结束:之后执行!\n");

return returnValue;
    }

}

 4、异常通知:

// 异常通知,接口没有包含任何方法。通知方法自定义

public class BaseAfterThrowsAdvice implements ThrowsAdvice {
    
// 通知方法,需要按照这种格式书写
    
// @param method
    
//
         
可选:切入的方法
    
//@param args
    
//
         
可选:切入的方法的参数
    // @param
target
    
//
        
可选:目标对象
    
// @param throwable
    
//  必填 : 异常子类,出现这个异常类的子类,则会进入这个通知。
   
    public void
afterThrowing(Method method, Object[] args, Object target,
Throwable throwable) {
       
System.out.println("删除出错啦");
    }

}

步骤四、定义指定切点:

//
定义一个切点,指定对应方法匹配。来供切面来针对方法进行处理<br>

// 继承NameMatchMethodPointcut类,来用方法名匹配

public class Pointcut extends NameMatchMethodPointcut {

private
static final long serialVersionUID = 3990456017285944475L;

@SuppressWarnings("rawtypes")
   
@Override
    public
boolean matches(Method method, Class targetClass) {
       
// 设置单个方法匹配
       
this.setMappedName("delete");
       
// 设置多个方法匹配
       
String[] methods = { "delete", "modify" };

//也可以用“ * ” 来做匹配符号
       
// this.setMappedName("get*");

this.setMappedNames(methods);

return super.matches(method, targetClass);
    }

}

步骤五、配置xml文件:
<?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:p="http://www.springframework.org/schema/p"
   xmlns:context="http://www.springframework.org/schema/context"

xmlns:aop="http://www.springframework.org/schema/aop"

xsi:schemaLocation="

http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

<!-- 基于注解导入的
-->
  

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-3.0.xsd

<!-- 基于aop导入的
-->

        
http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
   
default->

<!--
==============================利用spring自己的aop配置================================
-->
   
<!-- 声明一个业务类 -->
   
<bean id="baseBusiness"
class="aop.base.BaseBusiness" />
  
   
<!-- 声明通知类 -->
   
<bean id="baseBefore"
class="aop.base.advice.BaseBeforeAdvice" />
   
<bean id="baseAfterReturn"
class="aop.base.advice.BaseAfterReturnAdvice"
/>
   
<bean id="baseAfterThrows"
class="aop.base.advice.BaseAfterThrowsAdvice"
/>
   
<bean id="baseAround"
class="aop.base.advice.BaseAroundAdvice" />

<!-- 指定切点匹配类 -->
   
<bean id="pointcut"
class="aop.base.pointcut.Pointcut" />

<!-- 包装通知,指定切点 -->
   
<bean id="matchBeforeAdvisor"
class="org.springframework.aop.support.DefaultPointcutAdvisor">

<property name="pointcut">
           
<ref bean="pointcut" />
       
</property>
       
<property name="advice">
           
<ref bean="baseBefore" />
       
</property>
   
</bean>

<!-- 使用ProxyFactoryBean 产生代理对象
-->
   
<bean id="businessProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">

<!-- 代理对象所实现的接口 ,如果有接口可以这样设置
-->
       
<property
name="proxyInterfaces">
           
<value>aop.base.IBaseBusiness</value>

</property>

<!-- 设置目标对象 -->
       
<property name="target">
           
<ref local="baseBusiness" />
       
</property>
       
<!-- 代理对象所使用的拦截器 -->
       
<property
name="interceptorNames">
           
<list>
               
<value>matchBeforeAdvisor</value>

<value>baseAfterReturn</value>

<value>baseAround</value>

</list>
       
</property>
   
</bean>
</beans>

步骤六、测试类:

public class Debug {

public
static void main(String[] args) {
       
ApplicationContext context = new
ClassPathXmlApplicationContext("aop/schema_aop.xml");
       
AspectBusiness business = (AspectBusiness)
context.getBean("aspectBusiness");
       
business.delete("猫");
    }

}

g、测试结果:运行下测试类,清晰明了。由于结果呈现太长就不贴了。
  具体的代码实现可以从代码注释中很容易理解
接口方式的实现。结果也可想而知,前置方法会在切入点方法之前执行,后置会在切入点方法执行之后执行,环绕则会在切入点方法执行前执行同事方法结束也会执行对应的部分。主要是调用proceed()方法来执行切入点方法。来作为环绕通知前后方法的分水岭。然后在实现的过程中,有几点却是可以细揣摩一下的。

  可以看出在xml 配置
businessProxy这个bean的时候,ProxyFactoryBean类中指定了,proxyInterfaces参数。这里我把他配置了
IBaseBusiness接口。因为在项目开发过程中,往往业务类都会有对应的接口,以方便利用IOC解耦。但Spring
AOP却也能支持没有接口的代理。这就是为什么需要导入cglib.jar的包了。看过spring的源码,知道在目标切入对象如果有实现接口,spring会默认使用jdk动态代理来实现代理类。如果没有接口,则会通过cglib来实现代理类。

  这个业务类现在有
前置通知,后置通知,环绕三个通知同时作用,可能以及更多的通知进行作用。那么这些通知的执行顺序是怎么样的?就这个例子而言,同时实现了三个通知。在例
子xml中,则显示执行before通知,然后执行around的前处理,执行切点方法,再执行return处理。最后执行around的后处理。经过测
试,知道spring
处理顺序是按照xml配置顺序依次处理通知,以队列的方式存放前通知,以压栈的方式存放后通知。所以是前通知依次执行,后通知到切入点执行完之后,从栈里
在后进先出的形式把后通知执行。

  在实现过程中发现通知执行对应目标对象的整个类中的方法,如何精确到某个方法,则需要定义一个切点匹配的方式:spring提供了方法名匹配或正则方式来匹配。然后通过DefaultPointcutAdvisor来包装通知,指定切点。

 利用方式一的配置起来,可见代码还是非常的厚重的,定义一个切面就要定义一个切面类,然而切面类中,就一个通知方法,着实没有必要。所以Spring提
供了,依赖aspectj的schema配置和基于aspectj
注解方式。这两种方式非常简介方便使用,也是项目中普遍的使用方式。

Spring学习4-面向切面(AOP)之Spring接口方式的更多相关文章

  1. Spring学习笔记-面向切面(AOP)-04

    什么是面向切面编程 先大概了解一下部分术语 横切关注点:软件开发中,散布于多出的功能称为横切关注点(cross-cutting concern),简单的可以描述为可以影响应用多处的功能,比如日志.安全 ...

  2. Spring学习笔记--面向切面编程(AOP)

    什么是AOP AOP(Aspect Oriented Programming),意为面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的 ...

  3. spring学习 八 面向切面编程(AOP)概述

    注:本文大部分参考   --------------------- 本文来自 -望远- 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/yanquan345/artic ...

  4. Spring框架系列(4) - 深入浅出Spring核心之面向切面编程(AOP)

    在Spring基础 - Spring简单例子引入Spring的核心中向你展示了AOP的基础含义,同时以此发散了一些AOP相关知识点; 本节将在此基础上进一步解读AOP的含义以及AOP的使用方式.@pd ...

  5. spring的面向切面实现的两种方式

    面向切面:主要应用在日志记录方面.实现业务与日志记录分离开发. spring面向切面有两种实现方式:1.注解 2.xml配置. 1.注解实现如下: (1)配置如下: <?xml version= ...

  6. 【Spring学习笔记-MVC-3】SpringMVC返回Json数据-方式1

    <Spring学习笔记-MVC>系列文章,讲解返回json数据的文章共有3篇,分别为: [Spring学习笔记-MVC-3]SpringMVC返回Json数据-方式1:http://www ...

  7. 【Spring学习笔记-MVC-4】SpringMVC返回Json数据-方式2

    <Spring学习笔记-MVC>系列文章,讲解返回json数据的文章共有3篇,分别为: [Spring学习笔记-MVC-3]SpringMVC返回Json数据-方式1:http://www ...

  8. Spring基础(二)_面向切面(AOP)

    面向切面编程 面向切面编程[AOP,Aspect Oriented Programming]:通过预编译方式和运行期间动态代理实现程序功能的统一维护的技术.AOP 是 Spring 框架中的一个重要内 ...

  9. Spring框架使用(控制反转,依赖注入,面向切面AOP)

    参见:http://blog.csdn.net/fei641327936/article/details/52015121 Mybatis: 实现IOC的轻量级的一个Bean的容器 Inversion ...

随机推荐

  1. MyEclipse无法启动调试:Cannot connect to VM

    MyEclipse无法启动调试:Cannot connect to VM 问题描述:Eclipse普通的Run模式没有问题,Debug模式却启动不了.换了Eclipse,MyEclipse,JDK都不 ...

  2. ubuntu 查看软件包版本以及软件包的源码

    aptitude show  xxx sudo apt-cache show autoconf setattr, getattr, setattr http://ju.outofmemory.cn/e ...

  3. Linux 进程通信(有名管道)

    有名管道(FIFO) 有名管道是持久稳定的. 它们存在于文件系统中. FIFO比无名管道作用更大,因为他们能让无关联的进程之间交换数据. 管道文件一般用于交换数据. shell命令创建管道 一个she ...

  4. Difference Between TIF and TIFF

    TIF vs TIFF Many people are confused with similar file extensions that only differ by a single lette ...

  5. 获取元素的xpath, 转换xpath为csspath进行jQuery元素获取

    获取元素的xpath, 转换xpath为csspath进行jQuery元素获取 博客分类: 编程心得 jQueryCSSHTML  var $shadow = new Object(); /** 获取 ...

  6. Activiti系列:带有serviceTask的或者定时启动任务的流程部署失败的原因分析

    在将两个带有serviceTask任务的流程部署到数据库的时候发现无法导入,最终分析出如下问题: 1)流程1是打算让定时启动事件可以每小时触发一次 由于原来是用 R/2015-11-01T01:00: ...

  7. 浪潮之巅IT那点事之一——AT&T的兴衰

    首次接触到<浪潮之巅>这本书,几乎是熬了一个通宵把上下两册全部看完,感慨颇多.从事计算机基础教育多年,每次在讲计算机导论课程时,总是在重复同样的内容,讲一些计算机结构.操作系统.算法.软件 ...

  8. 通通制作Html5小游戏——第二弹(仿flappy bird像素鸟)

    亲爱的博友们,我又回来啦~因为我们技术宅的思想只有技术宅懂得,好不容易写了点好玩的东西发QQ空间,结果只有11的UV,0回复....10分钟ps一个女神的素描效果发QQ空间朋友圈,一大堆回复加赞,作为 ...

  9. nodejs学习之文件上传

    最近要做个图片上传的需求,因为服务端春节请假回家还没来,所以就我自己先折腾了一下,大概做出来个效果,后台就用了nodejs,刚开始做的时候想网上找一下资料,发现大部分资料都是用node-formida ...

  10. hello,world!

    这可以算是我的第一篇博客了吧?真正意义上的开始... 我于2016年6月毕业于山东英才学院信工学院计算机管理系,至今已有近半年.几经周转,总算是准备稳定下来了.于是,当初的博客想法提上了日程. 本人工 ...