SpringBoot入门(二)--SpringBoot常用注解及配置

在上一篇文章 springboot入门(一)–快速搭建一个springboot框架介绍了如何快速开始一个springboot应用,文中几乎没有配置任何信息,就得到了一个可运行的WEB应用,其实这是因为springboot帮我们做了自动配置,springboot的理念就是约定大于配置,springboot默认帮我们做好了绝大多数的配置,我们只需要配置自己特殊的一部分,本文将从springboot如何工作开始讲起springboot的配置文件以及常用注解

正文

springboot如何启动

在启动一个springboot应用的时候我们通常会使用如下启动类:

1
2
3
4
5
6
7
@SpringBootApplication
public class SpringBootApplicationStarter {

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

我们可以看到这是一个普通的带main方法的java类,运行main方法,就会启动一个springboot应用,那么从该类明显可以看出,启动一个springboot应用发挥作用的一个是@SpringBootApplication注解,一个是SpringApplication.run方法,那么我们从@SpringBootApplication注解开始讲解springboot应用是如何自动配置并启动的,点开@SpringBootApplication注解可以看到该注解是个复合注解,其包含了以下7个注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
...
}

首先上面4个注解都是java的注解,对springboot应用没什么作用就不再多说,下面3个注解才是对spring应用启动发挥作用的比较重要的注解,我们将启动类上面的@SpringBootApplication注解去掉换成@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan这三个注解如下:

1
2
3
4
5
6
7
8
9
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public class SpringBootApplicationStarter {

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

启动程序,发现程序可以照常启动,但是每次都写三个注解很累,所以springboot就通过使用@SpringBootApplication注解来将这三个注解合成为一个注解,所以我们可以直接使用@SpringbootApplication来启动应用,既然这三个注解是springboot应用启动发挥作用的注解,那么分别介绍这三个注解:

  • @SpringBootConfiguration:点开该注解,发现该注解其实就是穿着马甲的@Configuration,而对于@Configuration注解我们就不陌生了,该注解是spring用来代替xml方式使用java代码配置IOC容器的注解,被@Configuration注解的类里面的方式可以使用@Bean来向spring的IOC容器注册一个Bean,其和使用xml方式在标签中注册是同样的作用,所以@SpringBootConfiguration就是用来给springboot应用提供配置的注解

  • @EnableAutoConfiguration:该注解是springboot能自动配置最为核心的一个注解,使用该注解,springboot就启动了自动配置功能,点开该注解:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import({EnableAutoConfigurationImportSelector.class})
    public @interface EnableAutoConfiguration {
    ...
    }

可以看到,在该注解中通过@Import注解可以将相关的bean定义注册到IOC容器中,通过EnableAutoConfigurationImportSelector类会去类路径下的spring.factories文件中读取相关的bean,而这些bean就是各种xxxxAutoConfiguration类,而这些类就会完成自己功能的自动配置,比如rabbitmq会去读取org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration类,该类就实现了rabbitmq的自动配置,这样自动配置就完成了,关于springboot的启动过程,我打算重新写一篇文章来详细描述,本文主要讲解springboot的注解和配置,就不再深入springboot如何启动了。

  • @ComponentScan 这个注解大家也不陌生了,这是spring的一个注解,用于扫描指定package下的所有定义的BeanDefinition,在默认情况下,springboot至扫描启动类当前包及其子包的bean,所以其它层定义的bean扫描不到,所以通常我们还需要在@SpringApplication注解下方再加上@ComponentScan来指定要扫描的包。

以上,讲解完三个注解之后再来看看SpringApplication.run方法,因为打算重新开启一篇文章来讲解springboot启动过程,所以这里不再过多叙述,来看看在方法中比较主要的几行代码:

1
2
3
4
5
6
7
8
9
run(...){
...
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
listeners.finished(context, (Throwable)null);
stopWatch.stop();
...
}

看过spring源码的同学看见这几行代码看方法名就知道大概做了些什么,prepareContext是做一些创建IOC容器前的一些操作,refreshContext的作用是创建IOC容器,afterRefresh表示创建完容器之后的一些善后操作,所以run方法的作用和spring容器启动类似,主要创建IOC容器,和向容器中注册BeanDefinition。

springboot配置文件

默认springboot会从classpath的resources文件夹下去读取配置文件,springboot的配置文件分为application.properties和application.yml分别提供两种风格的配置方式。在resources文件夹下新建一个application.properties和一个application.yml,在application.properties文件中添加如下两行:

1
2
3
spring.application.name=spring-boot-config
server.port=8181
server.context-path=/config

server.port表示应用启动的端口,server.context-path表示应用根路径,那么springboot如何读取配置文件的内容的?上面说到@EnableAutoConfiguration注解会去spring.factories文件中读取需要向spring容器注册的bean,这其中就有一个bean是org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,而这个bean部分代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Configuration
@EnableConfigurationProperties
@ConditionalOnWebApplication
public class ServerPropertiesAutoConfiguration {
public ServerPropertiesAutoConfiguration() {
}

@Bean
@ConditionalOnMissingBean(
search = SearchStrategy.CURRENT
)
public ServerProperties serverProperties() {
return new ServerProperties();
}
...
}

可以看到再这个bean内部通过@Bean又向spring容器注册了一个ServerProperties,再看这个ServerProperties:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@ConfigurationProperties(
prefix = "server",
ignoreUnknownFields = true
)
public class ServerProperties implements EmbeddedServletContainerCustomizer, EnvironmentAware, Ordered {
...
public void setPort(Integer port) {
this.port = port;
}
...
public void setContextPath(String contextPath) {
this.contextPath = this.cleanContextPath(contextPath);
}
...

其通过@ConfigurationProperties注解读取配置文件中以server开头的信息,所以server.port和server.context-path就通过set方式被读取到了ServerProperties,而ServerProperties被注册到了IOC容器,spring自然就可以使用配置文件中的信息来启动应用了。关于另一种.yml方式的配置文件主要是用来配置比较复杂的情况,其通过缩进和换行来配置,比如上方的application.properties中的信息写在application.yml中应该这样写:

1
2
3
4
5
6
spring:
application:
name: spring-boot-config-yml
server:
port: 8182
context-path: /config

多环境配置

通常在实际应用中会出现多环境配置,生产环境、测试环境、开发环境的配置通常不一样,springboot也考虑到了这个问题,所以可以在resourcces文件夹下分别新建三个文件application-dev.properties、application-test.properties、application-pro.properties分别对应开发、测试、生产三种环境的配置,然后在application.properties中通过spring.profiles.active=dev|test|pro来分别指定使用哪个配置文件

关于优先级

springboot的配置文件允许出现在4个位置分别为 1.启动类当前文件夹的/config目录下 2.当前文件夹 3.classpath的/config目录下 4.classpath下
关于在多个配置文件中出现了相同属性会有一个优先级,这个优先级从高到低为:1>2>3>4,也就是在1中如果配置了server.port,同时在其他地方也配置了server.port那么1中的会覆盖其他地方的,其实完全没有必要去纠结优先级的问题,springboot的理念就是集中化配置,所以不应该在多处配置相同的属性,也就没有必要去纠结这个优先级关系。