读不在三更五鼓,功只怕一曝十寒。 —— 郭沫若

@[toc]

一、导言

随着软件世界一直复杂化,传统的OOP(面向对象)建模思路曾经不足以很好的解决好开发时所面临的种种挑战,AOP(Aspect Orient Programming)应运而生,它和OOP建模形式并不抵触,它是OOP编程的一种无效补充。

OOP面向名词领域建模,应用类作为单位来模块化指标零碎,而AOP面向动词领域建模,其模块化单位则是Aspect:切面。

常见于解决一些具备==横切性质==的零碎级服务,例如: 日志治理、事务管理、安全检查缓存、对象池治理等

本文首先介绍了AOP次要概念和常见实现原理,后就动静代理引入SpringAOP,并简略介绍了SpringAOP的原理和其反对的切面,最初做了一个总结。

二、AOP

2.1. 代理模式

说到AOP不得不说说代理模式。代理模式顾名思义,不间接拜访指标对象,而是通过一个代理对象来间接拜访指标对象。客户端代码隔着一层代理对象拜访指标对象,代理对象能够对拜访过程做各种加工、管制。如下类图简洁明了的展现了代理模式的依赖关系、类构造。

ProxyPatternDemo类是客户端代码,Image是形象接口,代理类ProxyImage和指标类ReadlImage都实现Image,客户端代码通过代理对象能力拜访指标对象。

<img src="https://www.runoob.com/wp-content/uploads/2014/08/20201015-proxy.svg" style="zoom:70%;" />

2.2. AOP外围概念

常见的AOP框架都应用代理的形式给JointPoint~上面会介绍~增加“行为加强”,例如日志加强、事务加强等,具体能够了解为应用代理模式==通明的==的封装了客户端代码对被代理对象的拜访。

2.2.1. Aspect 切面

Aspect对标OOP中的class的含意,封装==横切点的逻辑==和横切点逻辑的==作用范畴==。切面由切入点和告诉组成,它既蕴含了横切逻辑的定义,也包含了切入点的定义。具体的日志、事务、线程池治理等逻辑都在横切逻辑的Advice(告诉)~上面有介绍~里实现。

2.2.1.1. PointCut 切入点

PointCut是用于==限定横切逻辑作用范畴==的谓词限定式,其外围是切点表达式。由“切点表达式匹配JointPpoint”的概念是AOP的外围,SpringAOP 默认应用AspectJ切点表达式语言,在理论应用上有以下两种模式,前者能够将切入点表达式形象进去,在SpringAOP中就是形象成一个办法:

  1. 应用@PointCut注解申明。
  2. 间接在Advice办法的注解里申明。
2.2.1.2. Advice 告诉

Advice封装具体的横切逻辑。不同的横切逻辑能够以around、before、after、return、afterThrow等模式体现。Spring AOP以拦截器的形式实现,并保护了一个以连接点~jointPoint~为核心的拦截器链。

2.2.2. JoinPoint 连接点

JoinPoint是横切逻辑执行时所关联的点。在JointPoint的“前后左右等”方向上执行Advice逻辑。PointCut表达式指定了JointPoint地位,Advice的体现模式~after、before等~指定了以什么模式在JointPoint“左近”执行 。

2.2.3. Introduction 引入

能够简略的了解为被代理对象增加办法或字段。

2.2.4. Targect Objecj 指标对象

被代理对象。被AOP框架解决的对象

2.2.5 Weaving 织入

织入是创立Advice代理对象并将Aspect代理对象和业务逻辑对象连接起来的过程。

织入能够在编译时,类加载时和运行时实现。在编译时进行织入就是动态代理,而在运行时进行织入则是动静代理。

2.3. 常见AOP实现原理

常见的AOP实现都是基于代理模式的,能够分为:1.动态代理 2.动静代理两种。

动态代理: 在java畛域中,最常见的基于动态代理的实现是AspectJ(官网链接),目前曾经更新到AspectJ 9了。AspectJ反对编译时、编译后、类加载时织入。具体原理和应用办法暂且略过不表。

动静代理: 动静代理能够分为基于JDK动静代理CGLIB动静代理。都是在内存中长期为指标对象生成一个代理对象,客户端代码==通明的==通过调用代理对象提供的办法来拜访指标对象的办法。

而目前市面上最常见的基于动静代理的AOP实现是SpringAOP

三、SpringAOP

SpringAOPSpring框架的一个要害组件,其是基于Spring IOC容器而实现,与IOC容器无缝连接。SpringAOP基于动静代理、纯java形式实现,不须要特定的编译过程,也不须要关怀classLoader类加载机制,实质上就是应用JDK动静代理或CGLIB来动静生成一个代理类。

3.1. SpringAOP代理原理浅谈

当被代理对象有对应接口~所有的要被代理的办法在这个接口里都有申明~时,SpringAOP应用JDK动静代理实现,当被代理对象没有对应的接口时,应用CGLIB实现。

CGLIB能够了解为继承被代理类而动静产生一个代理对象,这和传统的代理模式有一点区别~传统代理模式有共有接口~。因为是应用继承机制来实现AOP,所以SpringAOP不反对对final办法的代理,更不反对对fianl类的代理,因为子类无奈对父类办法进行override(重写)。

3.2. SpringAOP和AspectJ

SpringAOP目前只反对办法级别的AOP反对,我没有实现字段拦挡,如果须要对字段拜访进行AOP解决的话,能够思考应用AspectJ语言。

SpringAOP不同于大多其余的AOP实现,SpringAOP指标不是提供最残缺的AOP实现。它的亮点在于AOP和SpringIOC之间的紧密结合(尽管曾经很好用了),它非侵入的实现了AOP,不依赖于AspectJ编译器和AspectJ织入器。在SpringAOP中应用一般的bean定义来定义Aspect,SpringAOP和AspectJ是互补的,不是非此即彼的。

下表是摘自这里的SpringAOP和AspectJ的要害区别:

Spring AOPAspectJ
在纯 Java 中实现应用 Java 编程语言的扩大实现
不须要独自的编译过程除非设置 LTW,否则须要 AspectJ 编译器 (ajc)
只能应用运行时织入运行时织入不可用。反对编译时、编译后和加载时织入
性能不强-仅反对办法级编织更弱小 - 能够编织字段、办法、构造函数、动态初始值设定项、最终类/办法等......。
只能在由 Spring 容器治理的 bean 上实现能够在所有域对象上实现
仅反对办法执行切入点反对所有切入点
代理是由指标对象创立的, 并且切面利用在这些代理上在执行应用程序之前 (在运行时) 前, 各方面间接在代码中进行织入
比 AspectJ 慢多了更好的性能
易于学习和利用绝对于 Spring AOP 来说更简单

3.3. SpringAOP Advice

3.3.1. Before Advice : 前置告诉

在JointPoint前执行的Advice,不可阻止JointPoint处办法的执行~Around型能够~。应用@Before定义,具体源码如下所示。

@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface Before {    String value();    // 联合SpringAOP的命名绑定机制(name binding)应用,反对的PCD有: target、this、args    String argNames() default "";}

附: PCD(PointCut Designators 切点指示器),是切点表达式的重要组成部分。

3.3.2. After : 后置告诉

不论被代理办法是失常完结还是异样完结,都会执行这个Advice。

@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface After {    String value();    // 同上    String argNames() default "";}

3.3.3. AfterReturning : 后置返回告诉

在JointPoint后执行的Advice,当JointPoint处的办法失常执行完结后,会执行这个Advice,如果是异样执行话则不会。应用@AfterReturning定义,具体源码如下所示。

@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface AfterReturning {    String value() default "";    // 和value同一个作用    String pointcut() default "";    // 用于申明返回值    String returning() default "";    // 同上    String argNames() default "";}

能够简略的看成带返回值的Afteradvice。

3.3.4. AfterThrowing : 后置异样告诉

@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface AfterThrowing {    String value() default "";    String pointcut() default "";    // 当throwing指定的异样产生时会调用这个Advice    String throwing() default "";    // 同上    String argNames() default "";}

失常完结不会调用这个Advice,当指定异样产生时会调用这个Advice。

3.3.5. Around: 盘绕告诉

@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface Around {    String value();    // 同上    String argNames() default "";}// 常和ProceedingJoinPoint类结伴应用

==最弱小的Advice==,简直所有的其余类型的Advice都能够应用盘绕告诉来实现。但Spring官网倡议选用“能实现所需行为的性能最小的告诉类型”: 提供最简略的编程模式,缩小了出错的可能性。

四、总结

一般来说,咱们能够通过手动编写的模式来实现代理模式,但当须要被代理的对象、类==有很多==时,手动编写的形式就不太适合,AOP就是适宜这种有很多横切点的且横切点能够形象出对立逻辑的一种编程范型。

举一个在理论生产中很常见的例子:

如果某些业务逻辑接口的调用都须要在办法开始前和办法开始之后打日志,能够应用AOP切面的形式在所有的业务逻辑办法~JoinPoint~处定义两个Advice: before和after类型的。

五、参考链接

  1. 面试官:什么是AOP?Spring AOP和AspectJ的区别是什么?
  2. 代理模式
  3. 原生AOP用法剖析
  4. 了解AOP基本概念
  5. 代理模式是什么,SpringAOP还和它有关系?
==一键三连==反对下吧