一、初识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接口方式的更多相关文章
-
Spring学习笔记-面向切面(AOP)-04
什么是面向切面编程 先大概了解一下部分术语 横切关注点:软件开发中,散布于多出的功能称为横切关注点(cross-cutting concern),简单的可以描述为可以影响应用多处的功能,比如日志.安全 ...
-
Spring学习笔记--面向切面编程(AOP)
什么是AOP AOP(Aspect Oriented Programming),意为面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的 ...
-
spring学习 八 面向切面编程(AOP)概述
注:本文大部分参考 --------------------- 本文来自 -望远- 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/yanquan345/artic ...
-
spring的面向切面实现的两种方式
面向切面:主要应用在日志记录方面.实现业务与日志记录分离开发. spring面向切面有两种实现方式:1.注解 2.xml配置. 1.注解实现如下: (1)配置如下: <?xml version= ...
-
【Spring学习笔记-MVC-3】SpringMVC返回Json数据-方式1
<Spring学习笔记-MVC>系列文章,讲解返回json数据的文章共有3篇,分别为: [Spring学习笔记-MVC-3]SpringMVC返回Json数据-方式1:http://www ...
-
【Spring学习笔记-MVC-4】SpringMVC返回Json数据-方式2
<Spring学习笔记-MVC>系列文章,讲解返回json数据的文章共有3篇,分别为: [Spring学习笔记-MVC-3]SpringMVC返回Json数据-方式1:http://www ...
-
Spring基础(二)_面向切面(AOP)
面向切面编程 面向切面编程[AOP,Aspect Oriented Programming]:通过预编译方式和运行期间动态代理实现程序功能的统一维护的技术.AOP 是 Spring 框架中的一个重要内 ...
-
Spring框架使用(控制反转,依赖注入,面向切面AOP)
参见:http://blog.csdn.net/fei641327936/article/details/52015121 Mybatis: 实现IOC的轻量级的一个Bean的容器 Inversion ...
-
Spring中的面向切面编程(AOP)简介
一.什么是AOP AOP(Aspect-Oriented Programming, 面向切面编程): 是一种新的方法论, 是对传统 OOP(Object-Oriented Programming, 面 ...
随机推荐
-
关于css中pointer-events属性的怪异行为
在我的记忆中pointer-events就是用来进行事件穿透的,也就是说,如果给父元素设置了pointer-events:none,那么父元素不再监听鼠标事件事件(类似于touch,click这样的) ...
-
javascript 函数初探 (六)--- 闭包初探#4
循环中的闭包: 让我们来看一下一个会循环三次的操作,她在每次迭代中都会创建一个返回当前序列号的新函数,该函数会被添加到一个数组中,并最终返回: function F(){ var arr = [], ...
-
virtualBox切换到无缝模式后,如何调出菜单
host+c host就是指右边的那个ctrl键
-
java设计模式。。。转载
maowang I am a slow walker,but I never walk backwards! 博客园 首页 新随笔 联系 订阅 管理 随笔 - 125 文章 - 0 评论 - 12 ...
-
jQuery中append()与appendTo()方法区别
1. append(content)方法 方法作用:向每个匹配的元素内部追加内容. 参数介绍:content (<Content>): 要追加到目标中的内容. 用法示例: HTML代码为& ...
-
Select-Object用法
展开 modules 属性的详细信息 get-process |? {$_.processname -eq "cmd"}|select -ExpandProperty module ...
-
C++的表驱动法
目的:使用表驱动法,替换复杂的if/else和switch/case语句. 说明:JS 等其他语言也都支持的. 表驱动发示例:http://blog.csdn.net/zhouyulu/article ...
-
终极秘籍教你怎么找回被盗iPhone 查询ICCID
iPhone不慎丢失后怎么办?普通青年:立刻报警,基本没用.文艺青年:用Find my iPhone查找位置.但那只是个大概位置,iPhone关机后更是没戏,接着是用iCloud锁定手机,发送警告信息 ...
-
基于visual Studio2013解决C语言竞赛题之0502最小数替换
题目
-
android activity四种启动模式
1.standard <activity android:name=".MainActivity" android:launchMode="standard&quo ...