20201209 Spring Boot - 拉勾教育
SpringBoot 基础回顾
-
Spring Boot 是所有基于 Spring 开发的项目的起点。Spring Boot 的设计是为了让你尽可能快的跑起来 Spring 应用程序并且尽可能减少你的配置文件。
-
约定优于配置(Convention over Configuration),又称按约定编程,是一种软件设计范式
-
Spring Boot 在 Spring 的基础上做了两项改进:
- 依赖管理
- starter 约定
- 依赖版本控制
- 自动配置
- 依赖管理
单元测试
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
@RunWith(SpringRunner.class) //测试启动器,并加载spring boot测试注解
@SpringBootTest //标记该类为spring boot单元测试类,并加载项目的applicationContext上下文环境
class Springboot01DemoApplicationTests {
//入门案例测试
@Autowired
private HelloController helloController;
@Test
void contextLoad1() {
String demo = helloController.demo();
System.out.println(demo);
}
}
热部署
- 增加依赖
<!-- 引入热部署依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
- 勾选 File | Settings | Build, Execution, Deployment | Compiler | Build project automatically
- 快捷键 Ctrl+Shift+Alt+/ ,选择 Registry,勾选 compiler.automake.allow.when.app.running
- Ctrl+F9 触发 Build Project
- 重启生效
配置相关
@ConfigurationProperties 添加配置提示
-
POM
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
-
使用注解
@ConfigurationProperties
@ConfigurationProperties(prefix = "person") //将配置文件中以person开头的属性注入到该类中 public class Person { private int id; //id private String name; //名称 private List hobby; //爱好 private String[] family; //家庭成员 private Map map; private Pet pet; //宠物 }
-
Ctrl+F9,Build Project
Properties、Yaml 配置写法
person.id=10
person.name=张三
person.hobby=h1,h2,h3
person.family=f1,f2,f3
person.map.k1=v1
person.map.k2=v2
person.pet.type=dog
person.pet.name=旺财
person:
id: 12
name: 李四
hobby: [h1,h2,h3]
family: [f1,f2]
map: {k1: v1, k2: v2}
pet: {type: dog, name: 旺财}
person:
id: 12
name: 李四
hobby:
- h1
- h2
- h3
family:
f1
f2
map:
k1: v1
k2: v2
k3: v3
"[/key1]": v4
pet:
type: dog
name: ndog
绑定 Map
属性时,如果 key 包含除小写字母、数字字符或 -
之外的任何内容,则需要使用方括号表示法,以便保留原始值。如果键没有被 []
包围,则所有其他字符被删除。例如:
map:
"[/key1]": value1
"[/key2]": value2
/key3: value3
Map具有 /key1
,/key2
和 key3
作为映射中的键。
@Value 属性注入
@Value("${person.id:20}")
@PropertySource 加载配置文件
将属性文件作为 PropertySource
加载到 Spring 容器中
@Data
@Component
@PropertySource("classpath:testa.properties") //配置自定义配置文件的名称及位置
@ConfigurationProperties(prefix = "test")
public class MyProperties {
private int id;
private String name;
}
@Configuration 编写自定义配置类
- 类似于声明了一个 XML 配置文件
随机数设置
org.springframework.boot.env.RandomValuePropertySource
# 配置随机值
my.secret=${random.value}
# 配置随机整数
my.number=${random.int}
# 配置随机long类型数
my.bignumber=${random.long}
# 配置随机uuid类型数
my.uuid=${random.uuid}
# 配置小于10的随机整数
my.number.less.than.ten=${random.int(10)}
# 配置范围在[1024,65536]之间的随机整数
my.number.in.range=${random.int[1024,65536]}
参数间引用
test.v1=v1
test.v2=${test.v1} is v2
test.v3=${user.country} is v2
SpringBoot 原理深入及源码剖析
依赖管理
Spring Boot 项目在 POM 中需要继承 spring-boot-starter-parent
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
在 spring-boot-starter-parent-2.2.2.RELEASE.pom
指定了继承 spring-boot-dependencies
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>
在 spring-boot-dependencies-2.2.2.RELEASE.pom
中,定义了版本控制
<properties>
......
<wsdl4j.version>1.6.3</wsdl4j.version>
......
</properties>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
......
......
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
<version>${wsdl4j.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
自动配置(启动流程)
SpringBoot 将 spring.factories
中 org.springframework.boot.autoconfigure.EnableAutoConfiguration
对应的值加入容器的过程:
org.springframework.boot.autoconfigure.SpringBootApplication
org.springframework.boot.autoconfigure.EnableAutoConfiguration
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#selectImports
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#getAutoConfigurationMetadata
org.springframework.boot.autoconfigure.AutoConfigurationMetadataLoader#loadMetadata(java.lang.ClassLoader)
org.springframework.boot.autoconfigure.AutoConfigurationMetadataLoader#PATH
META-INF/spring-autoconfigure-metadata.properties
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getCandidateConfigurations
org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories
org.springframework.core.io.support.SpringFactoriesLoader#FACTORIES_RESOURCE_LOCATION
META-INF/spring.factories
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#filter
@EnableAutoConfiguration
就是从 classpath 中搜寻 META-INF/spring.factories
配置文件,并将其中
org.springframework.boot.autoconfigure.EnableutoConfiguration
对应的配置项通过反射(Java
Refletion)实例化为对应的标注了 @Configuration
的 JavaConfig 形式的配置类,并加载到 IOC 容器中
实例分析:
-
以
mybatis-spring-boot-starter
为例,依赖mybatis-spring-boot-autoconfigure
,这个 jar 包内存在META-INF/spring.factories
,内容为:# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\ org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
自定义 Stater
- starter 是 SpringBoot 非常重要的一部分,可以理解为一个可拔插式的插件,正是这些 starter 使得使用某个功能的开发者不需要关注各种依赖库的处理,不需要具体的配置信息,由 Spring Boot 自动通过 classpath 路径下的类发现需要的 Bean ,并织入相应的 Bean 。
- SpringBoot 提供的 starter 以
spring-boot-starter-xxx
的方式命名的。官方建议自定义的 starter 使用
xxx-spring-boot-starter
命名规则。
-
POM
<?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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.lagou.starter</groupId> <artifactId>mystarter-spring-boot-starter</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> <version>2.2.2.RELEASE</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies> </project>
-
META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.lagou.starter.mystarter.MyAutoConfiguration
-
MyAutoConfiguration.java
@ConditionalOnMissingBean({MySimpleBean.class}) @Configuration public class MyAutoConfiguration { @Bean public MySimpleBean mySimpleBean() { return new MySimpleBean(); } }
-
POM,引用自定义 starter
<dependency> <groupId>com.lagou.starter</groupId> <artifactId>mystarter-spring-boot-starter</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
执行原理
分析启动代码:
public static void main(String[] args) {
SpringApplication.run(Springboot01DemoApplication.class, args);
}
org.springframework.boot.SpringApplication#run
如果 pom 中有以下依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
Main 线程被关闭,启动新的线程,基于监听器 RestartApplicationListener
org.springframework.boot.devtools.restart.Restarter#immediateRestart
SpringBoot 数据访问
-
SpringData 是 Spring 提供的一个用于简化数据库访问、支持云服务的开源框架。它是一个伞形项目,包含了大量关系型数据库及非关系型数据库的数据访问解决方案,其设计目的是使我们可以快速且简单地使用各种数据访问技术。
-
Spring Boot 默认采用整合 SpringData 的方式统一处理数据访问层,通过添加大量自动配置,引入各种数据访问模板
xxxTemplate
以及统一的Repository
接口,从而达到简化数据访问层的操作。 -
Spring Data 提供了多种类型数据库支持,对支持的的数据库进行了整合管理,提供了各种依赖启动器,接下来,通过一张表罗列提供的常见数据库依赖启动器
名称 描述 spring-boot-starter-data-jpa 使用 Spring Data JPA 与 Hibernate spring-boot-starter-data mongodb 使用 MongoDB 和 Spring Data MongoDB spring-boot-starter-data-neo4j 使用 Neo4j 图数据库和 Spring Data Neo4j spring-boot-starter-data-redis 使用 Redis 键值数据存储与 Spring Data Redis
Spring Boot 整合 MyBatis
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
@Mapper
注解表示该类是一个 MyBatis 接口文件,并保证能够被 Spring Boot 自动扫描到 Spring 容器中- 对应的接口类上添加了
@Mapper
注解,如果编写的 Mapper 接口过多时,需要重复为每一个接口文件添加@Mapper
注解 - 为了解决这种麻烦,可以直接在 Spring Boot 项目启动类上添加
@MapperScan("xxx")
注解,不需要再逐个添加@Mapper
注解,@MapperScan("xxx")
注解的作用和@Mapper
注解类似,但是它必须指定需要扫描的具体包名
@MapperScan
将扫描到的接口转换成 org.mybatis.spring.mapper.MapperFactoryBean
的过程:
-
org.mybatis.spring.annotation.MapperScan
注解引入了MapperScannerRegistrar
-
引入
MapperScannerConfigurer
,是一个BeanDefinitionRegistryPostProcessor
-
触发生命周期,
org.mybatis.spring.mapper.MapperScannerConfigurer#postProcessBeanDefinitionRegistry
org.mybatis.spring.mapper.ClassPathMapperScanner#doScan
org.mybatis.spring.mapper.ClassPathMapperScanner#processBeanDefinitions
mybatis-spring-boot-starter
自动配置类:
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
Spring Boot 整合 JPA
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
@Data
@Entity
@Table(name = "t_comment")
public class Comment {
@Id //表明映射主键id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id; //评论id
private String content; //评论内容
private String author; //评论作者
@Column(name = "a_id")
private Integer aId; //外键:表示当前这条评论是属于那篇文章
}
public interface CommentRepository extends JpaRepository<Comment, Integer> {
}
//测试整合JPA
@Autowired
private CommentRepository commentRepository;
@Test
public void selectComment() {
Optional<Comment> byId = commentRepository.findById(1);
if (byId.isPresent()) {
System.out.println(byId.get());
}
}
相关自动配置类:
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration
Spring Boot 整合 Redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
#redis服务器地址
spring.redis.host=127.0.0.1
#redis服务器连接端口
spring.redis.port=6379
#redis服务器连接密码
spring.redis.password=
@Data
@RedisHash(value = "persons") //指定实体类对象在redis中的存储空间
public class Person {
@Id // 用来标识实体类主键 字符串形式的hashkey标识唯一的实体类对象id
private String id;
@Indexed // 用来标识对应属性在redis中生成二级索引
private String firstname;
@Indexed
private String lastname;
private Address address;
}
public interface PersonRepository extends CrudRepository<Person,String> {
// 根据城市信息查询对应的人
List<Person> findByAddress_City(String name);
}
//测试整合redis
@Autowired
private PersonRepository personRepository;
@Test
public void savePerson(){
Person person = new Person();
person.setFirstname("张");
person.setLastname("三");
Address address = new Address();
address.setCity("北京");
address.setCountry("中国");
person.setAddress(address);
// 向redis数据库中添加了数据
personRepository.save(person);
}
@Test
public void selectPerson(){
List<Person> list = personRepository.findByAddress_City("北京");
for (Person person : list) {
System.out.println(person);
}
}
SpringBoot 视图技术
-
前端模板引擎技术的出现,使前端开发人员无需关注后端业务的具体实现,只关注自己页面的呈现效果即可,并且解决了前端代码错综复杂的问题、实现了前后端分离开发。Spring Boot框架对很多常用的模板引擎技术(如:FreeMarker、Thymeleaf、Mustache等)提供了整合支持
-
Spring Boot不太支持常用的JSP模板,并且没有提供对应的整合配置,这是因为使用嵌入式Servlet容器的Spring Boot应用程序对于JSP模板存在一些限制 :
- Spring Boot默认使用嵌入式Servlet容器以JAR包方式进行项目打包部署,这种JAR包方式不支持JSP模板。
- 如果使用Undertow嵌入式容器部署Spring Boot项目,也不支持JSP模板。
- Spring Boot默认提供了一个处理请求路径“/error”的统一错误处理器,返回具体的异常信息。使用JSP模板时,无法对默认的错误处理器进行覆盖,只能根据Spring Boot要求在指定位置定制错误页面。
Thymeleaf
略
SpringBoot 缓存管理
默认缓存管理
-
Spring 框架支持透明地向应用程序添加缓存对缓存进行管理,其管理缓存的核心是将缓存应用于操作数据的方法,从而减少操作数据的执行次数,同时不会对程序本身造成任何干扰。
-
Spring Boot 继承了 Spring 框架的缓存管理功能,通过使用
@EnableCaching
注解开启基于注解的缓存支持,Spring Boot 就可以启动缓存管理的自动化配置。 -
@EnableCaching
-
@Cacheable
属性名 说明 value/cacheNames 指定缓存空间的名称,必配属性。这两个属性二选一使用 key 指定缓存数据的key,默认使用方法参数值,可以使用SpEL表达式 keyGenerator 指定缓存数据的key的生成器,与key属性二选一使用 cacheManager 指定缓存管理器 cacheResolver 指定缓存解析器,与cacheManager属性二选一使用 condition 指定在符合某条件下,进行数据缓存 unless 指定在符合某条件下,不进行数据缓存 sync 指定是否使用异步缓存。默认false -
@CachePut
-
@CacheEvict
自定义 Redis 缓存序列化机制
自定义 JSON 格式的数据序列化方式进行数据缓存管理:
- 基于 API 的 Redis 缓存实现是使用
RedisTemplate
模板进行数据缓存操作的- 使用
RedisTemplate
手动控制,不推荐
- 使用
- 基于注解的 Redis 缓存需要自定义
RedisCacheManager
- 两者互不相关
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
// 创建JSON格式序列化对象,对缓存数据的key和value进行转换
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
// 解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
jackson2JsonRedisSerializer.setObjectMapper(om);
//设置redisTemplate模板API的序列化方式为json
template.setDefaultSerializer(jackson2JsonRedisSerializer);
return template;
}
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
// 分别创建String和JSON格式序列化对象,对缓存数据key和value进行转换
RedisSerializer<String> strSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
// 解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
jacksonSeial.setObjectMapper(om);
// 定制缓存数据序列化方式及时效
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofDays(1)).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(strSerializer)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jacksonSeial)).disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(config).build();
return cacheManager;
}
}