springboot官方文档解读
官网地址:https://docs.spring.io/spring-boot/docs/2.7.3/reference/htmlsingle/
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包。
上面,我们还没有引入任何依赖。当我们运行mvn dependency:tree命令,我们可以看到项目的依赖关系
如果我们需要创建的是一个web项目,则我们需要引入spring-boot-starter-web依赖。
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
注:springboot中为我们提供了很多starter,我们引入不同的starter会创建不同类型的应用。
我们再次查看依赖关系,此时依赖就和上面不一样了。
我们看到,我们引入了starter依赖后,项目会自动为我们加载springmvc、spring-autoconfigure、tomcat相关的jar包。通过这里我们可以体会一下starter的作用。
maven默认源码路径为:src/main/java,为什么?这属于maven的范畴,这里不做细究,只做一下说明:使用mvn help:effective-pom命令我们可以得到项目的一个整体pom,在这个pom中我们可以看到其源码路径:
我们在源码路径下面创建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 可以启动项目。
然后,我么浏览器访问
至于为什么使用mvn spring-boot:run启动项目,可以参考https://blog.csdn.net/zzuhkp/article/details/123493071
接下来,我们希望使用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包。
2 使用springboot
2.1 配置类
springboot建议使用基于java的配置来代替xml配置文件。通常main方法所在的类是首选 @Configuration 。
没有必要把所有 @Configuration 放到一个类上。我们可以使用 @Import 注解随时添加额外的配置类。我们也可以使用 @ComponentScan 自动扫描加载Component,包括 @Configuration 类。
如果确实需要加载xml格式的配置,建议仍然以 @Configuration 开始,然后使用 @ImportResource 加载xml配置。
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 注解上使用exclude属性排除这个配置类
@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路径下发生变化,项目就会自动重启。如下所示
在idea中,我们调试时,classpath指的是target目录(参考:https://www.cnblogs.com/zhenjingcool/p/16530567.html),项目跑起来,我们修改代码,发现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) 显式设置web类型。
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
后续内容持续更新中...