Spring对于Java开发者来说,熟悉程度大概与word、excel这些office软件相当。这是个人猜测,大家也可以来投个票:

它简单易用,极大降低了开发人员的门槛。但是,它毕竟是建立在很多约定俗成的规则之上。而这些约定并不一定是你所熟悉的。所以,当你遇到问题时,很有可能就抓狂了。

前段时间我做了网络编程的系列课程,过年了,咱们换换脑子。争取不太烧脑的情况下,来一个spring避坑指南的系列课程。

问题

在使用 @Autowired 时,不管你是菜鸟级还是专家级的 Spring 使用者,都应该制造或者遭遇过类似的错误:

required a bean of type 'xxx' that could not be found

在构建 Web 服务时,我们常使用 Spring Boot 来快速构建。

例如,使用下面的包结构和相关代码来完成一个简易的 Web 版 HelloWorld:其中,负责启动程序的 Application 类定义如下:

@SpringBootApplication
public class Application {
    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }
}

提供接口的 HelloWorldController 代码如下:

@RestController
public class HelloWorldController {
    @GetMapping(path = "hi")
    public String hi() {
        return "新年快乐!";
    }
}

不出意外,访问时结果应该如下所示:

842e5e9dc4c19aa88ee9d8e2612c104a.png

其中 HelloWorldController 因为添加了 @RestController,最终被识别成一个 Controller 的 Bean。

但是,假设有一天,我们希望用更清晰的包层次和结构来管理时,可能将Application 移动到一个单独的目录下,比如下面这样:

8b017d5000200df434b6963292ed2ec0.png

这时候访问就404了:

2d0418601a1b9a16314e8f48849a1e2b.png

实际上,我们没有改变任何代码,只是改变了包的结构,但是我们会发现这个 Web 应用失效了,即不能识别出 HelloWorldController 了。也就是说,我们找不到 HelloWorldController 这个 Bean 了。这是为何?

解析

要了解 HelloWorldController 为什么会失效,就需要先了解之前是如何生效的。对于 Spring Boot 而言,关键点在于 Application.java 中使用了 SpringBootApplication 注解。而这个注解继承了另外一些注解,具体定义如下:

@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {

从定义可以看出,SpringBootApplication 开启了很多功能,其中一个关键功能就是 ComponentScan。当 Spring Boot 启动时,ComponentScan 的启用意味着会去扫描出所有定义的 Bean,那么扫描什么位置呢?这是由 ComponentScan 注解的 basePackages 属性指定的。

直接使用的是 SpringBootApplication 注解定义的 ComponentScan,它的 basePackages 没有指定,所以默认为空(即{})。此时扫描的是什么包?

当 basePackages 为空时,扫描的包会是 declaringClass 所在的包,在本案例中,declaringClass 就是 Application,所以扫描的包其实就是它所在的包。因为后来移动目录后 Application 所在的包为 com.brmayi.concise.startup ,没有包含   HelloWorldController  所在的 com.brmayi.concise 。就扫描不到了。

这就是为什么一般咱们都会把 Application 放到项目最外层目录。这样,它所在的目录以及子目录都可以扫描到。

34afb2cecc516e7619241968d071e6c9.png

另外,我们也可以使用 @ComponentScans 来修复问题。

福利时间

有朋友让我拉群发红包。问题是:拉了群我说点啥呢。12月份的时候,我一看今年用户量增长很少,发力写了几篇文章。同时,也得到了贵人相助,用户增长终于完成了“KPI”。虽然我不知道贵人具体是谁,但是肯定和咱们用户朋友有很大的关系。答谢是应该的。

要做就做大的,我拉群,大家帮忙拉人进去,争取拉满500人。聊天你们聊,我只管过年发红包。

bfe76ac09794a7e575d87198d418f71e.png

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐