简单说说@SpringBootApplication

Posted by Jeremy Song on 2021-04-05
Estimated Reading Time 5 Minutes
Words 1.6k In Total
Viewed Times

@SpringBootApplication 注解想必各位Spring Boot开发者都不陌生了。作者为啥今天想来说说这个呢?

这是因为前几天从同事手里接手了一个模块,近期主要心思放在了业务上面。但是,调式的时候突然发现,这个模块不用登录就能直接访问接口了。搞得我一脸黑线!!!这是个什么操作?然后,因为这个问题又造成了一系列的问题,弄的我也是醉了。

问题分析

查看依赖

因为我们的项目的鉴权模块是独立出来的一个Maven Module,所以我第一反应是查看这个模块是不是忘记依赖鉴权模块了。这个简单,于是我查看了这个模块的pom.xml 文件。但是结果很意外,相关依赖的配置安安静静的在 pom.xml 中呆着。

傻眼了!我又开始怀疑是不是这个模块没有打包进去。于是又把编译完的 jar 包用 jd-gui 工具打开查看了一番。但是结果和我想的也不一样,jar包没问题!

检查过滤器设置

通过上面的检查已经可以确定了,模块依赖和打包都没问题。那会不会是这个模块的接口没有被过滤器拦截,没有做校验。权限校验这一块,我们使用的 Shiro。查看过滤器链配置也没问题,因为使用的是白名单策略,没加特殊的规则,所有的请求都会执行鉴权验证的。到这里已经很明确了,那就是这个公共模块的东西没生效。好了Debug看一下是什么问题吧。

Debug

在以下两个关键点设置断点,进行Debug分析:

  • doFilter 业务逻辑中鉴权部分的函数入口
  • filter组件初始化部分的函数入口

设置好断点后启动项目。

Duang!这一启动还没等调接口就发现问题了。组件初始化的函数没有停住!这问题很明显了,就是公共组件虽然引进来了,但是根本就没用。

于是我又对比了一下其他模块的依赖和配置,结果除过一些特殊化配置,其他基本完全一致。这个现象一出来突然让我懵了一下。这啥情况!

逻辑分析

分析到这里,很明确的知道就是我们鉴权模块的组件没有被初始化。那么Spring Boot怎么把这些组件初始化进来?这个大家搞过Spring Boot的人都知道,添加了注解 @Service 或者 @Component 的类会在启动的时候被扫描出来并实例化。

既然其他模块都问题,那就肯定是这个模块没有将我们所希望的组件扫描进来了,照这个思路接着看我们的项目代码。看着看着,突然眼前一亮,这个模块的 XXXApplication.java 启动的入口类的包名比其他模块多了一级。再看一下这个类上加的注解,除过几个无关的注解外,仅剩了一个 @SpringBootApplication

这下真相大白了,问题就出在这多出来的一级包名。但这是问什么呢?下面来说说。

@SpringBootApplication

大多数的Spring Boot开发者会在自己的Spring Boot项目的启动类上添加@SpringBootApplication注解作为项目的入口,当然Spring Boot的示例代码也是这么做的。但事实上,一个单独的 @SpringBootApplication 注解相当于同时添加了如下三个注解,并开启了三个特性。

  • @EnableAutoConfiguration :开启Spring Boot自动配置机制
  • @ComponentScan :开始Application.java所在包的 @Component 组件扫描
  • @Configuration :允许在上下文中添加额外的Bean,以及导入额外的配置类

然而,我们上面遇到的问题就是在注解 @ComponentScan 上面。因为它默认扫描的是application.java 所在的包以及它的子包中的组件。而我们上面通过查看代码发现,我这个模块的XXXApplication.java 所在的包名多了一级子包。那按照默认规则,处在它父级包中的组件肯定不会被扫描到了。

OK!问题找到了,接下来看看如何解决。

解决方法

蛮力解决

这个方法为啥叫蛮力法呢?因为这种方法是工作量最大的。

我们可以通过将 XXXApplication.java 类向上移动到当前包的父级包里去,以达到修改默认扫描组件包的目的。如果要和其他模块的包组织结构对齐,可能还需要将其他的包统一向上移动。这个想想工作量还是巨大的,但是有的时候你没办法,只能这么移动。

添加@ComponentScan注解

这个方法比较简单,仅仅需要在 XXXApplication.java 类上添加如下注解和属性就可以:

1
2
3
4
5
6
7
8
9
@SpringBootApplication()
@ComponentScan(basePackages = {"com.jeremy.example"})
public class MyApplication {

public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}

}

注:com.jeremy.example 就是 XXXApplication.java 当前所在包的父级包名。

添加@SpringBootApplication属性

因为注解 @SpringBootApplication 也提供了自定义修改注解 @EnableAutoConfiguration@ComponentScan 属性的别名。因此我们也可以按照如下方式修改:

1
2
3
4
5
6
7
8
@SpringBootApplication(scanBasePackages = {"com.jeremy.example"})
public class MyApplication {

public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}

}

注:com.jeremy.example 就是 XXXApplication.java 当前所在包的父级包名。

至此,使用以上三种任意一种即可解决问题。接下来测试验证OK,问题解决!

最后

这个问题也不是个什么大问题,也好解决。但是通过这个事情,让我反思到的是:

团队协作,在一些公共领域尽量不要添加一些个性化的东西进来,尤其是当在不清楚整个系统架构还缺乏文档的情况下。

有时候,团队内约定俗成的东西也是团队规则,虽然这些没有书面化,但在一定程度上也是一种约束和行为准则(这里不讨论合理性和对错)。破坏了这个规则,肯定会有问题暴露出来。


欢迎关注我的公众号 须弥零一,跟我一起学习IT知识。


如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !