Spring AOP源码学习:基本概念

简介: 本文的内容以 AspectJ 来进行介绍。

前言


之前用十几篇文章介绍了 Spring IoC的源码,作为与 IoC 齐名的 AOP 自然也不能错过。同样的,接下去将会通过几篇文章来解析 Spring AOP 的源码。

如何将 Spring 源码导入 IDEA,请参考:Spring IoC源码学习:总览

 

注:本文的内容以 AspectJ 来进行介绍。

 

关于AOP


百度百科:AOP Aspect Oriented Programming,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP OOP 的延续,是软件开发中的一个热点,也是 Spring 框架中的一个重要内容,是函数式编程的一种衍生范型。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

 

例子


我们有两个接口,一个用于进行加法计算,一个用于进行减法计算,为了避免计算出现问题,我们需要对每次接口调用的入参进行日志记录,于是我们有了以下的第一版实现。

image.png


看起来还不错,简单明了。但是这个方案有个问题,就是后续每次新增一个接口,就需要拷贝一次记录入参的代码。对于一个懒人,这是不可容忍的。好,提出一个公共方法,每个接口都来调用这个方法,于是我们有了以下第二版实现。这里有点切面的味道了。 

image.png

这个方案看起来更好了,但是同还是存在问题,虽然不用每次都拷贝代码了,但是,每个接口总得要调用这个方法吧,有办法让调用也省掉吗。我们设想一下,我们可以通过策略识别出所有要加入日志记录的接口,然后在接口调用时,将日志记录注入到接口调用的地方(切点),这就是 AOP 的核心思想。按这个思想,我们有了第三版的实现。

image.png

这样接口只需要关心具体的业务,而不需要关注其他非该接口关注的逻辑或处理。 红框处,就是面向切面编程的思想。

 

AOP 的常见概念


通过上面的例子,大家应该对 AOP 有了初步的认识,下面介绍下 AOP 涉及的相关概念。


Joinpoint(连接点):在系统运行之前,AOP 的功能模块都需要织入到具体的功能模块中。要进行这种织入过程,我们需要知道在系统的哪些执行点上进行织入过程,这些将要在其之上进行织入操作的系统执行点就称之为 Joinpoint,最常见的 Joinpoint 就是方法调用。


Pointcut(切点):用于指定一组 Joinpoint,代表要在这一组Joinpoint 中织入我们的逻辑,它定义了相应 Advice 将要发生的地方。通常使用正则表达式来表示。对于上面的例子,Pointcut 就是表示所有要加入日志记录的接口的一个表达式。例如:“execution(* com.joonwhee.open.demo.service..*.*(..))”


Advice(通知/增强)Advice 定义了将会织入到 Joinpoint 的具体逻辑,通过 @Before@After@Around 来区别在 JointPoint 之前、之后还是环绕执行的代码。


Aspect(切面)Aspect 是对系统中的横切关注点逻辑进行模块化封装的 AOP 概念实体。类似于 Java 中的类声明,在 Aspect 中可以包含多个 Pointcut 以及相关的 Advice 定义。


Weaving(织入):织入指的是将 Advice 连接到 Pointcut 指定的 Joinpoint 处的过程,也称为:将 Advice 织入到 Pointcut 指定的 Joinpoint 处。


Target(目标对象):符合 Pointcut 所指定的条件,被织入 Advice 的对象。

 

对于上面的例子来说:


·       加法接口减法接口每次被调用时所处的程序执行点都是一个 Jointpoint

·       Pointcut 就是用于指定加法接口减法接口的一个表达式,当然这个表达式还可以指定很多其他的接口,表达式常见的格式为:“execution(* com.joonwhee.open.demo.service..*.*(..))”

·       Aspect 是定义 AdvicePointcut 的地方

·       Advice 就是我们要在加法接口减法接口织入的日志记录逻辑

·       Weaving 就是指将日记记录逻辑加到加法接口减法接口的过程

·       Target 就是定义了加法接口减法接口的对象实例

 

整体如下图所示:

image.png


简单的使用


import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
 * @author joonwhee
 * @date 2019/3/3
 */
@Component
@Aspect
public class AopAspect {
    private static final Logger LOGGER = LoggerFactory.getLogger(AopAspect.class);
    @Pointcut("execution(* com.joonwhee.open.demo.service..*.*(..))")
    public void pointcut() {
    }
    @Before("pointcut()")
    public void before() {
        LOGGER.info("before advice");
    }
    @After("pointcut()")
    public void after() {
        LOGGER.info("after advice");
    }
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws InterruptedException {
        System.out.println("around advice start");
        try {
            Object result = proceedingJoinPoint.proceed();
            System.out.println("result: " + result);
            System.out.println("around advice end");
            return result;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            return null;
        }
    }
}

 

实现机制


Spring AOP 底层实现机制目前有两种:JDK 动态代理、CGLIB  动态字节码生成。在阅读源码前对这两种机制的使用有个认识,有利于更好的理解源码。

 

JDK 动态代理


public class MyInvocationHandler implements InvocationHandler {
    private Object origin;
    public MyInvocationHandler(Object origin) {
        this.origin = origin;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("invoke start");
        Object result = method.invoke(origin, args);
        System.out.println("invoke end");
        return result;
    }
}
public class JdkProxyTest {
    public static void main(String[] args) {
        UserService proxy = (UserService) Proxy.newProxyInstance(JdkProxyTest.class.getClassLoader(),
                new Class[]{UserService.class}, new MyInvocationHandler(new UserServiceImpl()));
        proxy.doSomething();
    }
}

 

CGLIB 代理

public class CglibInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("intercept start");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("intercept end");
        return result;
    }
}
public class CglibProxyTest {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(CglibObject.class);
        enhancer.setCallback(new CglibInterceptor());
        CglibObject proxy = (CglibObject) enhancer.create();
        proxy.doSomething();
    }
}
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
7天前
|
运维 Java 程序员
Spring5深入浅出篇:基于注解实现的AOP
# Spring5 AOP 深入理解:注解实现 本文介绍了基于注解的AOP编程步骤,包括原始对象、额外功能、切点和组装切面。步骤1-3旨在构建切面,与传统AOP相似。示例代码展示了如何使用`@Around`定义切面和执行逻辑。配置中,通过`@Aspect`和`@Around`注解定义切点,并在Spring配置中启用AOP自动代理。 进一步讨论了切点复用,避免重复代码以提高代码维护性。通过`@Pointcut`定义通用切点表达式,然后在多个通知中引用。此外,解释了AOP底层实现的两种动态代理方式:JDK动态代理和Cglib字节码增强,默认使用JDK,可通过配置切换到Cglib
|
14天前
|
安全 Java 测试技术
Spring Boot集成支付宝支付:概念与实战
【4月更文挑战第29天】在电子商务和在线业务应用中,集成有效且安全的支付解决方案是至关重要的。支付宝作为中国领先的支付服务提供商,其支付功能的集成可以显著提升用户体验。本篇博客将详细介绍如何在Spring Boot应用中集成支付宝支付功能,并提供一个实战示例。
36 2
|
19小时前
|
XML Java 数据格式
Spring高手之路18——从XML配置角度理解Spring AOP
本文是全面解析面向切面编程的实践指南。通过深入讲解切面、连接点、通知等关键概念,以及通过XML配置实现Spring AOP的步骤。
21 6
Spring高手之路18——从XML配置角度理解Spring AOP
|
23小时前
|
监控 Java 应用服务中间件
Spring Boot 源码面试知识点
【5月更文挑战第12天】Spring Boot 是一个强大且广泛使用的框架,旨在简化 Spring 应用程序的开发过程。深入了解 Spring Boot 的源码,有助于开发者更好地使用和定制这个框架。以下是一些关键的知识点:
12 6
|
2天前
|
Java 应用服务中间件 测试技术
深入探索Spring Boot Web应用源码及实战应用
【5月更文挑战第11天】本文将详细解析Spring Boot Web应用的源码架构,并通过一个实际案例,展示如何构建一个基于Spring Boot的Web应用。本文旨在帮助读者更好地理解Spring Boot的内部工作机制,以及如何利用这些机制优化自己的Web应用开发。
11 3
|
4天前
|
存储 前端开发 Java
Spring Boot自动装配的源码学习
【4月更文挑战第8天】Spring Boot自动装配是其核心机制之一,其设计目标是在应用程序启动时,自动配置所需的各种组件,使得应用程序的开发和部署变得更加简单和高效。下面是关于Spring Boot自动装配的源码学习知识点及实战。
13 1
|
5天前
|
JavaScript Java 开发者
Spring Boot中的@Lazy注解:概念及实战应用
【4月更文挑战第7天】在Spring Framework中,@Lazy注解是一个非常有用的特性,它允许开发者控制Spring容器的bean初始化时机。本文将详细介绍@Lazy注解的概念,并通过一个实际的例子展示如何在Spring Boot应用中使用它。
18 2
|
6天前
|
传感器 人工智能 前端开发
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
智慧校园电子班牌,坐落于班级的门口,适合于各类型学校的场景应用,班级学校日常内容更新可由班级自行管理,也可由学校统一管理。让我们一起看看,电子班牌有哪些功能呢?
47 4
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
|
6天前
|
XML Java 数据格式
Spring使用AOP 的其他方式
Spring使用AOP 的其他方式
15 2
|
6天前
|
XML Java 数据格式
Spring 项目如何使用AOP
Spring 项目如何使用AOP
19 2