SpringBoot
推荐阅读:
一、SpringBoot 基础
1. SpringBoot 简介
1.1 Spring 缺点
- 配置复杂
- 依赖管理比较麻烦,费时费力
1.2 SpringBoot 特点
- 起步依赖,就是将具备某种功能的坐标打包到一起,并提供一些默认的功能
- 自动配置,开箱即用,没有代码生成,也无需 xml 配置
2. SpringBoot 快速入门
2.1 手动创建项目
创建基本 maven 工程
添加 SpringBoot 起步依赖
SpringBoot 要求,项目要继承 SpringBoot 的起步依赖 spring-boot-starter-parent
1<parent>
2 <groupId>org.springframework.boot</groupId>
3 <artifactId>spring-boot-starter-parent</artifactId>
4 <version>2.0.1.RELEASE</version>
5</parent>
SpringBoot 要集成 SpringMVC 进行 Controller 的开发,所以项目要导入 web 的启动依赖
1<dependencies>
2 <dependency>
3 <groupId>org.springframework.boot</groupId>
4 <artifactId>spring-boot-starter-web</artifactId>
5 </dependency>
6</dependencies>
编写 SpringBoot 引导类
要通过 SpringBoot 提供的引导类起步 SpringBoot 才可以进行访问
1import org.springframework.boot.SpringApplication;
2import org.springframework.boot.autoconfigure.SpringBootApplication;
3
4// 声明此类是引导类
5@SpringBootApplication
6public class MySpringBootApplication {
7 public static void main(String[] args) {
8 // 启动程序,传入引导类字节码对象
9 SpringApplication.run(MySpringBootApplication.class);
10 }
11}
我们需要将引导类放置在项目的根包目录下,与 comtroller,service,dao 包平级,springboot 会自动扫描与引导类平级的包及其子目录下文件的注解
然后编写 controller ,即可发现能够使用
2.2 自动生成项目
使用 idea 新建项目的时候不要选择 maven,选择左边的 Spring Initializr
根据需求勾选需要组件即可
自动生成的 ServletInitializer 说明
ServletInitializer.java
1public class ServletInitializer extends SpringBootServletInitializer {
2
3 @Override
4 protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
5 return application.sources(SpringSecurityApplication.class);
6 }
7
8}
Springboot 启动方式有多种,常用的就是 application 启动类启动,直接 run 即可。
我们也可以使用外置 tomcat 启动,这时候就使用到了此类,此类需要继承 SpringBootServletInitializer 类并复写 configure() 方法,SpringBootServletInitializer 的执行过程,简单来说就是通过 SpringApplicationBuilder 构建并封装 SpringApplication 对象,并最终调用 SpringApplication 的 run 方法的过程。
SpringBootServletInitializer 就是原有的 web.xml 文件的替代。
2.3 自动生成项目依赖说明
2.3.1 Developer Tools
Spring Boot Devtools
热部署使用,具体请看 2.4
Spring Configuration Processor
spring boot 默认使用 yml/properties 中的配置,但有时候要用传统的 xml 或 其他配置文件,这时候就需要用到 spring configuration processor。
在配置类开头加上 @PropertySource("classpath:your.xml")
,其余用法与加载 yml 的配置一样
@PropertySource 中的属性
- value:指明加载配置文件的路径。
- ignoreResourceNotFound:指定的配置文件不存在是否报错,默认是false。当设置为 true 时,若该文件不存在,程序不会报错。实际项目开发中,最好设置 ignoreResourceNotFound 为 false。
- encoding:指定读取属性文件所使用的编码,我们通常使用的是UTF-8。
@ConfigurationProperties 设定前缀,这样就不用在每个属性前面设置 @Value("${user.*}")
了
1@ConfigurationProperties(prefix = "user")
2@PropertySource(value = {"classpath:resources/user.xml"},
3 ignoreResourceNotFound = false, encoding = "UTF-8")
4// 自动读取配置文件中的 user.name,user.age 赋给对应变量
5public class User {
6 private String name;
7 private int age;
8}
2.3.2 Web
Spring Web
构建 web 项目,使用 spring mvc 框架,使用 tomcat 当做默认服务器
2.4 SpringBoot 工程热部署
添加依赖
1<dependency>
2 <groupId>org.springframework.boot</groupId>
3 <artifactId>spring-boot-devtools</artifactId>
4</dependency>
开启自动编译
Idea -> settings -> build、execution、deployment -> compiler -> 勾选 Build project automatically
设置注册表
然后 shift+ctrl+alt+/ -> 选择 registry -> 勾选 compiler.automake.allow.when.runing
3. SpringBoot 原理分析
3.1 分析 spring-boot-starter-parent
查看 pom.xml 中,parent 标签中 spring-boot-starter-parent, 跳转到了 spring-boot-starter-parent 的 pom.xml,重点部分如下
1<parent>
2 <groupId>org.springframework.boot</groupId>
3 <artifactId>spring-boot-dependencies</artifactId>
4 <version>2.4.2</version>
5</parent>
6
7<resource>
8 <directory>${basedir}/src/main/resources</directory>
9 <filtering>true</filtering>
10 <includes>
11 <include>**/application*.yml</include>
12 <include>**/application*.yaml</include>
13 <include>**/application*.properties</include>
14 </includes>
15</resource>
我们发现自动扫描了 resources 中以 application 开头的三个配置文件,我们一般使用 yml 来替换默认配置,注意,如果我们既使用了 yml 也使用了 properties,则properties 配置会覆盖 yml 配置。
我们继续查看 spring-boot-starter-parent 的 parent,spring-boot-dependencies
1<properties>
2 <activemq.version>5.15.3</activemq.version>
3 <antlr2.version>2.7.7</antlr2.version>
4 <appengine-sdk.version>1.9.63</appengine-sdk.version>
5 <artemis.version>2.4.0</artemis.version>
6 <aspectj.version>1.8.13</aspectj.version>
7 <assertj.version>3.9.1</assertj.version>
8 <atomikos.version>4.0.6</atomikos.version>
9 <bitronix.version>2.1.4</bitronix.version>
10 <build-helper-maven-plugin.version>3.0.0</build-helper-maven-plugin.version>
11 <byte-buddy.version>1.7.11</byte-buddy.version>
12</properties>
13<dependencyManagement>
14 <dependencies>
15 <dependency>
16 <groupId>org.springframework.boot</groupId>
17 <artifactId>spring-boot</artifactId>
18 <version>2.0.1.RELEASE</version>
19 </dependency>
20 <dependency>
21 <groupId>org.springframework.boot</groupId>
22 <artifactId>spring-boot-test</artifactId>
23 <version>2.0.1.RELEASE</version>
24 </dependency>
25 </dependencies>
26</dependencyManagement>
27<build>
28 <pluginManagement>
29 <plugins>
30 <plugin>
31 <groupId>org.jetbrains.kotlin</groupId>
32 <artifactId>kotlin-maven-plugin</artifactId>
33 <version>${kotlin.version}</version>
34 </plugin>
35 <plugin>
36 <groupId>org.jooq</groupId>
37 <artifactId>jooq-codegen-maven</artifactId>
38 <version>${jooq.version}</version>
39 </plugin>
40 <plugin>
41 <groupId>org.springframework.boot</groupId>
42 <artifactId>spring-boot-maven-plugin</artifactId>
43 <version>2.0.1.RELEASE</version>
44 </plugin>
45 </plugins>
46 </pluginManagement>
47</build>
从上面的 pom.xml 中我们可以发现,一部分坐标的版本、依赖管理、插件管理已经定义好,所以我们的 SpringBoot 工程继承 spring-boot-starter-parent 后已经具备版本锁定等配置了。所以起步依赖的作用就是进行依赖的传递。
3.2 分析 spring-boot-starter-web
查看 pom.xml 中的 spring-boot-starter-web,跳转到了spring-boot-starter-web 的pom.xml,xml 配置如下(只摘抄了部分重点配置):
1<?xml version="1.0" encoding="UTF-8"?>
2<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
4 <dependencies>
5 <dependency>
6 <groupId>org.springframework.boot</groupId>
7 <artifactId>spring-boot-starter</artifactId>
8 <version>2.4.2</version>
9 <scope>compile</scope>
10 </dependency>
11 <dependency>
12 <groupId>org.springframework.boot</groupId>
13 <artifactId>spring-boot-starter-json</artifactId>
14 <version>2.4.2</version>
15 <scope>compile</scope>
16 </dependency>
17 <dependency>
18 <groupId>org.springframework.boot</groupId>
19 <artifactId>spring-boot-starter-tomcat</artifactId>
20 <version>2.4.2</version>
21 <scope>compile</scope>
22 </dependency>
23 <dependency>
24 <groupId>org.springframework</groupId>
25 <artifactId>spring-web</artifactId>
26 <version>5.3.3</version>
27 <scope>compile</scope>
28 </dependency>
29 <dependency>
30 <groupId>org.springframework</groupId>
31 <artifactId>spring-webmvc</artifactId>
32 <version>5.3.3</version>
33 <scope>compile</scope>
34 </dependency>
35 </dependencies>
36</project>
从上面的 spring-boot-starter-web 的 pom.xml 中我们可以发现,spring-boot-starter-web 就是将 web 开发要使用的 spring-web、spring-webmvc 等坐标进行了“打包”,这样我们的工程只要引入 spring-boot-starter-web 起步依赖的坐标就可以进行 web 开发了,同样体现了依赖传递的作用。
3.3 自动配置原理解析
查看启动类的注解 @SpringBootApplication,源码如下
1@Target(ElementType.TYPE)
2@Retention(RetentionPolicy.RUNTIME)
3@Documented
4@Inherited
5@SpringBootConfiguration
6@EnableAutoConfiguration
7@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
8 @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
9public @interface SpringBootApplication {
10 @AliasFor(annotation = EnableAutoConfiguration.class)
11 Class<?>[] exclude() default {};
12}
- @SpringBootConfifiguration:等同与 @Confifiguration,既标注该类是 Spring的一个配置类
- @EnableAutoConfifiguration:SpringBoot 自动配置功能开启
查看注解 @EnableAutoConfifiguration
1@Target(ElementType.TYPE)
2@Retention(RetentionPolicy.RUNTIME)
3@Documented
4@Inherited
5@AutoConfigurationPackage
6@Import(AutoConfigurationImportSelector.class)
7public @interface EnableAutoConfiguration {}
其中,@Import(AutoConfifigurationImportSelector.class)
导入了AutoConfifigurationImportSelector 类,查看源码
1protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
2 if (!isEnabled(annotationMetadata)) {
3 return EMPTY_ENTRY;
4 }
5 AnnotationAttributes attributes = getAttributes(annotationMetadata);
6 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
7 configurations = removeDuplicates(configurations);
8 Set<String> exclusions = getExclusions(annotationMetadata, attributes);
9 checkExcludedClasses(configurations, exclusions);
10 configurations.removeAll(exclusions);
11 configurations = getConfigurationClassFilter().filter(configurations);
12 fireAutoConfigurationImportEvents(configurations, exclusions);
13 return new AutoConfigurationEntry(configurations, exclusions);
14}
15
16protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
17 List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
18 Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " + "are using a custom packaging, make sure that file is correct.");
19 return configurations;
20}
其中,SpringFactoriesLoader.loadFactoryNames 方法的作用就是从 META-INF/spring.factories
文件中读取指定类对应的类名称列表
如下:
1# Auto Configure
2org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
3org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
4org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
5org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
6org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
7org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
上面配置文件存在大量的以 Confifiguration 为结尾的类名称,这些类就是存有自动配置信息的类,而 SpringApplication 在获取这些类名后再加载。
我们以 ServletWebServerFactoryAutoConfiguration 为例来分析源码:
1@Configuration(proxyBeanMethods = false)
2@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
3@ConditionalOnClass(ServletRequest.class)
4@ConditionalOnWebApplication(type = Type.SERVLET)
5@EnableConfigurationProperties(ServerProperties.class)
6@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
7 ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
8 ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
9 ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
10public class ServletWebServerFactoryAutoConfiguration {}
其中,@EnableConfifigurationProperties(ServerProperties.class)
代表加载 ServerProperties 服务器配置属性类
源码如下:
1@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
2public class ServerProperties {
3 private Integer port;
4 private InetAddress address;
5}
其中,prefifix = "server"
表示 SpringBoot 配置文件中的前缀,SpringBoot 会将配置文件中以 server 开始的属性映射到该类的字段中。映射关系如下:
然后查看 spring-boot-autoconfigure 下的 META-INF/spring-configuration-metadata.json
,能够发现如下默认配置
1{
2 "name": "server.port",
3 "type": "java.lang.Integer",
4 "description": "Server HTTP port.",
5 "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties",
6 "defaultValue": 8080
7}
我们可以自定义配置文件来替换默认配置。
4. SpringBoot 的配置文件
SpringBoot 是基于约定的,所以很多配置都有默认值,但如果想使用自己的配置替换默认配置的话,一般使用 application.properties 或者 application.yml(application.yaml)进行配置。
SpringBoot 默认会从 resources 目录下加载 application.properties 或 application.yml(application.yaml)文件
配置信息的查询,上面有提及从源码中查找的方式,也可以从文档查找
常用的配置摘抄如下:
1# QUARTZ SCHEDULER (QuartzProperties)
2spring.quartz.jdbc.initialize-schema=embedded # Database schema initialization mode.
3spring.quartz.jdbc.schema=classpath:org/quartz/impl/jdbcjobstore/tables_@@platform@@.sql # Path to the SQL file to use to initialize the database schema.
4spring.quartz.job-store-type=memory # Quartz job store type.
5spring.quartz.properties.*= # Additional Quartz Scheduler properties.
6
7# ----------------------------------------
8# WEB PROPERTIES
9# ----------------------------------------
10
11# EMBEDDED SERVER CONFIGURATION (ServerProperties)
12server.port=8080 # Server HTTP port.
13server.servlet.context-path= # Context path of the application.
14server.servlet.path=/ # Path of the main dispatcher servlet.
15
16# HTTP encoding (HttpEncodingProperties)
17spring.http.encoding.charset=UTF-8 # Charset of HTTP requests and responses. Added to the "Content-Type" header if not set explicitly.
18
19# JACKSON (JacksonProperties)
20spring.jackson.date-format= # Date format string or a fully-qualified date format class name. For instance, `yyyy-MM-dd HH🇲🇲ss`.
21
22# SPRING MVC (WebMvcProperties)
23spring.mvc.servlet.load-on-startup=-1 # Load on startup priority of the dispatcher servlet.
24spring.mvc.static-path-pattern=/** # Path pattern used for static resources.
25spring.mvc.view.prefix= # Spring MVC view prefix.
26spring.mvc.view.suffix= # Spring MVC view suffix.
27
28# DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
29spring.datasource.driver-class-name= # Fully qualified name of the JDBC driver. Auto- detected based on the URL by default.
30spring.datasource.password= # Login password of the database.
31spring.datasource.url= # JDBC URL of the database.
32spring.datasource.username= # Login username of the database.
33
34# JEST (Elasticsearch HTTP client) (JestProperties)
35spring.elasticsearch.jest.password= # Login password.
36spring.elasticsearch.jest.proxy.host= # Proxy host the HTTP client should use.
37spring.elasticsearch.jest.proxy.port= # Proxy port the HTTP client should use.
38spring.elasticsearch.jest.read-timeout=3s # Read timeout.
39spring.elasticsearch.jest.username= # Login username.
我们可以通过配置 application.poperties 或者 application.yml 来修改 SpringBoot 的默认配置
例如
1# application.properties 文件
2server.port=8888
3server.servlet.context-path=demo
4
5# application.yml 文件
6server:
7 port: 8888
8 servlet:
9 context-path: /demo
5. 配置文件与配置类的属性映射方式
5.1 @Value
我们可以通过 @Value 注解将配置文件中的值映射到一个 Spring 管理的 Bean 的字段上。
例如:
application.yml 配置如下:
1person:
2 name: zhangsan
3 age: 18
实体 Bean 代码如下:
1@Controller
2public class QuickStartController {
3 @Value("${person.name}")
4 private String name;
5 @Value("${person.age}")
6 private Integer age;
7
8 @RequestMapping("/quick")
9 @ResponseBody public String quick(){
10 return "springboot 访问成功! name="+name+",age="+age;
11 }
12}
5.2 @ConfigurationProperties
通过注解 @ConfifigurationProperties(prefifix="配置文件中的key的前缀")
可以将配置文件中的配置自动与实体进行映射。
application.yml 配置如下:
1person:
2 name: zhangsan
3 age: 18
实体 Bean 代码如下:
1@Controller
2@ConfigurationProperties(prefix = "person")
3public class QuickStartController {
4 private String name;
5 private Integer age;
6
7 @RequestMapping("/quick")
8 @ResponseBody public String quick(){
9 return "springboot 访问成功! name="+name+",age="+age;
10 }
11}
6. SpringBoot 整合其他技术
6.1 整合 Mybatis
添加依赖
1<!--mybatis起步依赖-->
2<dependency>
3 <groupId>org.mybatis.spring.boot</groupId>
4 <artifactId>mybatis-spring-boot-starter</artifactId>
5 <version>1.1.1</version>
6</dependency>
7<!-- MySQL连接驱动 -->
8<dependency>
9 <groupId>mysql</groupId>
10 <artifactId>mysql-connector-java</artifactId>
11</dependency>
配置连接信息
application.properties
1#DB Configuration:
2spring.datasource.driverClassName=com.mysql.jdbc.Driver
3spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8
4spring.datasource.username=root
5spring.datasource.password=root
编写业务
**在 application.properties 中添加 mybatis 的信息 **
1#spring集成Mybatis环境
2#pojo别名扫描包
3mybatis.type-aliases-package=com.itheima.domain
4#加载Mybatis映射文件
5mybatis.mapper-locations=classpath:mapper/*Mapper.xml
6.2 整合 Junit
添加依赖
1<!--测试的起步依赖-->
2<dependency>
3 <groupId>org.springframework.boot</groupId>
4 <artifactId>spring-boot-starter-test</artifactId>
5 <scope>test</scope>
6</dependency>
编写测试类
1package com.itheima.test;
2
3import com.itheima.MySpringBootApplication;
4import com.itheima.domain.User;
5import com.itheima.mapper.UserMapper;
6import java.util.List;
7import org.junit.Test;
8import org.junit.runner.RunWith;
9import org.springframework.beans.factory.annotation.Autowired;
10import org.springframework.boot.test.context.SpringBootTest;
11import org.springframework.test.context.junit4.SpringRunner;
12
13@RunWith(SpringRunner.class)
14@SpringBootTest(classes = MySpringBootApplication.class)
15public class MapperTest {
16
17 @Autowired
18 private UserMapper userMapper;
19
20 @Test
21 public void test() {
22 List<User> users = userMapper.queryUserList();
23 System.out.println(users);
24 }
25}
- SpringRunner 继承自 SpringJUnit4ClassRunner,使用哪一个 Spring 提供的测试测试引擎都可以。
- @SpringBootTest 的属性指定的是引导类的字节码对象
6.3 整个 Redis
添加依赖
1<!-- 配置使用redis启动器 -->
2<dependency>
3 <groupId>org.springframework.boot</groupId>
4 <artifactId>spring-boot-starter-data-redis</artifactId>
5</dependency>
配置连接信息
1# Redis
2spring.redis.host=127.0.0.1
3spring.redis.port=6379
测试
1@RunWith(SpringRunner.class)
2@SpringBootTest(classes = SpringbootJpaApplication.class)
3public class RedisTest {
4
5 @Autowired
6 private UserRepository userRepository;
7
8 @Autowired
9 private RedisTemplate<String, String> redisTemplate;
10
11 @Test
12 public void test() throws JsonProcessingException { //从redis缓存中获得指定的数据
13 String userListData = redisTemplate.boundValueOps("user.findAll").get();
14 //如果redis中没有数据的话
15 if (null == userListData) {
16 //查询数据库获得数据
17 List<User> all = userRepository.findAll();
18 //转换成json格式字符串
19 ObjectMapper om = new ObjectMapper();
20 userListData = om.writeValueAsString(all);
21 //将数据存储到redis中,下次在查询直接从redis中获得数据,不用在查询数据库
22 redisTemplate.boundValueOps("user.findAll").set(userListData);
23 System.out.println("===============从数据库获得数据===============");
24 } else {
25 System.out.println("===============从redis缓存中获得数据===============");
26 }
27 System.out.println(userListData);
28 }
29}
6.4 整合 Spring Data JPA
添加依赖
1<!-- springBoot JPA的起步依赖 -->
2<dependency>
3 <groupId>org.springframework.boot</groupId>
4 <artifactId>spring-boot-starter-data-jpa</artifactId>
5</dependency>
6<!--jdk9需要导入如下坐标-->
7<dependency>
8 <groupId>javax.xml.bind</groupId>
9 <artifactId>jaxb-api</artifactId>
10 <version>2.3.0</version>
11</dependency>
配置文件
application.properties
1#DB Configuration:
2spring.datasource.driverClassName=com.mysql.jdbc.Driver
3spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8
4spring.datasource.username=root
5spring.datasource.password=root
6
7#JPA Configuration:
8spring.jpa.database=MySQL
9spring.jpa.show-sql=true
10spring.jpa.generate-ddl=true
11spring.jpa.hibernate.ddl-auto=update
12spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.ImprovedNamingStrategy
创建实体类
1@Entity
2public class User {
3 // 主键
4 @Id
5 @GeneratedValue(strategy = GenerationType.IDENTITY)
6 private Long id; // 用户名
7 private String username; // 密码
8 private String password; // 姓名
9 private String name;
10 //此处省略setter和getter方法... ...
11}
编写 UserRepository
1public interface UserRepository extends JpaRepository<User, Long> {
2 public List<User> findAll();
3}
编写测试类
1@RunWith(SpringRunner.class)
2@SpringBootTest(classes = MySpringBootApplication.class)
3public class JpaTest {
4
5 @Autowired
6 private UserRepository userRepository;
7
8 @Test
9 public void test() {
10 List<User> users = userRepository.findAll();
11 System.out.println(users);
12 }
13}