Spring官网阅读(十八)AOP的核心概念(1)

简介: 本篇文章将作为整个Spring官网阅读笔记的最后一篇。如果要谈SpringFramework必定离不开两点1.IOC(控制反转)2.AOP(面向切面)在前面的文章中我们已经对IOC做过详细的介绍了,本文主要介绍AOP,关于其中的源码部分将在专门的源码专题介绍,本文主要涉及的是AOP的基本概念以及如何使用,本文主要涉及到官网中的第5、6两大章

什么是AOP


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


------------------《百度百科》


可能你看完上面这段话还是迷迷糊糊,一堆专业词汇看起来很牛逼的样子,不用担心,任何东西都是需要积累的,有些东西只需要记在脑子里,在以后实践的过程中自然而然的就明白了。


另外放一段网上大佬写的一段话,我觉得很好的解释了面向对象跟面向切面


面向对象编程解决了业务模块的封装复用的问题,但是对于某些模块,其本身并不独属于摸个业务模块,而是根据不同的情况,贯穿于某几个或全部的模块之间的。例如登录验证,其只开放几个可以不用登录的接口给用户使用(一般登录使用拦截器实现,但是其切面思想是一致的);再比如性能统计,其需要记录每个业务模块的调用,并且监控器调用时间。可以看到,这些横贯于每个业务模块的模块,如果使用面向对象的方式,那么就需要在已封装的每个模块中添加相应的重复代码,对于这种情况,面向切面编程就可以派上用场了。


面向切面编程,指的是将一定的切面逻辑按照一定的方式编织到指定的业务模块中,从而将这些业务模块的调用包裹起来


AOP中的核心概念


官网中的相关介绍如下:

微信图片_20221113135821.png

是不是看得头皮发麻,不用紧张,我们一点点看过去就好了,现在从上往下开始介绍


前置场景,假设我们现在要对所有的controller层的接口进行性能监控


切面


切点跟通知组成了切面


连接点


所有我们能够将通知应用到的地方都是连接点,在Spring中,我们可以认为连接点就是所有的方法(除构造函数外),连接点没有什么实际意义,这个概念的提出只是为了更好的说明切点


通知


就是我们想要额外实现的功能,在上面的例子中,实现了性能监控的方法就是通知


切点


在连接点的基础上,来定义切入点,比如在我们上面的场景中,要对controller层的所有接口完成性能监控,那么就是说所有controller中的方法就是我们的切点(service层,dao层的就是普通的连接点,没有什么作用)。


引入


我们可以让代理类实现目标类没有实现的额外的接口以及持有新的字段。


目标对象


引入中所提到的目标类,也就是要被通知的对象,也就是真正的业务逻辑,他可以在毫不知情的情况下,被织入切面,而自己专注于业务本身的逻辑。


代理对象


将切面织入目标对象后所得到的就是代理对象。代理对象是正在具备通知所定义的功能,并且被引入了的对象。


织入


把切面应用到目标对象来创建新的代理对象的过程。切面的织入有三种方式


  1. 编译时织入
  2. 类加载时期织入
  3. 运行时织入

我们通常使用的SpringAOP都是运行时期织入,另外Spring中也提供了一个Load Time Weaving

(LTW,加载时期织入)的功能,此功能使用较少,有兴趣的同学可以参考一下两个链接:


上面这些名词希望你不仅能记住而且要能理解,因为不管是Spring源码还是官网中都使用了这些名词,并且从这些名称中还衍生了一些新的名词,比如:Advisor,虽然这些在源码阶段会再介绍,不过如果现在能懂的话无疑就在为学习源码减负了。


在对AOP中的核心概念有了一定了解之后,我们就来看看,如何使用AOP,在学习使用时,第一步我们需要知道怎么去在容器中申明上面所说的那些AOP中的元素


Spring中如何使用AOP


XML方式本文不再介绍了,笔者近两年来没有通过XML的方式来使用过SpringAOP,现在注解才是王道,本文也只会介绍注解的方式


1、开启AOP

@Configuration
@ComponentScan("com.spring.study.springfx.aop")
// 开启AOP
@EnableAspectJAutoProxy
public class Config {
}

核心点就是在配置类上添加@EnableAspectJAutoProxy,这个注解中有两个属性如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
  // 是否使用CGLIB代理,默认不使用。默认使用JDK动态代理
  boolean proxyTargetClass() default false;
    // 是否将代理类作为线程本地变量(threadLocal)暴露(可以通过AopContext访问)
    // 主要设计的目的是用来解决内部调用的问题
  boolean exposeProxy() default false;
}

2、申明切面

@Aspect  // 申明是一个切面
@Component  // 切记,一定要将切面交由Spring管理,否则不起作用
public class DmzAnnotationAspect {
  //......
}

3、申明切点


我们一般都会通过切点表达式来申明切点,切点表达式一般可以分为以下几种


切点表达式


excecution表达式

语法

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)


这里问号表示当前项是非必填的,其中各项的语义如下:


  • modifiers-pattern(非必填):方法的可见性,如public,protected;
  • ret-type-pattern(必填):方法的返回值类型,如int,void等;
  • declaring-type-pattern(非必填):方法所在类的全路径名,如com.spring.Aspect;
  • name-pattern(必填):方法名类型,如buisinessService();
  • param-pattern(必填):方法的参数类型,如java.lang.String;
  • throws-pattern(非必填):方法抛出的异常类型,如java.lang.Exception;
  • 可以看到,必填的参数只有三个,返回值,方法名,方法参数。


示例

按照上面的语法,我们可以定义如下的切点表达式

// 1.所有权限为public的,返回值不限,方法名称不限,方法参数个数及类型不限的方法,简而言之,所有public的方法
execution(public * *(..))
// 2.所有权限为public的,返回值限定为String的,方法名称不限,方法参数个数及类型不限的方法
execution(public java.lang.String *(..)) 
// 3.所有权限为public的,返回值限定为String的,方法名称限定为test开头的,方法参数个数及类型不限的方法
execution(public java.lang.String test*(..))
// 4.所有权限为public的,返回值限定为String的,方法所在类限定为com.spring.study.springfx.aop.service包下的任意类,方法名称限定为test开头的,方法参数个数及类型不限的方法
execution(public java.lang.String com.spring.study.springfx.aop.service.*.test*(..))
// 5.所有权限为public的,返回值限定为String的,方法所在类限定为com.spring.study.springfx.aop.service包及其子包下的任意类,方法名称限定为test开头的,方法参数个数及类型不限的方法
execution(public java.lang.String com.spring.study.springfx.aop.service..*.test*(..))
// 6.所有权限为public的,返回值限定为String的,方法所在类限定为com.spring.study.springfx.aop.service包及其子包下的Dmz开头的类,方法名称限定为test开头的,方法参数个数及类型不限的方法
execution(public java.lang.String com.spring.study.springfx.aop.service..Dmz*.test*(..))
// 7.所有权限为public的,返回值限定为String的,方法所在类限定为com.spring.study.springfx.aop.service包及其子包下的Dmz开头的类,方法名称限定为test开头的,方法参数限定第一个为String类,第二个不限但是必须有两个参数
execution(public java.lang.String com.spring.study.springfx.aop.service..Dmz*.test*(String,*))
// 8.所有权限为public的,返回值限定为String的,方法所在类限定为com.spring.study.springfx.aop.service包及其子包下的Dmz开头的类,方法名称限定为test开头的,方法参数限定第一个为String类,第二个可有可无并且不限定类型
execution(public java.lang.String com.spring.study.springfx.aop.service..Dmz*.test*(String,..))

看完上面的例子不知道大家有没有疑问,比如为什么修饰符一直是public呢?其它修饰符行不行呢?修饰符的位置能不能写成*这种形式呢?


答:

1.如果使用的是JDK动态代理,这个修饰符必须是public,因为JDK动态代理是针对于目标类实现的接口进行的,接口的实现方法必定是public的。

2.如果不使用JDK动态代理而使用CGLIB代理(@EnableAspectJAutoProxy(proxyTargetClass = true))那么修饰符还可以使用protected或者默认修饰符。但是不能使用private修饰符,因为CGLIB代理生成的代理类是继承目标类的,private方法子类无法复写,自然也无法代理。基于此,修饰符是不能写成*这种格式的。


@annotation表达式

语法

@annotation(annotation-type)


示例

// 代表所有被DmzAnnotation注解所标注的方法
// 使用注解的方法定义切点一般会和自定义注解配合使用
@annotation(com.spring.study.springfx.aop.annotation.DmzAnnotation)

within表达式

语法

within(declaring-type-pattern)


示例

// within表达式只能指定到类级别,如下示例表示匹配com.spring.service.BusinessObject中的所有方法
within(com.spring.service.BusinessObject)
// within表达式能够使用通配符,如下表达式表示匹配com.spring.service包(不包括子包)下的所有类
within(com.spring.service.*) 
// within表达式能够使用通配符,如下表达式表示匹配com.spring.service包及子包下的所有类
within(com.spring.service..*)

官网中一共给出了9中切点表达式的定义方式,但是实际上我们常用的就两种,就是excecution表达式以及annotation表达式。所以下文对于其余几种本文就不做详细的介绍了,大家有兴趣的可以了解,没有兴趣的可以直接跳过。可以参考官网链接


@within表达式

语法


@within(annotation-type)

跟annotation表达式的区别在于,annotation表达式是面向方法的,表示匹配带有指定注解的方法,而within表达式是面向类的,表示匹配带有指定注解的类。


示例

// 代表所有被DmzAnnotation注解所标注的类
// 使用注解的方法定义切点一般会和自定义注解配合使用
@within(com.spring.study.springfx.aop.annotation.DmzAnnotation)

arg表达式

语法

args(param-pattern)

示例

// 匹配所有只有一个String类型的方法
args(String)
// 匹配所有有两个参数并且第一个参数为String的方法
args(String,*)
// 匹配所有第一个参数是String类型参数的方法
args(String,..)

@args表达式

语法


@args(annotation-type)

示例


@args(com.spring.annotation.FruitAspect)

跟@annotation表达式以及@within表达式类似,@annotation表达式表示匹配使用了指定注解的方法,@within表达式表达式表示匹配了使用了指定注解的类,而@args表达式则代表使用了被指定注解标注的类作为方法参数


this表达式

// 代表匹配所有代理类是AccountService的类
this(com.xyz.service.AccountService)

target表达式

// 代表匹配所有目标类是AccountService的类
target(com.xyz.service.AccountService)

this跟target很鸡肋,基本用不到


定义切点

@Aspect
@Component
public class DmzAnnotationAspect {
    @Pointcut("execution(public * *(..))")
    private void executionPointcut() {}
    @Pointcut("@annotation(com.spring.study.springfx.aop.annotation.DmzAnnotation)")
    private void annotationPointcut() { }
    // 可以组合使用定义好的切点
    // 表示同时匹配满足两者
    @Pointcut("executionPointcut() && annotationPointcut()")
    private void annotationPointcutAnd() {}
    // 满足其中之一即可
    @Pointcut("executionPointcut() || annotationPointcut()")
    private void annotationPointcutOr() {}
    // 不匹配即可
    @Pointcut("!executionPointcut()")
    private void annotationPointcutNot() {}
}

现在我们已经在一个切面中定义了两个切点了,现在开始编写通知

相关文章
|
1月前
|
XML Java 数据安全/隐私保护
Spring Aop该如何使用
本文介绍了AOP(面向切面编程)的基本概念和术语,并通过具体业务场景演示了如何在Spring框架中使用Spring AOP。文章详细解释了切面、连接点、通知、切点等关键术语,并提供了完整的示例代码,帮助读者轻松理解和应用Spring AOP。
Spring Aop该如何使用
|
22天前
|
监控 安全 Java
什么是AOP?如何与Spring Boot一起使用?
什么是AOP?如何与Spring Boot一起使用?
46 5
|
27天前
|
Java 开发者 Spring
深入解析:Spring AOP的底层实现机制
在现代软件开发中,Spring框架的AOP(面向切面编程)功能因其能够有效分离横切关注点(如日志记录、事务管理等)而备受青睐。本文将深入探讨Spring AOP的底层原理,揭示其如何通过动态代理技术实现方法的增强。
53 8
|
27天前
|
Java 开发者 Spring
Spring AOP 底层原理技术分享
Spring AOP(面向切面编程)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理等。本文将深入探讨Spring AOP的底层原理,包括其核心概念、实现方式以及如何与Spring框架协同工作。
|
27天前
|
XML 监控 安全
深入调查研究Spring AOP
【11月更文挑战第15天】
40 5
|
27天前
|
Java 开发者 Spring
Spring AOP深度解析:探秘动态代理与增强逻辑
Spring框架中的AOP(Aspect-Oriented Programming,面向切面编程)功能为开发者提供了一种强大的工具,用以将横切关注点(如日志、事务管理等)与业务逻辑分离。本文将深入探讨Spring AOP的底层原理,包括动态代理机制和增强逻辑的实现。
39 4
|
1月前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
40 0
|
2月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
230 2
|
5天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
46 14
|
28天前
|
缓存 IDE Java
SpringBoot入门(7)- 配置热部署devtools工具
SpringBoot入门(7)- 配置热部署devtools工具
46 1
SpringBoot入门(7)- 配置热部署devtools工具