Spring之面向切面(AOP)

1.AOP简介

1.1 概念

AOP(Aspect Orient Programming),一般称为面向切面编程,作为面向对象的一种补充,用于处理系统中分布于各个模块的横切关注点,比如事务管理、日志、缓存等等。

散布于应用中多处的功能被称为横切关注点(cross-cutting concern),这些关注点从概念上是与应用业务逻辑分离的。而把这些横切关注点与业务逻辑相分离正式面向切面编程(AOP)要解决的问题。

AOP实现的关键在于AOP框架自动创建的AOP代理,AOP代理主要分为静态代理和动态代理,静态代理的代表为AspectJ;而动态代理则以Spring AOP为代表。

1.2 基本术语

连接点(Joinpoint):指那些被拦截到的点。在spring中指的是方法,因为spring只支持方法类型的连接点。

切入点(Pointcut):指要对哪些Joinpoint进行拦截的定义。

通知(Advice):指拦截到Joinpoint之后所要做的事情,分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)。

切面(Aspect):是切入点和通知的结合,把增强应用到具体方法上面,此过程称为切面 。

引入(Introduction):一种特殊的通知。在不修改类代码的前提下,引介可以在运行期为类动态的添加一些方法或者Field。

目标对象(Target):所代理的目标对象(要增强的类)。

织入(Weaving):将advice应用到target的过程。

代理(Proxy):一个类被AOP织入增强后,就产生一个结果代理类。

2.实现原理

图2.1 Spring的切面由包裹了目标对象的代理类实现。代理类处理方法的调用,执行额外的切面逻辑,并调用目标方法

Spring在运行时通知对象。通过在代理勒种包裹切面,Spring在运行期把切面织入到Spring管理的bean中。如图2.1所示,代理类封装了目标类,并且拦截被通知方法的调用,再把调用转发给真正的目标bean。当代理拦截到方法调用时,在调用目标bean方法之前,会执行切面逻辑。
通过JDKProxy或CGLibProxy动态生成代理对象,当从外部调用目标对象的方法时,外部调用的对象为动态代理生成的Proxy对象。因此外部调用的方法实际为Proxy对象中的实现的方法,这个方法中包含了AOP中设置的Advice。然后通过反射机制调用目标对象的处理逻辑方法,从而达到对目标对象方法的增强。
如何判断是否执行增强的通知,在执行代理对象的方法时CglibAopProxy调用intercept(Object, Method, Object[], MethodProxy)方法,JDKProxy调用JdkDynamicAopProxy.invoke(Object, Method, Object[])方法,在这两个方法中我们可以看到,在调用目标对象的方法前会先去获取该目标对象方法的拦截器链:

 // Get the interception chain for this method.
 List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

获取拦截器链为空则直接调用目标对象的方法,拦截器链不为空则执行拦截器链中需要增强的通知方法,这样就达到了AOP动态增强的目标。

拦截器拦截原理
Dispatchservlet在执行dodispatch方法时会根据request查询匹配的HandlerMapping,匹配请求URL的HandlerMapping会去查找所有拦截匹配该请求的拦截器,并将handler和所有的拦截器HandlerInterceptor组成处理执行器链HandlerExecutionChain返回给dispatchservlet。dispatchservlet在执行处理器的方法前先调用所有HandlerInterceptor的preHandle、postHandle、afterCompletion方法。

2.1 静态代理

静态和动态是由代理产生的时间段来决定的。静态代理产生于代码编译阶段,即一旦代码运行就不可变了

2.2 动态代理

那能否通过实现一次代码即可织入到函数中呢,答案当然是可以的,此时就要用到java中的反射机制。它的好处理时可以为我们生成任何一个接口的代理类,并将需要增强的方法织入到任意目标函数。但它仍然具有一个局限性,就是只有实现了接口的类,才能为其实现代理。

2.3 CGLIB

CGLIB解决了动态代理的难题,它通过生成目标类子类的方式来实现来实现代理,而不是接口,规避了接口的局限性。CGLIB是一个强大的高性能代码生成包,其在运行时期(非编译时期)生成被 代理对象的子类,并重写了被代理对象的所有方法,从而作为代理对象。当然CGLIB也具有局限性,对于无法生成子类的类(final类),肯定是没有办法生成代理子类的。

3.在XML中声明切面

步骤

(1)xml文件中配置bean对象(包含增强类和被增强类)
(2)配置aop操作
配置切入点
配置切面

案例:为类Book在实现增加功能add()方法之前,打印一条日志。

<!-- 1.配置对象 -->

<bean id="book" class="com.myaop.Book"></bean>

<bean id="myBook" class="com.myaop.MyBook"></bean>

<!-- 2.配置aop操作 -->

<aop:config>

<!-- 2.1 配置切入点 -->

<aop:pointcut expression="execution(* com.myaop.Book.*(..))" id="pointcut1">

<!-- 2.2 配置切面。把增强用到方法上面 -->

<aop:aspect ref="myBook">

<!-- 配置增强类型。method表示增强类中使用哪个方法作为前置 -->

<aop:before  method="before1" pointcut-ref="pointcut1">

</aop:aspect>

</aop:config>

测试类代码:

ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");

Book book = (Book)context.getBean("book");

book.add();

环绕通知实现原理

入参:ProceedingJoinPoint,表示切入点

通过调用方法proceed()实现

//方法之前要做的逻辑
doBefore();

//执行被增强的方法
proceedingJoinPoint.proceed();

//方法之后要做的逻辑
doAfter();

4.使用注解创建切面

基于aspectj的注解实现aop

(1)创建对象

(2)在spring核心配置文件中,开启aop代理

(3)在增强类上使用注解完成aop操作

案例:为类Book在实现增加功能add()方法之前,打印一条日志。

1.创建对象

<!-- 1.配置对象 -->

<bean id="book" class="com.myaop.Book"></bean>

<bean id="myBook" class="com.myaop.MyBook"></bean>

2.开启aop代理


<!-- 2.开启aop操作 -->

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

3.在增强类中使用注解完成aop操作

在通知中通过value属性定义切点


@Aspect

public class MyBook{

//在方法上使用注解完成增强配置

@Before(value="execution(* com.myaop.Book.*(..))")

public void before1(){

System.out.println(before........);

}

}

5.Demo实现

在XML中声明切面方式实现
使用注解创建切面方式实现

参考文章

https://www.jianshu.com/p/503836e398ee
https://www.jianshu.com/p/a1caa1a64393

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