一、SpringAOP的概述

1、AOP概念

AOP是Aspect-Oriented Programming(面向切面编程)的简称。维基百科的解释例如以下:

Aspect是一种新的模块化机制,用来描写叙述分散在对象、类或函数中的横切关注点(crosscutting concern)。

从关注点中分离出横切关注点是面向切面的程序设计的核心。

分离关注点使解决特定领域问题的代码从业务逻辑代码中独立出来,业务逻辑的代码中不再含有针对特定领用问题代码的调用。业务逻辑同特定领域问题的关系通过切面来封装、维护,这样原本分散在整个应用程序中的变动就能够非常好的管理起来。

2、Advice通知

Advice定义在连接点为切面增强提供织入接口。

在Spring AOP中。他主要描写叙述Spring AOP环绕方法调用而注入的切面行为。Advice是定义在org.aopalliance.aop.Advice中的接口。在Spring AOP使用这个统一接口。并通过这个接口为AOP切面增强的织入功能做了很多其它的细节和扩展,比方提供了更详细的通知类型,如BeforeAdvice,AfterAdvice,ThrowsAdvice等。

2.1 BeforeAdvice

首先我们从BeforeAdvice開始:

在BeforeAdvice的继承关系中,定义了为待增强的目标方法设置的前置增强接口MethodBeforeAdvice,使用这个前置接口须要实现一个回调函数:

void before(Method method,Object[] args,Object target) throws Throwable;

作为回调函数,before方法的实如今Advice中被配置到目标方法后,会在调用目标方法时被回调。详细的參数有:Method对象。这个參数是目标方法的反射对象;Object[]对象数组,这个对象数组中包括目标方法的输入參数。

以CountingBeforeAdvice为例来说明BeforeAdvice的详细使用,CountBeforeAdvice是接口MethodBeforeAdvice的详细实现。他仅仅是统计被调用方法的次数,作为切面增强实现。他会依据调用方法的方法名进行统计,把统计结果依据方法名和调用次数作为键值对放入一个map中。代码例如以下:

public class CountingBeforeAdvice extends MethodCounter implements MethodBeforeAdvice {
//实现方法前置通知MethodBeforeAdvice接口的方法
public void before(Method m, Object[] args, Object target) throws Throwable {
//以目标对象方法作为參数,调用父类MethodCounter的count方法统计方法调用次数
count(m);
}
}
CountingBeforeAdvice的父类MethodCounter的源代码例如以下:
public class MethodCounter implements Serializable {
//方法名—>方法调用次数的map集合,存储方法的调用次数
private HashMap<String, Integer> map = new HashMap<String, Integer>();
//全部方法的总调用次数
private int allCount;
//统计方法调用次数,CountingBeforeAdvice的调用入口
protected void count(Method m) {
count(m.getName());
}
//统计指定名称方法的调用次数
protected void count(String methodName) {
//从方法名—>方法调用次数集合中获取指定名称方法的调用次数
Integer i = map.get(methodName);
//假设调用次数不为null,则将调用次数加1,假设调用次数为null,则设置调用次数为1
i = (i != null) ? new Integer(i.intValue() + 1) : new Integer(1);
//又一次设置集合中保存的方法调用次数
map.put(methodName, i);
//全部方法总调用次数加1
++allCount;
}
//获取指定名称方法的调用次数
public int getCalls(String methodName) {
Integer i = map.get(methodName);
return (i != null ? i.intValue() : 0);
}
//获取全部方法的总调用次数
public int getCalls() {
return allCount;
}
public boolean equals(Object other) {
return (other != null && other.getClass() == this.getClass());
}
public int hashCode() {
return getClass().hashCode();
}
}

2.2 AfterAdvice

在Advice的实现体系中,Spring还提供了AfterAdvice这样的通知类型,这里以AfterReturningAdvice通知的实现为例,代码例如以下:

public interface AfterReturningAdvice extends AfterAdvice {
//后置通知的回调方法。在目标方法对象调用结束并成功返回之后调用
// returnValue參数为目标方法对象的返回值。method參数为目标方法对象,args为
//目标方法对象的输入參数
void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;
}

afterReturning方法也是一个回调函数。AOP应用须要在这个接口实现中提供切面增强的详细设计,在这个Advice通知被正确配置以后,在目标方法调用结束并成功返回的时候,接口会被SpringAOP调用。与前面分析的一样,在Spring AOP包中,相同能够看到CountingAfterReturningAdvice。实现基本一致:

public class CountingAfterReturningAdvice extends MethodCounter implements AfterReturningAdvice {
//实现后置通知AfterReturningAdvice的回调方法
public void afterReturning(Object o, Method m, Object[] args, Object target) throws Throwable {
//调用父类MethodCounter的count方法。统计方法的调用次数
count(m);
}
}

在实现AfterReturningAdvice的接口方法afterReturning中。能够调用MethodCounter的count方法,从而完毕依据方法名对目标方法调用次数的统计。

2.3 ThrowsAdvice

以下我们来看一下Advice通知的还有一种类型ThrowsAdvice。

对于ThrowsAdvice,并没有制定须要实现的接口方法,他在抛出异常时被回调。这个回调是AOP使用反射机制来完毕的。能够通过CountingThrowsAdvice来了解ThrowsAdvice的用法:

public static class CountingThrowsAdvice extends MethodCounter implements ThrowsAdvice {
//当抛出IO类型的异常时的回调方法,统计异常被调用的次数
public void afterThrowing(IOException ex) throws Throwable {
count(IOException.class.getName());
}
//当抛出UncheckedException类型异常时的回调方法,统计异常被调用的次数
public void afterThrowing(UncheckedException ex) throws Throwable {
count(UncheckedException.class.getName());
}
}

3、Pointcut切点

决定Advice通知应该作用于哪个连接点。也就是说通过Pointcut来定义须要增强的方法的集合,这些集合的选取能够依照一定的规则来完毕。Pointcut通常意味着标识方法,比如,这些须要增强的地方能够由某个正則表達式进行标识,或依据某个方法名进行匹配。源代码例如以下:

public interface Pointcut {
//获取类过滤器
ClassFilter getClassFilter();
//获取匹配切入点的方法
MethodMatcher getMethodMatcher();
//总匹配的标准切入点实例
Pointcut TRUE = TruePointcut.INSTANCE;
}

查看Pointcut切入点的继承体系,发现Pointcut切入点的实现类非常的多,如针对注解配置的AnnotationMatchingPointcut、针对正則表達式的JdkRegexpMethodPointcut等等,我们以JdkRegexpMethodPointcut为例,分析切入点匹配的详细实现。源代码例如以下:

public class JdkRegexpMethodPointcut extends AbstractRegexpMethodPointcut {
//要编译的正則表達式模式
private Pattern[] compiledPatterns = new Pattern[0];
//编译时要排除的正則表達式模式
private Pattern[] compiledExclusionPatterns = new Pattern[0];
//将给定的模式字符串数组初始化为编译的正則表達式模式
protected void initPatternRepresentation(String[] patterns) throws PatternSyntaxException {
this.compiledPatterns = compilePatterns(patterns);
}
//将给定的模式字符串数组初始化为编译时要排除的正則表達式模式
protected void initExcludedPatternRepresentation(String[] excludedPatterns) throws PatternSyntaxException {
this.compiledExclusionPatterns = compilePatterns(excludedPatterns);
}
//使用正則表達式匹配给定的名称
protected boolean matches(String pattern, int patternIndex) {
Matcher matcher = this.compiledPatterns[patternIndex].matcher(pattern);
return matcher.matches();
}
//使用要排除的正則表達式匹配给定的名称
protected boolean matchesExclusion(String candidate, int patternIndex) {
Matcher matcher = this.compiledExclusionPatterns[patternIndex].matcher(candidate);
return matcher.matches();
}
//将给定的字符串数组编译为正则表达的模式
private Pattern[] compilePatterns(String[] source) throws PatternSyntaxException {
Pattern[] destination = new Pattern[source.length];
for (int i = 0; i < source.length; i++) {
destination[i] = Pattern.compile(source[i]);
}
return destination;
}
}

4、Advisor通知器

完毕对目标方法的切面增强设计(Advice)和关注点的设计(Pointcut)以后。须要一个对象把他们结合起来,完毕这个作用的就是Advisor。

通过他能够定义应该使用哪个通知并在哪个关注点使用它。在DefaultPointcutAdvisor中有两个属性。各自是advice和Pointcut。通过这两个属性,能够分别配置Advice和Pointcut。源代码例如以下:

public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {
//默认切入点
//Pointcut.TRUE在切入点中的定义为:Pointcut TRUE = TruePointcut.INSTANCE;
private Pointcut pointcut = Pointcut.TRUE;
//无參构造方法。创建一个空的通知器
public DefaultPointcutAdvisor() {
}
//创建一个匹配全部方法的通知器
public DefaultPointcutAdvisor(Advice advice) {
this(Pointcut.TRUE, advice);
}
//创建一个指定切入点和通知的通知器
public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {
this.pointcut = pointcut;
setAdvice(advice);
}
//为通知设置切入点
public void setPointcut(Pointcut pointcut) {
this.pointcut = (pointcut != null ? pointcut : Pointcut.TRUE);
}
//获取切入点
public Pointcut getPointcut() {
return this.pointcut;
}
public String toString() {
return getClass().getName() + ": pointcut [" + getPointcut() + "]; advice [" + getAdvice() + "]";
}
}

上述源代码中,通知器的默认切入点是Pointcut.TRUE,Pointcut.TRUE在切入点中的定义为:Pointcut TRUE = TruePointcut.INSTANCE

TruePointcut的INSTANCE是一个单件。比方使用static类变量来持有单件实例,使用private私有构造函数来确保除了在当前单件实现中。单件不会被再次创建和实例化。

TruePointcut和TrueMethodMatcher的实现如代码例如以下:

/**
* Canonical Pointcut instance that always matches.
*
* @author Rod Johnson
*/
@SuppressWarnings("serial")
class TruePointcut implements Pointcut, Serializable {
public static final TruePointcut INSTANCE = new TruePointcut(); /**
* Enforce Singleton pattern.
* 这里是单件模式的实现特点,设置私有构造函数,使其不能直接被实例化
* 并设置一个静态类变量来保证该实例是唯一的
*/
private TruePointcut() {
}
public ClassFilter getClassFilter() {
return ClassFilter.TRUE;
}
public MethodMatcher getMethodMatcher() {
return MethodMatcher.TRUE;
}
/**
* Required to support serialization. Replaces with canonical
* instance on deserialization, protecting Singleton pattern.
* Alternative to overriding {@code equals()}.
*/
private Object readResolve() {
return INSTANCE;
}
@Override
public String toString() {
return "Pointcut.TRUE";
}
}
/**
* Canonical MethodMatcher instance that matches all methods.
*
* @author Rod Johnson
*/
@SuppressWarnings("serial")
class TrueMethodMatcher implements MethodMatcher, Serializable {
public static final TrueMethodMatcher INSTANCE = new TrueMethodMatcher();
/**
* Enforce Singleton pattern.
*/
private TrueMethodMatcher() {
}
public boolean isRuntime() {
return false;
}
public boolean matches(Method method, Class targetClass) {
return true;
}
public boolean matches(Method method, Class targetClass, Object[] args) {
// Should never be invoked as isRuntime returns false.
throw new UnsupportedOperationException();
}
/**
* Required to support serialization. Replaces with canonical
* instance on deserialization, protecting Singleton pattern.
* Alternative to overriding {@code equals()}.
*/
private Object readResolve() {
return INSTANCE;
}
@Override
public String toString() {
return "MethodMatcher.TRUE";
}
}

下一篇我们開始一起学习Spring AOP究竟是什么实现的

未完待续……

Spring技术内幕:Spring AOP的实现原理(一)的更多相关文章

  1. Spring技术内幕总结 - AOP概述

    AOP是Aspect-Oriented Programming(面向方面/切面编程)的简称.Aspect是一种新的模块化机制,用来描述分散在对象.类或函数中的横切关注点.分离关注点使解决特定领域问题的 ...

  2. Spring技术内幕:SpringIOC原理学习总结

    前一段时候我把Spring技术内幕的关于IOC原理一章看完,感觉代码太多,不好掌握,我特意又各方搜集了一些关于IOC原理的资料,特加深一下印象,以便真正掌握IOC的原理. IOC的思想是:Spring ...

  3. Spring技术内幕:设计理念和整体架构概述(转)

    程序员都很崇拜技术大神,很大一部分是因为他们发现和解决问题的能力,特别是线上出现紧急问题时,总是能够快速定位和解决. 一方面,他们有深厚的技术基础,对应用的技术知其所以然,另一方面,在采坑的过程中不断 ...

  4. 《spring技术内幕》读书笔记(1)——什么是POJO模式

    今天在看<spring技术内幕>,第一章中多次提到了使用POJO来完成开发,就百度了一下,在此保留 1.     什么是POJO POJO的名称有多种,pure old java obje ...

  5. 深入探索spring技术内幕(一): spring概述

    一.Spring是什么? Spring是一个开源的控制反转 ( IoC ) 和面向切面 ( AOP ) 的容器框架, 它的主要目的是简化企业开发. 二.控制反转(IoC) 控制反转: 所谓的控制反转就 ...

  6. 《Spring技术内幕》笔记-Spring的设计理念和总体架构

    1.Spring的主要子项目:     -1.Spring Framework(Core):Spring项目的核心.提供IoC,AOP,MVC等核心功能.     -2.Spring Web Flow ...

  7. Spring技术内幕:Spring AOP的实现原理(二)

    **二.AOP的设计与实现 1.JVM的动态代理特性** 在Spring AOP实现中, 使用的核心技术时动态代理.而这样的动态代理实际上是JDK的一个特性.通过JDK的动态代理特性,能够为随意Jav ...

  8. Spring技术内幕:Spring AOP的实现原理(三)

    生成SingleTon代理对象在getSingleTonInstance方法中完毕,这种方法时ProxyFactoryBean生成AopProxy对象的入口.代理对象会封装对target目标对象的调用 ...

  9. Spring技术内幕:Spring AOP的实现原理(五)

    7.Advice通知的实现 AopProxy代理对象生成时,其拦截器也一并生成.以下我们来分析下Aop是怎样对目标对象进行增强的.在为AopProxy配置拦截器的实现中,有一个取得拦截器配置过程,这个 ...

随机推荐

  1. Hibernate学习之关系映射(转)

    一.一对多 "一对多"是最普遍的映射关系,简单来讲就如消费者与订单的关系.一对多:从消费者角的度来说一个消费者可以有多个订单,即为一对多.多对一:从订单的角度来说多个订单可以对应一 ...

  2. Android ActionBar详解(一)--->显示和隐藏ActionBar

    MainActivity如下: package cc.testsimpleactionbar0; import android.os.Bundle; import android.view.View; ...

  3. R与数据分析旧笔记(二)随机抽样的一个综合例子

    题目:模拟产生统计专业同学的名单(学号区分),记录数学分析.线性代数.概率统计三科成绩,然后进行一些统计分析 > num=seq(10378001,10378100) > num [1] ...

  4. 5.7.2.1 Math对象

    ECMAScript还为保存数学公司公式和信息提供了一个公共位置,即Math对象.与我们在JavaScript直接编写的计算功能相比,Math对象提供的计算功能执行起来要快得多.Math对象中还提供了 ...

  5. phpstorm 同步远程服务器代码

    1.打开view —Toolbar,点击红框中的小工具 2.单击Deployment,在connection中填写对应选项, 其中:type勾选sftp root path :点击后面的Autodet ...

  6. 阿里云ECS每天一件事D6:安装nginx-1.6.2

    自从接触nginx就开始喜欢上这个小东西了,似乎没什么特别的原因,就是喜欢而已. 1.安装环境的准备 yum install pcre pcre-devel openssl openssl-devel ...

  7. 深度探索QT窗口系统(五篇)

    窗口作为界面编程中最重要的部分,没有窗口就没有界面,是窗口让我们摆脱了DOS时代,按钮是一个窗口,文本框是一个窗口,标签页是一个窗口,...一个窗口可以由多个窗口组成,每天我们都在与窗口打交道,当你打 ...

  8. sqlite 查询数据 不用回调

    int main( void ){    sqlite3 *db=NULL;    char *zErrMsg = 0;    int rc;    //打开数据库连接    rc = sqlite3 ...

  9. C++使用OLE高速读写EXCEL的源码

    我的代码参考的地方是这儿,再次感谢原作者 http://blog.csdn.net/gyssoft/archive/2007/04/29/1592104.aspx 我根据自己的需要做了整理,干净了一点 ...

  10. Sencha Touch 2 结合HTML5的本地存储创建数据库实现增、删、改、查

    大家好!我是范范.本人刚接触ST2到现在刚刚两个月,6月1号接的项目,那时才知道有Sencha Touch2这个东西,到现在两个月了期间的幸酸就不说了.今天说说在项目中用到的HTML5的本地存储.可能 ...