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() {}
}

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

相关文章
|
3天前
|
Java
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
这篇文章是Spring5框架的实战教程,深入讲解了AOP的基本概念、如何利用动态代理实现AOP,特别是通过JDK动态代理机制在不修改源代码的情况下为业务逻辑添加新功能,降低代码耦合度,并通过具体代码示例演示了JDK动态代理的实现过程。
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
|
3天前
|
XML Java 数据格式
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
这篇文章是Spring5框架的AOP切面编程教程,通过XML配置方式,详细讲解了如何创建被增强类和增强类,如何在Spring配置文件中定义切入点和切面,以及如何将增强逻辑应用到具体方法上。文章通过具体的代码示例和测试结果,展示了使用XML配置实现AOP的过程,并强调了虽然注解开发更为便捷,但掌握XML配置也是非常重要的。
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
|
3天前
|
XML Java 数据库
Spring5入门到实战------15、事务操作---概念--场景---声明式事务管理---事务参数--注解方式---xml方式
这篇文章是Spring5框架的实战教程,详细介绍了事务的概念、ACID特性、事务操作的场景,并通过实际的银行转账示例,演示了Spring框架中声明式事务管理的实现,包括使用注解和XML配置两种方式,以及如何配置事务参数来控制事务的行为。
Spring5入门到实战------15、事务操作---概念--场景---声明式事务管理---事务参数--注解方式---xml方式
|
6天前
|
安全 Java 开发者
Java 新手入门:Spring 两大利器IoC 和 AOP,小白也能轻松理解!
Java 新手入门:Spring 两大利器IoC 和 AOP,小白也能轻松理解!
13 1
|
5天前
|
Java Spring
Spring的AOP组件详解
该文章主要介绍了Spring AOP(面向切面编程)组件的实现原理,包括Spring AOP的基础概念、动态代理模式、AOP组件的实现以及Spring选择JDK动态代理或CGLIB动态代理的依据。
Spring的AOP组件详解
|
18天前
|
Java API Spring
Spring Boot 中的 AOP 处理
对 Spring Boot 中的切面 AOP 做了详细的讲解,主要介绍了 Spring Boot 中 AOP 的引入,常用注解的使用,参数的使用,以及常用 api 的介绍。AOP 在实际项目中很有用,对切面方法执行前后都可以根据具体的业务,做相应的预处理或者增强处理,同时也可以用作异常捕获处理,可以根据具体业务场景,合理去使用 AOP。
|
23天前
|
缓存 安全 Java
Spring高手之路21——深入剖析Spring AOP代理对象的创建
本文详细介绍了Spring AOP代理对象的创建过程,分为三个核心步骤:判断是否增强、匹配增强器和创建代理对象。通过源码分析和时序图展示,深入剖析了Spring AOP的工作原理,帮助读者全面理解Spring AOP代理对象的生成机制及其实现细节。
16 0
Spring高手之路21——深入剖析Spring AOP代理对象的创建
|
3天前
|
XML Java 数据库
Spring5入门到实战------10、操作术语解释--Aspectj注解开发实例。AOP切面编程的实际应用
这篇文章是Spring5框架的实战教程,详细解释了AOP的关键术语,包括连接点、切入点、通知、切面,并展示了如何使用AspectJ注解来开发AOP实例,包括切入点表达式的编写、增强方法的配置、代理对象的创建和优先级设置,以及如何通过注解方式实现完全的AOP配置。
|
3月前
|
安全 Java Spring
Spring之Aop的底层原理
Spring之Aop的底层原理
|
3月前
|
设计模式 Java uml
Spring AOP 原理
Spring AOP 原理
23 0