Spring Boot入门
一 快速入门
1.1 最简单的HelloWrold程序
步骤一 在pom中引入以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
这个依赖会自动把spring-web, spring-webmvc, spring-context, tomcat等包引到项目中。
步骤二 新建一个Controller,代码如下:
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 HelloController {
@RequestMapping("/")
public String hello() {
return "hello";
}
public static void main(String[] args) {
SpringApplication.run(HelloController.class);
}
}
步骤三 运行HelloController中的main方法,访问http://localhost:8080/即可看到页面上输出的结果。
注: Spring官方推荐使用java8.0,并且使用maven3.2+版本。
二 使用SpringBoot整合常见的框架
2.1 使用SpringBoot整合mybatis
首先要引入mybatis-spring-boot-starter依赖和mysql的依赖:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
注:mybatis-spring-boot-starter是应该是由mybatis提供的,而不是SpringBoot提供的。如果我们不想用mybatis,只想用数据库方面的内容,可以引入SpringBoot提供的spring-boot-starter-data-jdbc依赖
然后要告诉SpringBoot我们数据库的数据源,在resources目录下新建一个文件application.peoperties文件(SpringBoot会默认读取这个文件的配置),然后配置好数据库相关的内容:
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
同时,还要配置好mybatis相关的内容,主要是要指定mapper文件的位置和类别名的扫描路径:
mybatis.typeAliasesPackage=com.akichyan.domain
mybatis.mapperLocations=classpath:mapper/*.xml
接下来只需要编写好Dao和对应的mapper文件即可,需要注意的是,启动类中要指定好Dao的描扫目录,使用@MapperScan注解,如下代码的第三行:
@RestController
@EnableAutoConfiguration
@MapperScan("com.akichyan.dao")
public class HelloController {
@Resource
private UserDao userDao;
@RequestMapping("/")
public String hello() {
return userDao.getUser(1).getName();
}
public static void main(String[] args) {
SpringApplication.run(HelloController.class);
}
}
重新启动即可。
2.2 整合SpringMVC
快速入门中的例子其实就已经是整合了SpringMVC,此处不再重复,但是上述的例子中,HelloController即充当了启动类,又充当了Controller类,我们可以把它拆一下,方便理解:
- 首先HelloController类只保留Controller的相关代码:
package com.akichyan.web;
import com.akichyan.dao.UserDao;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
public class HelloController {
@Resource
private UserDao userDao;
@RequestMapping("/")
public String hello() {
return userDao.getUser(1).getName() ;
}
}
- 其次再新建一个启动类,在这个类中我们要额外加一@ComponentScan(basePackages = "com.akichyan"),这个注解配置了一个自动扫包,可以让我们上面定义的HelloController被扫描到。
package com.akichyan;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
@EnableAutoConfiguration
@MapperScan("com.akichyan.dao")
@ComponentScan(basePackages = "com.akichyan")
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
三 SpringBoot中常用的注解及其作用
@SpringBootApplication
等同于同时加上了以下三个注解:
@ComponentScan
@EnableAutoConfiguration
@Configuration
@Configuration
其实就是JavaConfig形式的SpringIoC容器配置的那个@Configuration,SpringBoot是基本JavaConfig形式来实现的。
这个注解表明本类是一个JavaConfig配置类。
@EnableAutoConfiguration
这个注解会去META-INF目录下查找一个叫spring.factories的文件,然后将文件中的org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的条目解析出来,加载对应的类,这些类都是AppConfig类。可以写一个示例代码:
- 步骤一:新建一个AppConfig类:
package com.akichyan.config;
import com.akichyan.domain.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyAppConfig {
@Bean
public User getDefaultUser() {
return new User();
}
}
- 步骤二:在resources目录下新建一个META-INF目录,然后在META-INF目录下新建一个spring.factories文件,指定要加载的配置类:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.akichyan.config.MyAppConfig
可以指定多个,用逗号隔开。
- 步骤三:在其他类中引入这个User类即可(可以直接debug一下getDefaultUser方法,发现这个方法在启动过程中的确被调用到了)
注意:EnableAutoConfiguration注解会加载所有jar包下的META-INF下的spring.factories文件,在spring-boot-autoconfigure包下有一个自带的spring.factories文件,这个文件的配置会被加载。
@ComponentScan
这个用于指定描包的路径,如:
@ComponentScan(basePackages = "com.akichyan.scan")
我们可以在指定包下加一个类,这个类必需要使用@Component @Controller @Service等注解注册为bean
package com.akichyan.scan;
import org.springframework.stereotype.Component;
@Component
public class ScanBean {
private String id = "SCAN_BAN_ID";
public String getId() {
return id;
}
}
四 SpringBoot其它关键内容
4.1 SpringBoot配置properties文件
- 读取默认配置文件中的属性
直接把proerty数据写在spring.properties文件中:
db.username=root
db.password=123456
db.maxIdle=20
然后直接使用@Value注解配置即可:
@RestController
public class HelloController {
@Value("${db.username}")
private String dbName;
@Value("${db.maxIdle}")
private int maxIdle;
@RequestMapping("/")
public String hello() {
return dbName + ":" +maxIdle;
}
}
- 读取指定文件
可以使用@PropertySource注解来读取其他配置文件中的数据:
@Component
@PropertySource("db.properties")
public class DBConfig {
@Value("${db.name}")
private String name;
@Value("${db.password}")
private String password;
@Value("${db.url}")
private String url;
}
db.properties文件内容:
db.name=root
db.password=123456
db.url=jdbc:mysql://localhost:3306/test
注意这个@PropertySource("db.properties")虽然是放在DBConfig这个类上的,但是其他类中也可以引用db.properties中的数据。
- 使用@ConfigurationProperties自动映射
上个例子中properties中的数据的key和类对的字段名是有对应关系的,都是多了一个前缀db.,我们可以使用ConfigurationProperties来自动映射,可以避免多处写@Value:
@Component
@Data
@ConfigurationProperties(prefix = "db")
public class DBConfig {
private String name;
private String password;
private String url;
}
- 使用Environment对象来获取
@Repository
public class DataSourceFactory {
@Autowired
private Environment environment;
public DataSource createDataSource() {
String name = environment.getProperty("db.name");
String password = environment.getProperty("db.password");
String url = environment.getProperty("db.url");
System.out.println(name + ":" + password + ":" + url);
return new MyDataSource(name, password, url) ;
}
}
Environment对象是SpringBoot用于保存properties数据的,直接引入即可,然后就可以使用这个对象来获取对象的property数据。
4.2 使用spring-boot-parent做版本控制
可以使用parent的方式和dependencyManagement的方式:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
</dependencies>
</dependencyManagement>
Spring-boot除了基本maven外,还可以基本Gradle来使用,此外还可以使用Spring Boot CLI来使用。
网络上有的文章 说SpringBoot还要执行什么命令之类的,就是直接使用了SpringBoot CLI,如果使用Maven的话不需要执行任何命令
4.3 JavaConfig内容复习
除了使用xml文件配置bean外,SpringIoC还提供了一种JavaConfig的方式,比较简单,示例代码如下:
@Configuration
@ComponentScan("com.akichyan.scan")
@Import(DataSourceFactory.class)
public class MyAppConfig {
@Bean
public User emptyUser() {
return new User();
}
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyAppConfig.class);
//.......
DataSource dataSource = applicationContext.getBean(DataSource.class);
System.out.println(dataSource);
}
}
@Configuration
@PropertySource("db.properties")
public class DataSourceFactory {
@Autowired
private Environment environment;
@Bean(name = "dataSource")
@Profile("prod")
public DataSource createProdDataSource() {
System.out.println("Init prod datasource");
String name = environment.getProperty("db.prod.name");
String password = environment.getProperty("db.prod.password");
String url = environment.getProperty("db.prod.url");
return DataSourceBuilder.create().username(name).password(password).url(url).build();
}
@Bean(name = "dataSource")
@Profile("dev")
public DataSource createDevDataSource() {
System.out.println("Init dev datasource");
String name = environment.getProperty("db.dev.name");
String password = environment.getProperty("db.dev.password");
String url = environment.getProperty("db.dev.url");
return DataSourceBuilder.create().username(name).password(password).url(url).build();
}
}
启动时需要添加启动参数:
-Dspring.profiles.active="dev" 或 -Dspring.profiles.active="prod"
- @Configuration: 注解在类上,表明此类是一个JavaConfig类,等同于xml文件中的<beans />标签。
- @Import: 导入其他的JavaConfig类,类似于xml文件中的<import />标签
- @Bean: 标记在方法上,这个方法会在Spring容器启动时被调用,方法的返回值会被注册成一个bean,等同于<bean />标签
- Environment: 这是个类,不是注解,里面保存了properties文件中的数据
- @PropertySource: 用于引入properties文件
- @Profile 用于标记环境,在启动参数中激活环境
五 SpringApplication的工作流程
-
就是说你要启动的应用到底是个普通的java应用,还是一个Web应用。SpringBoot中定义了三种类型:
public enum WebApplicationType {
NONE,
SERVLET,
REACTIVE;
}
NONE : 就是一个普通的Java应用。
SERVLET:看名字就知道,servlet,一个Web应用。
REACTIVE:嗯,这个东西笔者没接触过,网上查了一下发现是Spring5.0出的响应式流的新框架。
默认值是SERVLET
SpringBoot会尝试去load Servlet和ConfigurableWebApplicationContext这两个类,只要有一个加载失败(就是说你根本就没引对应的jar包),就会认为是NONE类型。
-
关于ApplicationContextInitializer,这有一篇文章写得比较详细,它是用于做什么的,以及它的配置方式都有了:https://www.jianshu.com/p/3828e93be20d。
简单重复一下:我们可以写一个类实现ApplicationContextInitializer,配置在spring.factories文件或者application.properties文件中,那Spring在刷新上下文时会调用这个实现类
-
类似于上面的ApplicationListener,这是Spring的监听器,可以配置在spring.factories文件中,SpringBoot会自动查找。SpringBoot默认的配置文件中的监听器配置如下:
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
-
SpringBoot先创建了一个RuntimeExecption类,然后拿到调用栈后依次判断涉及到的每个类是否包含main方法,如果包含,则认为那个类为启动类。
以上四步都是SpringApplication在初始化的时候做的事,接下来的步骤会在SpringApplication.run方法中完成
-
SpringApplicationRunListener与上面的ApplicationListener类似,是一个监听器,这个类中定义了以下几个方法:
public interface SpringApplicationRunListener {
void starting();
void environmentPrepared(ConfigurableEnvironment environment);
void contextPrepared(ConfigurableApplicationContext context);
void contextLoaded(ConfigurableApplicationContext context);
void started(ConfigurableApplicationContext context);
void running(ConfigurableApplicationContext context);
void failed(ConfigurableApplicationContext context, Throwable exception);
}
同样的可以在spring.factories文件中配置,配置的key就是SpringApplicationRunListener的全类名。
SpringBoot查找到这些类后会马上实例化,并调用这些类的stared方法。
Environment是Spring的环境类,主要是包含了perperties信息。还包含了Spring当前所处的环境信息,如dev,prod等。
- 首先会根据当前的应用来型来创建不同的对象:
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
创业对象完成后,会去加载properties文件中的数据
这一步作者还没有去研究,先贴一下书上说的内容:
如果SpringApplication的showBanner属性被设置为true,则打印banner(SpringBoot 1.3.x版本,这里应该是基于Banner.Mode决定banner的打印行为)。这一步的逻辑其实可以不关心,我认为唯一的用途就是“好玩”(Just For Fun)。
首先会创建ApplicationContext对象,同样的会根据应用类型来使用不同的ApplicationContext实现类。
然后会根据用户的设置来判断是否需要使用指定的beanNameGenerator,以及是否需要使用自定义的resourceLoader。
接着会调用ApplicationContextInitializer的initialize方法
接着会调用SpringApplicationRunListeners的contextPrepared方法
然后会调用ApplicationContext对象的refresh方法(这个方法其实也就是SpringIoC容器初始化的方法,这个方法调用完说明IoC容器完成了)
然后会调用SpringApplicationRunListener的started方法
然后会调用SpringApplicationRunListener的running方法
至此SpringBoot就算启动完成了,以上过程看似复杂,其实大部都是在调用一下Listener和EventListener的相应方法,如果把这些方法去掉,整个启动过程其实可以简化为以下四步:
- 从配置文件中查找SpringApplicationRunListener对象,ApplicationContextInitializer对象,ApplicationListener对象等。也就是把我们注册的监听器全找出来。
- 初始化Environment对象,包括加载properties文件,处理profile信息。
- 初始化ApplicationContext对象。
-
refresh ApplicationContext
SpringBoot启动流程