聊聊Spring中的AOP(笔面试必问)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 我们在使用Spring框架的过程中,其实就是为了使用IOC和AOP,IoC容器是Spring的核心,AOP也是Spring框架的重要组成部分。之前的一篇文章聊聊Spring中IOC的基本原理介绍了Spring中Ioc的内容。那么这篇文章就来介绍Sping的Aop。

我们在使用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的体系可以总结如下图所示:

e1d62c1544b54f89b7c1849a5ee5ce85~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg

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");
    }
}
复制代码


然后启动项目:


8557a37c6db34097a08768bd0b0f3f5e~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


这里以查询所有数据的请求为例,地址栏输入:


微信图片_20220516180828.png


控制台也打印了相关信息:


0bd35e7cf6ed4db5b4d84ad50ebd2f26~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp (1).jpg

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的理解。


如果你觉得本文不错,就点赞分享给更多的人吧!


如果你觉得文章有不足之处,或者更多的想法和理解,欢迎指出讨论!

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
8天前
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP
|
21天前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
|
21天前
|
缓存 Java 数据库
【Java面试题汇总】Spring篇(2023版)
IoC、DI、aop、事务、为什么不建议@Transactional、事务传播级别、@Autowired和@Resource注解的区别、BeanFactory和FactoryBean的区别、Bean的作用域,以及默认的作用域、Bean的生命周期、循环依赖、三级缓存、
【Java面试题汇总】Spring篇(2023版)
|
9天前
|
XML Java 开发者
经典面试---spring IOC容器的核心实现原理
作为一名拥有十年研发经验的工程师,对Spring框架尤其是其IOC(Inversion of Control,控制反转)容器的核心实现原理有着深入的理解。
31 3
|
22天前
|
Java 数据库连接 数据库
Spring基础3——AOP,事务管理
AOP简介、入门案例、工作流程、切入点表达式、环绕通知、通知获取参数或返回值或异常、事务管理
Spring基础3——AOP,事务管理
|
2月前
|
XML Java 数据格式
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
这篇文章是Spring5框架的AOP切面编程教程,通过XML配置方式,详细讲解了如何创建被增强类和增强类,如何在Spring配置文件中定义切入点和切面,以及如何将增强逻辑应用到具体方法上。文章通过具体的代码示例和测试结果,展示了使用XML配置实现AOP的过程,并强调了虽然注解开发更为便捷,但掌握XML配置也是非常重要的。
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
|
2月前
|
缓存 Java 开发者
Spring高手之路22——AOP切面类的封装与解析
本篇文章深入解析了Spring AOP的工作机制,包括Advisor和TargetSource的构建与作用。通过详尽的源码分析和实际案例,帮助开发者全面理解AOP的核心技术,提升在实际项目中的应用能力。
23 0
Spring高手之路22——AOP切面类的封装与解析
|
2月前
|
Java Spring XML
掌握面向切面编程的秘密武器:Spring AOP 让你的代码优雅转身,横切关注点再也不是难题!
【8月更文挑战第31天】面向切面编程(AOP)通过切面封装横切关注点,如日志记录、事务管理等,使业务逻辑更清晰。Spring AOP提供强大工具,无需在业务代码中硬编码这些功能。本文将深入探讨Spring AOP的概念、工作原理及实际应用,展示如何通过基于注解的配置创建切面,优化代码结构并提高可维护性。通过示例说明如何定义切面类、通知方法及其应用时机,实现方法调用前后的日志记录,展示AOP在分离关注点和添加新功能方面的优势。
38 0
|
2月前
|
缓存 安全 Java
Spring AOP 中两种代理类型的限制
【8月更文挑战第22天】
16 0
|
2月前
|
Java Spring
下一篇
无影云桌面