Spring AOP 使用介绍,从前世到今生

本文详细解释了SpringAOP中的AspectJ配置,包括DefaultAdvisorAutoProxyCreator的使用,以及如何配置Pointcut(切点)和Advice(通知)。同时介绍了XML配置方式,如<aop:aspectj-autoproxy>和基于<aop/>命名空间的配置。重点强调了如何通过AspectJ注解实现AOP的自定义配置。
摘要由CSDN通过智能技术生成

2、之后,我们需要配置 DefaultAdvisorAutoProxyCreator,它的配置非常简单,直接使用下面这段配置就可以了,它就会使得所有的 Advisor 自动生效,无须其他配置。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

然后我们运行一下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

输出:

准备执行方法: createUser, 参数列表:[Tom, Cruise, 55]

方法返回:User{firstName=‘Tom’, lastName=‘Cruise’, age=55, address=‘null’}

准备执行方法: createOrder, 参数列表:[Leo, 随便买点什么]

方法返回:Order{username=‘Leo’, product=‘随便买点什么’}

从结果可以看出,create

方法使用了 logArgsAdvisor 进行传参输出,query

方法使用了 logResultAdvisor 进行了返回结果输出。

到这里,Spring 1.2 的配置就要介绍完了。本文不会介绍得面面俱到,主要是关注最核心的配置,如果读者感兴趣,要学会自己去摸索,比如这里的 Advisor 就不只有我这里介绍的 NameMatchMethodPointcutAdvisor 和 RegexpMethodPointcutAdvisor,AutoProxyCreator 也不仅仅是 BeanNameAutoProxyCreator 和 DefaultAdvisorAutoProxyCreator。

读到这里,我想对于很多人来说,就知道怎么去阅读 Spring AOP 源码了。

Spring 2.0 @AspectJ 配置

Spring 2.0 以后,引入了 @AspectJ 和 Schema-based 的两种配置方式,我们先来介绍 @AspectJ 的配置方式,之后我们再来看使用 xml 的配置方式。

注意了,@AspectJ 和 AspectJ 没多大关系,并不是说基于 AspectJ 实现的,而仅仅是使用了 AspectJ 中的概念,包括使用的注解也是直接来自于 AspectJ 的包。

首先,我们需要依赖 aspectjweaver.jar 这个包,这个包来自于 AspectJ:

org.aspectj

aspectjweaver

1.8.11

如果是使用 Spring Boot 的话,添加以下依赖即可:

org.springframework.boot

spring-boot-starter-aop

在 @AspectJ 的配置方式中,之所以要引入 aspectjweaver 并不是因为我们需要使用 AspectJ 的处理功能,而是因为 Spring 使用了 AspectJ 提供的一些注解,实际上还是纯的 Spring AOP 代码

说了这么多,明确一点,@AspectJ 采用注解的方式来配置使用 Spring AOP。

首先,我们需要开启 @AspectJ 的注解配置方式,有两种方式:

1、在 xml 中配置:

aop:aspectj-autoproxy/

  1. 使用 @EnableAspectJAutoProxy

@Configuration

@EnableAspectJAutoProxy

public class AppConfig {

}

一旦开启了上面的配置,那么所有使用 @Aspect 注解的 bean 都会被 Spring 当做用来实现 AOP 的配置类,我们称之为一个 Aspect

注意了,@Aspect 注解要作用在 bean 上面,不管是使用 @Component 等注解方式,还是在 xml 中配置 bean,首先它需要是一个 bean。

比如下面这个 bean,它的类名上使用了 @Aspect,它就会被当做 Spring AOP 的配置。

package org.xyz;

import org.aspectj.lang.annotation.Aspect;

@Aspect

public class NotVeryUsefulAspect {

}

接下来,我们需要关心的是 @Aspect 注解的 bean 中,我们需要配置哪些内容。

**首先,我们需要配置 Pointcut,**Pointcut 在大部分地方被翻译成切点,用于定义哪些方法需要被增强或者说需要被拦截,有点类似于之前介绍的 Advisor 的方法匹配。

Spring AOP 只支持 bean 中的方法(不像 AspectJ 那么强大),所以我们可以认为 Pointcut 就是用来匹配 Spring 容器中的所有 bean 的方法的。

@Pointcut(“execution(* transfer(…))”)// the pointcut expression

private void anyOldTransfer() {}// the pointcut signature

我们看到,@Pointcut 中使用了 execution 来正则匹配方法签名,这也是最常用的,除了 execution,我们再看看其他的几个比较常用的匹配方式:

  • within:指定所在类或所在包下面的方法(Spring AOP 独有)

如 @Pointcut(“within(com.javadoop.springaoplearning.service…*)”)

  • @annotation:方法上具有特定的注解,如 @Subscribe 用于订阅特定的事件。

如 @Pointcut("execution(

.*(…)) && @annotation(com.javadoop.annotation.Subscribe)")

  • bean(idOrNameOfBean):匹配 bean 的名字(Spring AOP 独有)

如 @Pointcut(“bean(*Service)”)

Tips:上面匹配中,通常 “.” 代表一个包名,“…” 代表包及其子包,方法参数任意匹配使用两个点 “…”。

对于 web 开发者,Spring 有个很好的建议,就是定义一个 SystemArchitecture

@Aspect

public class SystemArchitecture {

// web 层

@Pointcut(“within(com.javadoop.web…*)”)

public void inWebLayer() {}

// service 层

@Pointcut(“within(com.javadoop.service…*)”)

public void inServiceLayer() {}

// dao 层

@Pointcut(“within(com.javadoop.dao…*)”)

public void inDataAccessLayer() {}

// service 实现,注意这里指的是方法实现,其实通常也可以使用 bean(*ServiceImpl)

@Pointcut(“execution(* com.javadoop…service..(…))”)

public void businessService() {}

// dao 实现

@Pointcut(“execution(* com.javadoop.dao..(…))”)

public void dataAccessOperation() {}

}

上面这个 SystemArchitecture 很好理解,该 Aspect 定义了一堆的 Pointcut,随后在任何需要 Pointcut 的地方都可以直接引用(如 xml 中的 pointcut-ref=“”)。

配置 pointcut 就是配置我们需要拦截哪些方法,接下来,我们要配置需要对这些被拦截的方法做什么,也就是前面介绍的 Advice。

接下来,我们要配置 Advice。

下面这块代码示例了各种常用的情况:

注意,实际写代码的时候,不要把所有的切面都揉在一个 class 中。

@Aspect

public class AdviceExample {

// 这里会用到我们前面说的 SystemArchitecture

// 下面方法就是写拦截 “dao层实现”

@Before(“com.javadoop.aop.SystemArchitecture.dataAccessOperation()”)

public void doAccessCheck() {

// … 实现代码

}

// 当然,我们也可以直接"内联"Pointcut,直接在这里定义 Pointcut

// 把 Advice 和 Pointcut 合在一起了,但是这两个概念我们还是要区分清楚的

@Before(“execution(* com.javadoop.dao..(…))”)

public void doAccessCheck() {

// … 实现代码

}

@AfterReturning(“com.javadoop.aop.SystemArchitecture.dataAccessOperation()”)

public void doAccessCheck() {

// …

}

@AfterReturning(

pointcut=“com.javadoop.aop.SystemArchitecture.dataAccessOperation()”,

returning=“retVal”)

public void doAccessCheck(Object retVal) {

// 这样,进来这个方法的处理时候,retVal 就是相应方法的返回值,是不是非常方便

// … 实现代码

}

// 异常返回

@AfterThrowing(“com.javadoop.aop.SystemArchitecture.dataAccessOperation()”)

public void doRecoveryActions() {

// … 实现代码

}

@AfterThrowing(

pointcut=“com.javadoop.aop.SystemArchitecture.dataAccessOperation()”,

throwing=“ex”)

public void doRecoveryActions(DataAccessException ex) {

// … 实现代码

}

// 注意理解它和 @AfterReturning 之间的区别,这里会拦截正常返回和异常的情况

@After(“com.javadoop.aop.SystemArchitecture.dataAccessOperation()”)

public void doReleaseLock() {

// 通常就像 finally 块一样使用,用来释放资源。

// 无论正常返回还是异常退出,都会被拦截到

}

// 感觉这个很有用吧,既能做 @Before 的事情,也可以做 @AfterReturning 的事情

@Around(“com.javadoop.aop.SystemArchitecture.businessService()”)

public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {

// start stopwatch

Object retVal = pjp.proceed();

// stop stopwatch

return retVal;

}

}

细心的读者可能发现了有些 Advice 缺少方法传参,如在 @Before 场景中参数往往是非常有用的,比如我们要用日志记录下来被拦截方法的入参情况。

Spring 提供了非常简单的获取入参的方法,使用 org.aspectj.lang.JoinPoint 作为 Advice 的第一个参数即可,如:

@Before(“com.javadoop.springaoplearning.aop_spring_2_aspectj.SystemArchitecture.businessService()”)

public void logArgs(JoinPoint joinPoint) {

System.out.println(“方法执行前,打印入参:” + Arrays.toString(joinPoint.getArgs()));

}

注意:第一,必须放置在第一个参数上;第二,如果是 @Around,我们通常会使用其子类 ProceedingJoinPoint,因为它有 procceed()/procceed(args[]) 方法。

到这里,我们介绍完了 @AspectJ 配置方式中的 Pointcut 和 Advice 的配置。对于开发者来说,其实最重要的就是这两个了,定义 Pointcut 和使用合适的 Advice 在各个 Pointcut 上。

下面,我们用这一节介绍的 @AspectJ 来实现上一节实现的记录方法传参记录方法返回值

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

xml 的配置非常简单:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这里是示例,所以 bean 的配置还是使用了 xml 的配置方式。

测试一下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

输出结果:

方法执行前,打印入参:[Tom, Cruise, 55]

User{firstName=‘Tom’, lastName=‘Cruise’, age=55, address=‘null’}

方法执行前,打印入参:[]

User{firstName=‘Tom’, lastName=‘Cruise’, age=55, address=‘null’}

JoinPoint 除了 getArgs() 外还有一些有用的方法,大家可以进去稍微看一眼。

最后提一点,@Aspect 中的配置不会作用于使用 @Aspect 注解的 bean。

Spring 2.0 schema-based 配置

本节将介绍的是 Spring 2.0 以后提供的基于 <aop /> 命名空间的 XML 配置。这里说的 schema-based 就是指基于 aop这个 schema。

介绍 IOC 的时候也介绍过 Spring 是怎么解析各个命名空间的(各种 *NamespaceHandler),解析 <aop /> 的源码在 org.springframework.aop.config.AopNamespaceHandler 中。

有了前面的 @AspectJ 的配置方式的知识,理解 xml 方式的配置非常简单,所以我们就可以废话少一点了。

这里先介绍配置 Aspect,便于后续理解:

aop:config

<aop:aspect id=“myAspect” ref=“aBean”>

</aop:aspect>

</aop:config>

所有的配置都在 <aop:config> 下面。

<aop:aspect > 中需要指定一个 bean,和前面介绍的 LogArgsAspect 和 LogResultAspect 一样,我们知道该 bean 中我们需要写处理代码。

然后,我们写好 Aspect 代码后,将其“织入”到合适的 Pointcut 中,这就是面向切面。

然后,我们需要配置 Pointcut,非常简单,如下:

aop:config

<aop:pointcut id=“businessService”

expression=“execution(* com.javadoop.springaoplearning.service..(…))”/>

<aop:pointcut id=“businessService2”

expression=“com.javadoop.SystemArchitecture.businessService()”/>

</aop:config>

将 <aop:pointcut> 作为 <aop:config> 的直接子元素,将作为全局 Pointcut。

我们也可以在 <aop:aspect />内部配置 Pointcut,这样该 Pointcut 仅用于该 Aspect:

aop:config

<aop:aspect ref=“logArgsAspect”>

<aop:pointcut id=“internalPointcut”

expression=“com.javadoop.SystemArchitecture.businessService()” />

</aop:aspect>

</aop:config>

接下来,我们应该配置 Advice 了,为了避免废话过多,我们直接上实例吧,非常好理解,将上一节用 @AspectJ 方式配置的搬过来:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上面的例子中,我们配置了两个 LogArgsAspect 和一个 LogResultAspect。

其实基于 XML 的配置也是非常灵活的,这里没办法给大家演示各种搭配,大家抓住基本的 Pointcut、Advice 和 Aspect 这几个概念,就很容易配置了。

小结

到这里,本文介绍了 Spring AOP 的三种配置方式,我们要知道的是,到目前为止,我们使用的都是 Spring AOP,和 AspectJ 没什么关系。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

更多:Java进阶核心知识集

包含:JVM,JAVA集合,网络,JAVA多线程并发,JAVA基础,Spring原理,微服务,Zookeeper,Kafka,RabbitMQ,Hbase,MongoDB,Cassandra,设计模式,负载均衡,数据库,一致性哈希,JAVA算法,数据结构,加密算法,分布式缓存等等

image

高效学习视频

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!
G77f-1711813668598)]

[外链图片转存中…(img-Izcj2BvL-1711813668599)]

[外链图片转存中…(img-DExMZleB-1711813668599)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

更多:Java进阶核心知识集

包含:JVM,JAVA集合,网络,JAVA多线程并发,JAVA基础,Spring原理,微服务,Zookeeper,Kafka,RabbitMQ,Hbase,MongoDB,Cassandra,设计模式,负载均衡,数据库,一致性哈希,JAVA算法,数据结构,加密算法,分布式缓存等等

[外链图片转存中…(img-UkMNIin2-1711813668599)]

高效学习视频

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值