我们在使用Spring框架的过程中,其实就是为了使用IOC和AOP,IoC容器是Spring的核心,AOP也是Spring框架的重要组成部分。之前的一篇文章聊聊Spring中IOC的基本原理介绍了Spring中Ioc的内容。那么这篇文章就来介绍Sping的Aop。
1. 什么是AOP
在说AOP之前,先来聊聊一个和AOP很像的东西——OOP(是不是很像,就差一个字母)。
OOP是Object Oriented Programing的简称,也就是我们最常说的面向对象编程。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合,这也是OOP的三大特性。
1、封装
隐藏对象的属性和实现细节,仅对外提供公共访问方式,将变化隔离,便于使用,提高复用性和安全性。
2、继承
就是子类自动继承其父级类中的属性和方法,并可以添加新的属性和方法或者对部分属性和方法进行重写。继承增加了代码的可重用性;继承是多态的前提。
3、多态
子类继承了来自父级类中的属性和方法,并对其中部分方法进行重写。于是多个子类中虽然都具有同一个方法,但是这些子类实例化的对象调用这些相同的方法后却可以获得完全不同的结果,这种技术就是多态性。父类或接口定义的引用变量可以指向子类或具体实现类的实例对象。提高了程序的拓展性。
由于这三大特性,可以采用OOP的编程思想设计出高内聚、低耦合的系统结构,使得系统更灵活、更容易扩展,而且成本较低。
但是,OOP允许我们定义从上到下的关系,但并不适合定义从左到右的关系。所以当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。比如最常用的日志功能。由于日志代码都是水平地分散在所有的对象层次中,但是又与它所分散到的对象的核心功能并没有什么关系。所以在OOP的设计思想中,它导致了大量代码的重复,而不利于各个模块的重用。
这种分散在各处的又无关的代码被称之为横切(cross-cutting)代码。既然OOP代码也存在不足之处,那么肯定有针对不足之处的解决办法。
AOP(Aspect-Oriented Programming,面向方面编程),可以说是对OOP的补充和完善。AOP利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为 “Aspect”,翻译过来即切面,**方面,**所以AOP称之为面向切面编程。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任(比如日志代码、权限校验)封装起来,作为公共的方法或者接口进行调用。本来十个系统就有十个权限校验、日志的代码,这样进行处理之后就十个系统都调用这一个日志或者权限代码就可以了。这样就有利于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
所以OOP代表的是一种上下的关系,而AOP代表的是一个横向的关系。
AOP把系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。AOP的作用就是将系统中的各种关注点,核心关注点和横切关注点切开。
2. AOP的一些重要概念
实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的 方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。它们都有一些共同的特性:
**Weaving:**即织入,就是通过动态代理,在目标对象方法中执行处理内容的过程。
Pointcut:即切点,决定公共业务如权限校验、日志记录等在何处切入业务代码中(即织入切面)。切点分为execution方式和annotation方式。前者可以用路径表达式指定哪些类织入切面,后者可以指定被哪些注解修饰的代码织入切面。
execution格式:
以 下面表达式为例:
@Pointcut("execution(* com.jiang.controller..*.*(..)))") 复制代码
第一个 * 号的位置:表示返回值类型,* 表示所有类型。
包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,在本例中指 com.mutest.controller包、子包下所有类的方法。
第二个 * 号的位置:表示类名,* 表示所有类。
*(..):这个星号表示方法名,* 表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。
annotation() 格式:
annotation() 方式是针对某个注解来定义切点,比如我们对具有 @RequestMapping 注解的方法做切面,可以如下定义切面:
@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)") 复制代码
Advice:处理,又称之为通知。是pointcut的执行代码,是执行“方面”的具体逻辑。包括处理时机和处理内容。处理内容就是要做什么事,比如校验权限和记录日志。处理时机就是在什么时机执行处理内容,分为前置通知(即业务代码执行前)、后置通知(业务代码执行后)等。常见的通知有:
前置通知 :方法执行前调用 ,对应的注解是 @Before
后置通知 ** :方法执行后调用 ,对应的注解是@After**
返回通知 ** :方法返回后调用 ,对应注解是@AfterReturning**
异常通知 :方法出现异常调用,对应注解是**@AfterThrowing**
环绕通知 ** :动态代理、手动推荐方法运行,对应的注解是@Around**
Aspect:即切面,pointcut和advice结合起来就是aspect,它类似于OOP中定义的一个类,但它代表的更多是对象间横向的关系。
Joint point:连接点,是程序执行的一个点。例如,一个方法的执行或者一个异常的处理。在 Spring AOP 中,一个连接点总是代表一个方法执行。
AOP的体系可以总结如下图所示:
3.举个栗子
这个例子的基础是之前的一篇文章的项目基础,这里就不再赘述,可以参考SpringBoot开发Restful风格的接口实现CRUD功能。在这个项目的基础之上,来演示AOP的使用,创建一个AOP相关类,如下所示:
package com.springboot.springbootdemo.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.SourceLocation; import org.springframework.stereotype.Component; /** * the simple demo for aop * author:jiangxia * date:2021-07-13 */ @Aspect @Component public class LogInterceptor { //抽取公共的切入点表达式 //1、本类引用 //2、其他的切面引用 // 定义一个切点:所有被RequestMapping注解修饰的方法会织入advice @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)") public void aopMethod(){ } //@Before在目标方法之前切入;切入点表达式(指定在哪个方法切入) @Before("aopMethod()") public void before(){ System.out.println("aopMethod start"); } @Around("aopMethod()") public Object Around(ProceedingJoinPoint jp) throws Throwable { System.out.println("aopMethod Around"); SourceLocation sl = jp.getSourceLocation(); Object ret = jp.proceed(); System.out.println(jp.getTarget()); return ret; } @After("aopMethod()") public void after() { System.out.println("aopMethod after"); } @AfterReturning("aopMethod()") public void AfterReturning() { System.out.println("aopMethod AfterReturning"); } @AfterThrowing("aopMethod()") public void AfterThrowing() { System.out.println("aopMethod AfterThrowing"); } } 复制代码
然后启动项目:
这里以查询所有数据的请求为例,地址栏输入:
控制台也打印了相关信息:
4. 总结
Spring中的两大核心就是IOC和AOP。
在传统的程序设计中,当调用者需要被调用者的协助时,通常由调用者来创建被调用者的实例。但在spring里创建被调用者的工作不再由调用者来完成,因此控制反转(IoC);创建被调用者实例的工作通常由spring容器来完成,然后注入调用者,因此也被称为依赖注入(DI),依赖注入和控制反转是同一个概念。关于IOC的具体内容可以参考:聊聊Spring中IOC的基本原理。
这篇文章主要介绍了Spring中AOP的编程思想,AOP(面向切面编程)可以看成是对OOP(面向对象编程)的补充和拓展。AOP是以另一个角度来考虑程序结构,通过分析程序结构的关注点来完善OOP。OOP将应用程序分解成各个层次的对象,而AOP将程序分解成多个切面。AOP只实现了方法级别的连接点,这在J2EE应用中就已经足够了。在spring中,为了使IoC方便地使用健壮、灵活的企业服务,需要利用spring AOP实现为IoC和企业服务之间建立联系。
IOC采用的设计模式就是典型的工厂模式,主要通过sessionfactory去注入实例。而AOP就是典型的代理模式的体现。
Aop 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来,降低代码的耦合度,提高代码的可重复性和可拓展性,大大减少代码量。
以上就是我对于AOP的理解。
如果你觉得本文不错,就点赞分享给更多的人吧!
如果你觉得文章有不足之处,或者更多的想法和理解,欢迎指出讨论!