@SpringBootApplication中几点解惑

@SpringBootApplication这个annotation是由3个annotation组合而成:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan

假如你愿意,也可以分别用这3个annotation来代替@SpringBootApplication这一个annotation。

在讲下面之前,严重吐槽一下 springboot 的官方文档,它对所涉及的内容都提到那么一点点,但是每一点都是语焉不详,非常的晦涩。

@ComponentScan中的basePackages 和 @SpringBootApplication中的scanBasePackages有什么关系?

我们稍微复杂点的应用程序,一般都有core这样的jar包引入,core.jar包的包名和springboot应用的包名前面部分基本上一样。这个架构如下图所示:

core的包为:
    com.champbay.core

application的结构如下:
    com.champbay.app
    -->com.champbay.app.util
    -->com.champbay.app.service
    -->com.champbay.app.web
    -->com.champbay.app.Application.java

官方文档中有一段描述如下:

We generally recommend that you locate your main application class in a root package above other classes. 
The @SpringBootApplication annotation is often placed on your main class, 
and it implicitly defines a base “search package” for certain items.

上面这段话的描述反映了下面几点:
1,springboot工程有一个main函数的类。
2,这个main函数的类应该位于 com.champbay.app 下面。
3,这个main函数的类上有@SpringBootApplication的注解。
4,这个@SpringBootApplication注解,隐式地表示了凡是在com.champbay.app以及com.champbay.app下面的子包(com.champbay.app.util,com.champbay.app.service,com.champbay.app.web等)中的诸如:@Component,@Repository,@Controller等都将自动扫描。

那么,Application.java启动的时候,会自动扫描com.champbay.app下面的所有子包,但是对于com.champbay.core下面的就不会自动加载。为了解决这个问题,需要在@ComponentScan设置basePackages为com.champbay即可,这样com.champbay.core就会被自动扫描注册那些bean。

我们为了能够自动扫描那些包,需要设置@ComponentScan的属性basePackages,但是总不能因为这样的需求而不用@SpringBootApplication,而用3个annotation来写太麻烦了,所以,在@SpringBootApplication中,定义了一个scanBasePackages,效果和@ComponentScan的basePackages完全一样。我们来看看Spring文档关于这一段的描述,看完后又想XXX了,简直太惜字如金了:

@SpringBootApplication also provides aliases to customize the attributes of @EnableAutoConfiguration and @ComponentScan.
然后呢?然后就没有了

好吧,让我们来看看@SpringBootApplication的源代码吧:

/**
     * Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
     * for a type-safe alternative to String-based package names.
     * @return base packages to scan
     * @since 1.3.0
     */
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};

如此就完全明白了。scanBasePackages就是basePackages的别名!

我们从上面看到,当在 com.champbay.app 不同包下面,有@Component、@Repository、@Controller的时候,需要指定scanBasePackages。
但是我们从另外一篇文章:Auto-configuration的规范中可以看到,springboot不建议用这种方式来自动加载,而是采用如下的方式:

Spring Boot checks for the presence of a META-INF/spring.factories file within your published jar. 
The file should list your configuration classes under the EnableAutoConfiguration key, as shown in the following example:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.mycorp.libx.autoconfigure.LibXAutoConfiguration,\
com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration

Auto-configurations must be loaded that way only. 
Make sure that they are defined in a specific package space and that they are never the target of component scanning. 
Furthermore, auto-configuration classes should not enable component scanning to find additional components. 
Specific @Imports should be used instead.

也就是说,不要用scanBasePackages这样的方式,而是采用 META-INF/spring.factories 的方式。

发表评论

邮箱地址不会被公开。 必填项已用*标注