20201209 Spring Boot - 拉勾教育

SpringBoot 基础回顾

  • Spring Boot 是所有基于 Spring 开发的项目的起点。Spring Boot 的设计是为了让你尽可能快的跑起来 Spring 应用程序并且尽可能减少你的配置文件。

  • 约定优于配置(Convention over Configuration),又称按约定编程,是一种软件设计范式

  • Spring Boot 在 Spring 的基础上做了两项改进:

    • 依赖管理
      • starter 约定
      • 依赖版本控制
    • 自动配置
  • Spring Initializr

单元测试

<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);
    }
}

热部署

  1. 增加依赖
<!-- 引入热部署依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>
  1. 勾选 File | Settings | Build, Execution, Deployment | Compiler | Build project automatically
  2. 快捷键 Ctrl+Shift+Alt+/ ,选择 Registry,勾选 compiler.automake.allow.when.app.running
  3. Ctrl+F9 触发 Build Project
  4. 重启生效

配置相关

@ConfigurationProperties 添加配置提示

  1. POM

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
    
  2. 使用注解 @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; //宠物
    }
    
  3. 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/key2key3 作为映射中的键。

@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.factoriesorg.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 命名规则。
  1. 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>
    
  2. META-INF/spring.factories

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    com.lagou.starter.mystarter.MyAutoConfiguration
    
  3. MyAutoConfiguration.java

    @ConditionalOnMissingBean({MySimpleBean.class})
    @Configuration
    public class MyAutoConfiguration {
        @Bean
        public MySimpleBean mySimpleBean() {
            return new MySimpleBean();
        }
    }
    
  4. 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;
    }


}

参考资料

posted @ 2020-12-09 21:00  流星<。)#)))≦  阅读(127)  评论(0编辑  收藏  举报