Spring AOP

转载自
作者:Wwwwei
链接://www.greatytc.com/p/da55782965d9
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

什么是AOP?

我们先来看一下比较官方的解释。
  AOP,Aspect Oriented Programming的缩写,意为面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
  关于AOP我在网上博文中发现了这样一句话:运行时,动态地将代码切入到类的指定方法或者指定位置。是不是豁然开朗了呢?
  这里还想强调一下运行时和动态两个点,运行时不难理解,如果在程序非运行时我们只要将代码写入指定位置再运行就好了,而程序运行时,代码就无法进行更改了,这也体现了不修改源代码的意义;动态这个概念我们可以这样理解,切入的过程并非是事先完成好的,而是在程序运行过程中触发了某个时机而进行的。
  此处介绍几个概念,便于读者理解:
  通知(advice):切入到类指定方法或者指定位置的代码片段,即需要增加的功能代码,也就是上述的那片火腿(临时会议)。
  连接点(join point):程序运行过程中能够进行插入切面操作的时间点。例如方法调用、异常抛出或字段修改等,可以理解为上述的长面包(老板的整个日程安排)。
  切入点(pointcut):描述一个通知将被切入的一系列连接点的集合,即代码片段具体切入到哪些类、哪些方法,也就是上述面包切口处(特指午后的日程)。所以说,切入点规定了哪些连接点可以执行哪些通知。
  切面(aspect):AOP中的切面等同于OOP中的类(class),由通知(advice)和切入点(pointcut)组成,其中通知(advice)和切入点(pointcut)既可以是1对1的关系,也可以是1对多的关系。概括的说就是描述了何时何地干何事的基本单元,其中通知(advice)说明了切面干何事,而切入点则说明了切面何时何地切入。
  关于几者的关系,我们可以这样理解,通知是在连接点上执行的,但是我们不希望通知应用到所有的连接点,所以引入了切入点来匹配特定的连接点,指名我们所希望通知应用的连接点。
  因此,所谓的AOP(面向切面编程)就是在程序运行过程中的某个时机将代码片段插入到某些类的指定方法和指定位置。


为什么要使用AOP?

程序的最终目的就是实现业务,但是我们在进行编程的过程中经常会发现除了所谓的业务代码,还存在数量相当的公共代码,类似日志、安全验证、事物、异常处理等问题。这部分代码重要但是与我们编写程序要实现的功能没有关系,具有功能相似、重用性高、使用场景分散等特点。我们姑且称它们为共性问题。
  对大多数程序而言,代码都是以纵向结构将各个业务模块串联从而完成功能的。我们提到的共性问题本身不属于业务范围,但是又散落在各个业务模块间,同实现主功能的代码相互杂糅在一起.
  试想一下,如果将共性问题部分的代码融入业务代码中,一旦涉及到对某个共性问题部分的代码进行更改的时候,例如日志部分发生需求变更,我们可能需要牵涉许许多多其他模块代码。这在小规模程序中也许是可以接受的,可能只修改1、2处;但是如果牵涉的地方数量过多,特别是应用在中大型规模程序中,我们甚至会为了小小的一个功能,修改上千、上万处。这样的方式是十分糟糕的,不仅费时费力,可能还会引起一些不必要的麻烦(回归错误、结构混乱等等)。
  AOP的就是为了解决这类共性问题,将散落在程序中的公共部分提取出来,以切面的形式切入业务逻辑中,使程序员只专注于业务的开发,从事务提交等与业务无关的问题中解脱出来。
AOP的好处
  解耦:AOP将程序中的共性问题进行了剥离,毫无疑问地降低了各个业务模块和共性问题之间的耦合。
  重用性:共性问题散落于业务逻辑的各处,十分难维护,使用AOP进行提取后,能够将相似功能的共性问题收敛,减少重复代码,提高了代码的重用性。
  拓展性:对于一个程序而言,迭代的重心一定在于业务和功能上。AOP使得每当发生变更时,可以只关注业务逻辑相关的代码,而减少共性问题上带来的变化,大大降低了程序未来拓展的成本。


Spring如何实现AOP?

什么是Spring AOP?  
  Spring AOP就是负责完成AOP相关工作的框架,它将切面所定义的横切逻辑切入到切面所指定的连接点中。框架的目的就是将复杂的事情变得简单易用,所以其主要工作分成了如下两点:
  1.提供相应的数据结构来定义AOP所需基本结构,例如通知、连接点、切入点、切面等。
  2.封装切面切入的相关工作,提供相应接口给用户。封装的内容主要包括如何通过切面(切入点和通知)定位到特定的连接点;如何将切面中的功能代码植入到特定的连接点中等等;提供相应接口主要包括配置文件、注解等用户能够使用的工具。
  这里想提一下,在 Spring AOP 中,连接点(join point)总是方法的执行点, 即只有方法连接点。所以我们可以认为,在 Spring 中所有的方法都可以是连接点。

怎么使用Spring AOP?
  由于Spring对AOP的封装,使得我们可以十分方便的使用,我们只需要定义切面,即定义Advice通知和Pointcut切入点
  (1)Spring AOP中Pointcut切入点
  使用@Pointcut("execution()")注解定义切入点,其中execution的格式如下所示:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?) 

其中,除ret-type-pattern和name-pattern之外,其他都是可选的,各部分具体含义如下:
  1.modifiers-pattern:方法的操作权限
  2.ret-type-pattern:返回值
  3.declaring-type-pattern:方法所在的包
  4.name-pattern:方法名
  5.parm-pattern:参数名
  6.throws-pattern:异常
  
  (2)Spring AOP中Advice通知
  before advice:前置通知,由@Before注解定义,表示在 join point 前被执行的advice。(虽然before advice是在 join point 前被执行,但是它并不能够阻止 join point 的执行, 除非发生了异常,即在before advice代码中, 不能人为地决定是否继续执行join point中的代码)
  after return advice:后置通知,由@AfterReturning注解定义,表示在一个 join point 正常返回后执行的advice。
  after throwing advice:异常通知,由@AfterThrowing注解定义,表示当一个 join point 抛出异常后执行的advice。
  after(final) advice:最终通知,由@After注解定义,表示无论一个join point是正常退出还是发生了异常,都会被执行的advice。
  around advice:环绕通知,由@Around注解定义,表示在join point 前和joint point退出后都执行的 advice,是最常用的advice。
  (3)举个例子
  首先,我们创建一个简单的UserService,作为示例的业务模块,代码如下:

package com.demo.aop;

import org.springframework.stereotype.Service;

/**
 * Created by wwwwei on 17/8/14.
 */
@Service
public class UserService {
    //创建用户
    public void createUser(String userName) {
        System.out.println("创建用户 " + userName);
    }

    //删除用户
    public void deleteUser(String userName) {
        System.out.println("删除用户 " + userName);
    }

    //更新用户
    public void updateUser(String userName) {
        System.out.println("更新用户 " + userName);
        throw new RuntimeException("更新用户抛出异常");
    }
}

其次,定义切面数据结构,即通知(需要增加的功能代码片段)和切入点(切入的时间点),代码如下:

package com.demo.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * Created by wwwwei on 17/8/11.
 */
@Component
@Aspect
public class UserAspect {
    //定义切入点
    @Pointcut("execution(* com.demo.aop.*Service*.*(..))")
    public void pointCut() {
    }

    //前置通知 @Before(value="execution(public * *(..))")
    @Before("pointCut()")
    public void mybefore() {
        System.out.println("前置通知");
    }

    //后置通知 @AfterReturning(value="execution(public * *(..))")
    @AfterReturning(pointcut = "pointCut()")
    public void myafterReturning() {
        System.out.println("后置通知");
    }

    //异常通知 @AfterThrowing(value="execution(public * *(..))")
    @AfterThrowing(pointcut = "pointCut()", throwing = "error")
    public void myafterThrowing() {
        System.out.println("异常通知");
    }

    //环绕通知 @Around(value="execution(public * *(..))")
    @Around("pointCut()")
    public void myAround(ProceedingJoinPoint jp) {
        System.out.println("环绕前通知");
        try {
            jp.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("环绕后通知");

    }

    //最终通知 @After(value="execution(public * *(..))")
    @After("pointCut()")
    public void myafterLogger() {
        System.out.println("最终通知");
    }
}

最后,编写测试类测试,代码如下:

package com.demo.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Created by wwwwei on 17/8/14.
 */
public class UserTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        UserService userService = context.getBean(UserService.class);
        userService.createUser("测试用户");
    }
}

AOP的实现

AOP的实现是基于代理机制的,根据不同实现方式主要分为两类:
  1.静态代理,AOP框架会在编译阶段生成AOP代理类,即在编译器和类装载期实现切入的工作,但是这种方式需要特殊的Java编译器和类装载器。AspectJ框架就是采用这种方式实现AOP。
  2.动态代理,AOP框架不会去修改字节码,而是在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的连接点(切入点)做了添加通知(advice)处理,并回调原对象的方法。
  与AspectJ的静态代理不同,Spring AOP使用动态代理,通过JDK Proxy和CGLIB Proxy两种方法实现代理。两种方式的选择与目标对象有关:

如果目标对象没有实现任何接口,那么Spring将使用CGLIB来实现代理。CGLIB是一个开源项目,它是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。
如果目标对象实现了一个以上的接口,那么Spring将使用JDK Proxy来实现代理,因为Spring默认使用的就是JDK Proxy,并且JDK Proxy是基于接口的。这也是Spring提倡的面向接口编程。当然,你也可以强制使用CGLIB来进行代理,但是这样可能会造成性能上的下降。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,123评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,031评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,723评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,357评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,412评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,760评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,904评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,672评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,118评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,456评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,599评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,264评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,857评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,731评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,956评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,286评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,465评论 2 348

推荐阅读更多精彩内容

  • 定义 官方解释 AOP,Aspect Oriented Programming的缩写,意为面向切面编程,通过预编译...
    一凡呀阅读 1,545评论 0 3
  • 因为工作需求,自己去了解一下aop并做下的记录,当然大部分都是参考他人博客以及官方文档。 目录 [关于 AOP](...
    forip阅读 2,271评论 1 20
  • 我们的程序从编写到执行,单个模块一般都是从上到下、垂直、连续的。AOP是一种“横切”技术,能够在合适的地方“拦腰截...
    消失er阅读 1,974评论 0 5
  • 基本知识 其实, 接触了这么久的 AOP, 我感觉, AOP 给人难以理解的一个关键点是它的概念比较多, 而且坑爹...
    永顺阅读 8,132评论 5 114
  • AOP 简介 在学习 Spring AOP 之前,我们先来了解一下 AOP。我们都听过面向对象编程(OOP),那么...
    樱木天亥阅读 899评论 0 3