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/
- 使用 @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开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
更多:Java进阶核心知识集
包含:JVM,JAVA集合,网络,JAVA多线程并发,JAVA基础,Spring原理,微服务,Zookeeper,Kafka,RabbitMQ,Hbase,MongoDB,Cassandra,设计模式,负载均衡,数据库,一致性哈希,JAVA算法,数据结构,加密算法,分布式缓存等等
高效学习视频
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!
G77f-1711813668598)]
[外链图片转存中…(img-Izcj2BvL-1711813668599)]
[外链图片转存中…(img-DExMZleB-1711813668599)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
更多:Java进阶核心知识集
包含:JVM,JAVA集合,网络,JAVA多线程并发,JAVA基础,Spring原理,微服务,Zookeeper,Kafka,RabbitMQ,Hbase,MongoDB,Cassandra,设计模式,负载均衡,数据库,一致性哈希,JAVA算法,数据结构,加密算法,分布式缓存等等
[外链图片转存中…(img-UkMNIin2-1711813668599)]
高效学习视频
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!