5

花了2个钟才搞懂这AOP为啥没生效,水友却睡着了……

 3 years ago
source link: https://my.oschina.net/java4ye/blog/5123771
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

今天 4ye 来和小伙伴们分享一个小实战啦 ,冲冲冲~ (。・∀・)ノ

acef8e9f-1577-44e1-9476-4e5c6fc69986.jpg

(我居然拖到现在才写了这文章…… 😵)

主要是在一个周日(2020.6.6)在技术群里看到一个老哥在问

“怎么用切面来捕获自定义异常?” ( ̄▽ ̄)"

我当时想的是,捕获异常不是很常见的吗,平时经常用到这个全局异常捕获 ,于是就把自己 GitHub 上的小例子发给他(主要是这个 ControllerAdvice 注解),如图 👇

f2622811-0849-4f7e-a9f3-20d76bb778a7.png

结果老哥过了一段时候就加了我,还问我有没有空帮忙看下,还想打电话问我,我当时的内心是

这么急的吗(瑟瑟发抖……)

5bade4e7-3075-4f09-954e-8b1be1951765.png

不过刚好在外面没时间,就婉拒了,后来晚上回来,愣是花了两个钟,才看懂 为啥这个AOP 没有生效?为啥没能捕获到异常?

fe134afa-302c-4631-95d9-c375520bda4e.jpg

下面进入主题👉(项目整体介绍

maven项目结构图

在该项目中,maven项目采用多模块构建父子结构,由 父maven 来统一管理这些公共包,以及项目的整体版本属性配置等

里面有两个模块,starter 模块和 core 模块,其中 starter 模块依赖 core 模块,整体如下图👇

8c58fbd0-ac4d-4819-8c0f-d8bd777866b4.png

子模块介绍

不知道小伙伴们看到上面的 starter 有没有嗅到什么 ?

一开始我以为是常见的 自定义starter ,但是里面的内容却和我想的有点出入,居然只有一个 spring.factories 文件, 很明显这里使用到了 Springboot 的 SPI 机制

这个在我们之前的  👉 Springboot自动装配原理探索  一文中有介绍到,小伙伴们可以前去了解看看~😄

对两个模块中的核心部分进行展开,结构如下👇

f4e9b583-b8f5-4416-8638-8e1cc7e66535.png

core项目描述非web项目里面只有 service ,没有启动类等

👉 由于 core 模块 不是web项目!!,所以这个 ControllerAdvice 是肯定不能用的,毕竟它是在 web 包中的,一般我们在 web项目中配合这个@ExceptionHandler(Exception.class) 实现全局异常捕获,然后进行统一处理的。

👉 从 Springboot 的 SPI 机制 中我们可以得知,Springboot 项目启动时,会去扫描各个项目中的  META-INF/spring.factories 文件(包括各个jar包),然后将其中的配置信息读取到内存中,而自动配置时会根据一定的条件对这些类进行筛选,最后创建符合的类,完成这个自动装配。

很明显,这里就是通过自动配置,来实现相关 bean 的注入。

配置类说明

那么,在了解了这些基本信息后,我们可以把目光移到这个 xxxConfig 上,这里模仿了一个👇

d7052dca-f0f4-40a0-a3fd-f6bbf8ad8f51.png

紧接着就是项目中的切面配置了,例如以前写的小例子:👇

代码在我的 GitHub

https://github.com/RyzeYang/springboot-demo-4ye.git

通过异常通知来捕获

651de57b-aa47-4d4a-9286-b7d6a599a78c.png

除了上面这两个之外,项目中没有用到其他配置了!

这个时候问题就来了,在定义了切面之后,发现根本没有在项目中起作用!而其他都可以正常运行!

05241424-c26d-4bcd-80c8-cfa18df6fe4b.jpg

于是我一直在想,这是为啥呀,明明切面已经定义好了呀……

640?wx_fmt=jpeg

终于,我开始了尝试,在 yaml 配置文件中添加这个参数

spring:
 aop:
  auto: true

因为在印象中,这个默认是 true ,会默认使用这个 @EnableAspectJAutoProxy , 不用我们手动去添加这个 @EnableAspectJAutoProxy 注解(之前一直没有手动添加这个注解)🐖

4acb912a-f6b3-41cd-b4f8-a1019f0f4f84.png

结果也没什么效果……

于是乎,我决定手动添加到刚刚那个 xxxConfig 配置类上,结果也没有什么作用……

072d2053-7710-40ef-a2bd-e4ec17eac5bf.png

终于,我才想起那个 切面配置 没有被加载到这个 Spring 中 ,于是我又在那个配置类 xxxConfig 上添加了这个包扫描注解 @ComponentScan(basePackages = "com.xxx.xxx")

结果终于成功了!

于是我赶在 23:59 将修改后的文件发给那位老哥后,却发现他居然睡着了 哈哈哈

c3798d21-102c-4862-a837-f1c0b185621c.png

200f407a-62c0-4dab-a4c2-dc0c65a5afa1.jpg

解决问题后,我们可以发现这个问题就下面两点👇

  1. 没有将切面注册到 SpringIOC 容器中

  2. 没有使用这个 @EnableAspectJAutoProxy

第一步的解决也很简单,就是没有配置这个包扫描 @ComponentScan(basePackages = "com.xxx.xxx")

第二步的解决嘛,就有点一头雾水了当时,毕竟之前也不需要我手动去添加的,而且从配置的描述信息中可以发现,即使我们没有配置,他也是默认开启的,会自动使用这个注解的~

那么小伙伴们知道第二步问题的所在吗😄

640?wx_fmt=jpeg

嘿嘿,答案就出在这个自动配置 身上,可以发现我们上面都没有使用到这个 @EnableAutoConfiguration 注解,而在我们的 SpringBootApplication 组合注解中,最重要的就是它了!通过它去开启了我们的这个自动装配

这个时候又得把这文章搬出来了 👉 Springboot自动装配原理探索 哈哈

那么我们再来看看这个 AOP自动装配的配置类

AOP自动装配的配置类

AopAutoConfiguration 源码如下👇

589f8390-d352-4519-8a60-6eb7dc11fa43.png

开头有这么一个条件注解

@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)

havingValue = "true" 的意思是:值为 true 时才有效

matchIfMissing = true 的意思是:没有配置这个属性时也能加载

接着让我们把目光移到第一个静态内部类:AspectJAutoProxyingConfiguration  

如图👉:

73741d9d-812e-4718-8315-f5bef843b1b1.png

这个我们也比较熟悉啦, proxyTargetClasstrue 时表示使用 cglibfalse 使用 JDK动态代理

接着看最后的静态内部类:ClassProxyingConfiguration

e534f05a-ff3e-42da-b2a2-128c1f6bd1cc.png

可以发现它这里的条件是和  AspectJAutoProxyingConfiguration  相反的,当没有这个 Advice 类时,帮我们去注册这个代理到 IOC

看完该AOP自动装配类后, 我们可以发现当我们使用 @EnableAutoConfiguration 自动装配注解时并引入 AOP 的包时,它会自动帮我们装配这个AopAutoConfiguration ,而它里面就使用到了 @EnableAspectJAutoProxy ,所以我们一般不用手动添加该注解。

dae559ae-4f25-4401-8abc-ef7e45fb4b3e.jpg

嘿嘿,再出一个小问题考考小伙伴😝

有没有细心的小伙伴发现上面的 core 模块是没有用到这个 SpringBootApplication 的,而且我们也没有用到这个 @EnableAutoConfiguration ,那么没有自动装配,这个 SPI 显然也没啥作用 ,那么,我们在项目中要怎么测试呢~

b942bd26-8ed4-4f41-b65f-b6a9298d93ad.gif

答案就在 单元测试的注解身上 @SpringBootTest(classes = xxxConfiguration.class)

通过这个 classes ,我们直接指定并实例化这个配置类就可以了

我也是报错了才知道 哈哈😝

7964cc7e-cd70-4286-aa11-5647abff01c8.png

嘿嘿,老规矩,画个图总结下啦👇

4110ad52-899e-4cfb-bebd-68e525e0c8ee.png

ad27d24f-bdda-4092-9f37-7832efbb284a.png

那么,本期就分享到这啦,喜欢的小伙伴记得点点赞呀~ 下期看看情况分享下下面某一个叭😝

  • Springboot自定义starter

  • 利用AOP实现插件

  • Spring源码

1566ab91-0d44-4887-b11e-c12247e9a359.jpg

欢迎小伙伴们来一起探讨问题~

如果你觉得本篇文章还不错的话,那拜托再点点赞支持一下呀😝

让我们开始这一场意外的相遇吧!~

欢迎留言!谢谢支持!ヾ(≧▽≦*)o 冲冲冲!!

我是4ye 咱们下期应该……很快再见!! 😆


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK