1 第一个springboot项目

我们在一个路径下面创建pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>myproject</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.3</version>
    </parent>

    <!-- Additional lines to be added here... -->

</project>

这个pom文件是maven的一个配置文件,里面涵盖了项目结构和依赖。

其中spring-boot-starter-parent中为我们提供了很多maven默认配置,比如所打包插件,或者可能用到的依赖等等。

然后,我们可以使用mvn package命令对项目进行打包。我们将会在项目根目录下生成target目录,且在target目录下会生成一个jar包。

springboot企业知识库管理系统 springboot官方文档_maven

 

 

 上面,我们还没有引入任何依赖。当我们运行mvn dependency:tree命令,我们可以看到项目的依赖关系

springboot企业知识库管理系统 springboot官方文档_maven_02

 

 

 如果我们需要创建的是一个web项目,则我们需要引入spring-boot-starter-web依赖。

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

注:springboot中为我们提供了很多starter,我们引入不同的starter会创建不同类型的应用。

我们再次查看依赖关系,此时依赖就和上面不一样了。

springboot企业知识库管理系统 springboot官方文档_spring_03

 

 

我们看到,我们引入了starter依赖后,项目会自动为我们加载springmvc、spring-autoconfigure、tomcat相关的jar包。通过这里我们可以体会一下starter的作用。

maven默认源码路径为:src/main/java,为什么?这属于maven的范畴,这里不做细究,只做一下说明:使用mvn help:effective-pom命令我们可以得到项目的一个整体pom,在这个pom中我们可以看到其源码路径:

springboot企业知识库管理系统 springboot官方文档_java_04

 

 

我们在源码路径下面创建java启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@EnableAutoConfiguration
public class MyApplication {

    @RequestMapping("/")
    String home() {
        return "Hello World!";
    }

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

}

然后,我们运行 mvn spring-boot:run

springboot企业知识库管理系统 springboot官方文档_java_05

 

 

 然后,我么浏览器访问

springboot企业知识库管理系统 springboot官方文档_spring_06

 

 

至于为什么使用mvn spring-boot:run启动项目,可以参考

接下来,我们希望使用mvn package命令打包,打成一个可运行的jar包(也就是fat jar),因此我们需要在pom文件中引入如下插件

<build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

这个插件在spring-boot-starter-parent中有引入,在pluginManagement中进行管理,所以我们在自己的pom中就不用写版本号和goal之类的标签了。

<pluginManagement>
      <plugins>
        <plugin>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-maven-plugin</artifactId>
          <executions>
            <execution>
              <id>repackage</id>
              <goals>
                <goal>repackage</goal>
              </goals>
            </execution>
          </executions>
          <configuration>
            <mainClass>${start-class}</mainClass>
          </configuration>
        </plugin>
      </plugins>
    </pluginManagement>

注:<executions>标签规定了插件某些goal的执行方式。

下图是在自己项目pom中添加spring-boot-maven-plugin插件前后的对比,我们可以看到,添加这个打包插件后,jar包格式发生了很大变化,且包含了第三方jar包。

springboot企业知识库管理系统 springboot官方文档_spring_07

 

2 使用springboot

2.1 配置类

springboot建议使用基于java的配置来代替xml配置文件。通常main方法所在的类是首选 @Configuration

没有必要把所有 @Configuration 放到一个类上。我们可以使用 @Import 注解随时添加额外的配置类。我们也可以使用 @ComponentScan 自动扫描加载Component,包括 @Configuration

如果确实需要加载xml格式的配置,建议仍然以 @Configuration 开始,然后使用 @ImportResource

2.2 Auto-configuration

Spring Boot自动配置会根据我们引入的依赖来配置我们的应用程序。例如,如果我们在classpath中引入了HSQLDB,那么Spring Boot会自动配置内存数据库

通过在任何一个@Configuration类上添加 @EnableAutoConfiguration 或 @SpringBootApplication

如果想知道哪些配置类生效了,可以使用如下方法打印自动配置列表

java -jar ./target/myproject-0.0.1-SNAPSHOT.jar --debug
.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.7.3)
xxxbalabalaxxx...省略
============================
CONDITIONS EVALUATION REPORT
============================

Positive matches: //这些是自动配置识别到的配置类,通常是因为classpath下有特定的类(比如javax.servlet.Servlet)从而触发javabean的加载
-----------------
WebMvcAutoConfiguration matched:
      - @ConditionalOnClass found required classes 'javax.servlet.Servlet', 'org.springframework.web.servlet.DispatcherServlet', 'org.springframework.web.servlet.config.annotation.WebMvcConfigurer' (OnClassCondition)
      - found 'session' scope (OnWebApplicationCondition)
      - @ConditionalOnMissingBean (types: org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; SearchStrategy: all) did not find any beans (OnBeanCondition)
        ...省略
Negative matches: //这些是自动配置没有识别到的配置类,将不会加载到spring容器
-----------------
   ActiveMQAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)
    ...省略
Exclusions:
-----------
    None
Unconditional classes: //这些是没有@Condition注解的类,也会注入到spring容器
----------------------
    org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration
    org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration
    org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration
    org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration
    org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration
2022-09-09 15:13:57.141  INFO 12352 --- [           main] MyApplication                            : Started MyApplication in 1.615 seconds (JVM running for 1.924)
2022-09-09 15:13:57.141 DEBUG 12352 --- [           main] o.s.b.a.ApplicationAvailabilityBean      : Application availability state LivenessState changed to CORRECT
2022-09-09 15:13:57.142 DEBUG 12352 --- [           main] o.s.b.a.ApplicationAvailabilityBean      : Application availability state ReadinessState changed to ACCEPTING_TRAFFIC
2022-09-09 15:14:13.433 DEBUG 12352 --- [ionShutdownHook] o.s.b.a.ApplicationAvailabilityBean      : Application availability state ReadinessState changed from ACCEPTING_TRAFFIC to REFUSING_TRAFFIC
2022-09-09 15:14:13.433 DEBUG 12352 --- [ionShutdownHook] ConfigServletWebServerApplicationContext : Closing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@15975490, started on Fri Sep 09 15:13:55 CST 2022

如果我们发现,某一个配置类被注入到容器,而我们不希望它被自动注入,我们可以在 @SpringBootApplication

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class MyApplication {

}

2.3 组件扫描和自动注入

我们使用 @ComponentScan 注解开启自动扫描,届时所有@Component@Service@Repository@Controller都将会注册到spring中

并且我们建议使用构造器注入的方式进行依赖注入,例子如下(如果存在多个构造器,使用@Autowired指定期望spring使用哪一个)

@Service
public class MyAccountService implements AccountService {

    private final RiskAssessor riskAssessor;

    private final PrintStream out;

    @Autowired
    public MyAccountService(RiskAssessor riskAssessor) {
        this.riskAssessor = riskAssessor;
        this.out = System.out;
    }

    public MyAccountService(RiskAssessor riskAssessor, PrintStream out) {
        this.riskAssessor = riskAssessor;
        this.out = out;
    }

    // ...

}

2.4 @SpringBootApplication

大多数springboot开发者希望自己的项目都需要@EnableAutoConfiguration、@ComponentScan、@SpringBootConfiguration功能,我们使用 @SpringBootApplication

// Same as @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

此外,这个注解中还定义了一些别名

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {
    @AliasFor(annotation = EnableAutoConfiguration.class)
    Class<?>[] exclude() default {};

    @AliasFor(annotation = EnableAutoConfiguration.class)
    String[] excludeName() default {};

    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};

    @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};

}

如果我们不想使用@ComponentScan功能,则,我们可以按如下方式

@SpringBootConfiguration(proxyBeanMethods = false)
@EnableAutoConfiguration
@Import({ SomeConfiguration.class, AnotherConfiguration.class })
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

这里,组件扫描不会起作用,因此我们使用@Import注解注入两个配置类到spring容器。

2.5 spring-boot-devtools插件

 该插件目的是为了提高开发效率。我们在pom中引入如下依赖就开启devtools支持。

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

引入这个插件后,只要classpath路径下发生变化,项目就会自动重启。如下所示

springboot企业知识库管理系统 springboot官方文档_springboot企业知识库管理系统_08

 

在idea中,我们调试时,classpath指的是target目录(参考:),项目跑起来,我们修改代码,发现springboot并不会重启。原因是,idea没有设置自动编译,target路径下的东西并没有发生变化。如果我们希望达到修改代码立即重启的目的,我们可以设置idea自动编译。(个人认为没有必要,如果想重新运行,手动重启就可以了)

当项目打包,我们直接运行jar包时,这个插件将被自动禁用。

3 核心特性

3.1 SpringApplication

SpringApplication是一个类,该类提供了启动spring应用的便捷方式,通常,我们使用它的静态方法启动spring容器,如下所示

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

SpringApplication的构造器如下

public SpringApplication(Class<?>... primarySources) {
        this(null, primarySources);
    }

其参数是一个配置源,这个配置源是一个@Configuration对象。

3.1.1 Lazy Initialization

如果我们设置了Lazy Initialization,bean将会在首次使用的时候实例化。

Lazy Initialization可以缩短启动时间,但是也同时延后了异常的发生时间。

比如一个类实例化时可能会发生异常,从而导致应用程序的退出。

又比如说,lazy Initialiation还会导致jvm分配的内存的持续增加,甚至jvm内存溢出,所以我们要慎重指定jvm分配内存大小。

我么使用如下配合设置Lazy Initialization

spring.main.lazy-initialization=true

3.1.2 定制化SpringApplication

如果SpringApplication的默认使用方法不满足要求,我们可以使用如下方式来设置SpringApplication的属性

@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(MyApplication.class);
        application.setBannerMode(Banner.Mode.OFF);
        application.run(args);
    }
}

3.1.3 SpringApplicationBuilder

还有一种构建spring容器的方式是使用流式API,这种方式可以构建结构化的spring容器(各个容器之间有父子关系)。

new SpringApplicationBuilder()
        .sources(Parent.class)
        .child(Application.class)
        .bannerMode(Banner.Mode.OFF)
        .run(args);

3.1.4 Application Availability

 这部分内容与k8s相关,待后续补充

3.1.5 Events and Listeners

springboot启动过程中,会发送很多Event,对应有很多Listeners监听这些事件。

很多Event是在ApplicationContext创建之前触发的,所以我们不能通过@Bean来注册listener来监听这些Event。正确的做法是,我们可以通过如下方式在spring容器创建之前注册listener

SpringApplication.addListeners(…)

或者

SpringApplicationBuilder.listeners(…)

或者在如下文件中配置

META-INF/spring.factories
例如
org.springframework.context.ApplicationListener=com.example.project.MyListener

events是通过spring框架的事件发布机制发布的。该机制对有些事件会向当前容器的listener发布,同时也会向其父容器中的listener发布。为了区别事件是从哪里来的,必须将产生事件的上下文注入到容器中。比如可以通过实现ApplicationContextAware接口。

public interface ApplicationContextAware extends Aware {
    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

}

3.1.6 Web Environment

如果classpath下提供了spring MVC,则AnnotationConfigServletWebServerApplicationContext类型的web上下文将会被初始化

如果classpath下没有提供spring MVC,但是提供了Spring WebFlux,则AnnotationConfigReactiveWebServerApplicationContext类型的web上下文将会被初始化

否则,AnnotationConfigApplicationContext类型的上下文将会被初始化

我们也可以使用 springApplication.setWebApplicationType(WebApplicationType)

3.1.7 ApplicationRunner 和 CommandLineRunner

这两个的作用类似,如果需要在SpringApplication.run()执行后,在正式接收请求前,执行一些额外的操作,可以使用这两个类。

@Component
public class MyCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) {
        // Do something...
    }
}

3.2 配置参数

springboot允许你采用多种方式进行参数配置,比如properties,yml,环境变量,命令行等方式。

参数值可以通过@Value注解进行使用。

配置参数的优先级顺序(后面的配置会覆盖前面的配置):

1 SpringApplication的defaultProperties属性

public class SpringApplication {
    ...省略...
    private Map<String, Object> defaultProperties;
    ...省略...
    
    public void setDefaultProperties(Map<String, Object> defaultProperties) {
        this.defaultProperties = defaultProperties;
    }

    public void setDefaultProperties(Properties defaultProperties) {
        this.defaultProperties = new HashMap<>();
        for (Object key : Collections.list(defaultProperties.propertyNames())) {
            this.defaultProperties.put((String) key, defaultProperties.get(key));
        }
    }
}

2 @Configuration类上的@PropertySource注解

3 配置文件(比如application.properties或者application.yml)

  配置文件的使用顺序是

  jar包内的application.properties或者application.yml。
  jar包内的application-{profile}.properties或者application-{profile}.yml
  jar包外的application.properties或者application.yml
  jar包外的application-{profile}.properties或者application-{profile}.yml

4 操作系统环境变量

5 java系统变量(System.getProperties())

6 来自java:comp/env的JNDI属性

7 ServletContext初始化参数

8 ServletConfig初始化参数

9 来自SPRING_APPLICATION_JSON中的属性

注:比如我们在命令行中首先定义了一个这个变量,然后再使用

SPRING_APPLICATION_JSON='{"server":{"port":"9999"}}' java -jar aaa.jar

或者作为java的系统变量提供

java -Dspring.application.json='{"server":{"port":"9999"}}' -jar xxx.jar

10 命令行参数

java -jar app.jar --name="Spring"

总结:建议在整个应用程序中使用一种格式的配置文件

3.2.1 命令行参数

SpringApplication把命令行参数转化为spring中的属性并且添加到Spring Environment。命令行参数优先级最高

如果我们想禁用命令行参数,使用如下方式 SpringApplication.setAddCommandLineProperties(false)

3.2.2 SPRING_APPLICATION_JSON

通常环境变量和System变量有很多限制,因此,springboot允许我们在json中配置变量并使用。

例如我们配置my.name=test为一个环境变量

$ SPRING_APPLICATION_JSON='{"my":{"name":"test"}}' java -jar myapp.jar

或者,配置到系统属性

$ java -Dspring.application.json='{"my":{"name":"test"}}' -jar myapp.jar

或者,直接放在命令行参数中

$ java -jar myapp.jar --spring.application.json='{"my":{"name":"test"}}'

3.2.3 配置文件

当应用启动时SpringBoot将在按照如下顺序自动查找并加载application.properties和application.yaml。后面的会覆盖前面的配置

classpath
classpath /config
当前路径
当前路径 /config

也就是说当启动一个jar包时,springboot默认读取jar包所在路径下的/config/application.properties

如果不想使用application.properties(.yml)作为前缀,可以设置,方法如下:

$ java -jar myproject.jar --spring.config.name=myproject

 

 

 

 

 

 

 

 

 

后续内容持续更新中...

 

Application Availability