SpringBoot基本使用与简单原理分析

1、SpringBoot概述

SpringBoot是整合Spring技术栈的一站式框架
SpringBoot是简化Spring技术栈的快速开发脚手架

尚硅谷官方SpringBoot笔记

2、HelloWorld(开发->部署)

2.1Spring Initializr快速创建

2.2导入Web场景启动器

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

2.3编写Controller

@Controller
public class HelloController {

    @ResponseBody
    @GetMapping("/hello")
    public String hello(){
        return "Hello World";
    }

}

2.4运行主程序启动项目

//运行main方法
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
//小细节:run方法的返回值是一个ConfigurableApplicationContext,继承于ApplicationContext,所以,返回的就是IOC容器
        SpringApplication.run(Application.class, args);
    }
}

2.5测试

在这里插入图片描述

2.6打包

导入一个Maven插件(使用IDEA的快速初始化自带)

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

运行Maven命令将此项目直接打成jar包,

mvn clean
mvn package

在target目录下直接生成一个jar包
在这里插入图片描述

2.7运行jar包

java -jar直接运行

在这里插入图片描述

经过测试成功访问!!!!!

3、依赖管理

3.1导入依赖时无需指定版本号原因

就是基于Maven的继承,我们在导入依赖时无需指定版本号,因为SpringBoot的父pom文件已经指定了我们开发中要使用的基本上基本上所有依赖的版本号

<!--SpringBoot的父项目-->  
<parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>2.3.4.RELEASE</version>
       <relativePath/> 
   </parent>

<!--父项目的父项目 真正的用来管理所有依赖的版本号-->
 <parent>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-dependencies</artifactId>
   <version>2.3.4.RELEASE</version>
 </parent>

spring-boot-dependencies这个项目用来管理所有的依赖版本号

<properties>
    <activemq.version>5.15.13</activemq.version>
    <antlr2.version>2.7.7</antlr2.version>
    <appengine-sdk.version>1.9.82</appengine-sdk.version>
    <artemis.version>2.12.0</artemis.version>
    <aspectj.version>1.9.6</aspectj.version>
    <assertj.version>3.16.1</assertj.version>
    <atomikos.version>4.0.6</atomikos.version>
    <awaitility.version>4.0.3</awaitility.version>
      ..........
</properties>
	<dependencies>
      <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>log4j-over-slf4j</artifactId>
        <version>${slf4j.version}</version>
      </dependency>
      <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>${slf4j.version}</version>
      </dependency>
      <dependency>
    ..........
 </dependency>

3.2指定版本号

比如说某些版本号我们想要自己指定,我们在这个spring-boot-dependencies的依赖管理中看到MySQL的驱动是8版本,如果我们使用的MySQL版本是5版本,我们就要自己指定版本

两种方式(本质都是一种) Maven的就近原则

一、就是基于就近优先原则,在pom文件中导入依赖时手动指定版本号

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <!--手动指定版本-->
    <version>1.5.47</version>
</dependency>

二、使用properties的方式, Maven就近原则

因为父项目中使用的就是mysql.version 管理的依赖版本,所以可以在本pom文件中在properties标签中也写一个mysql.version 指定版本

<properties>
    <java.version>1.8</java.version>
    <mysql.version>5.1.47</mysql.version>
</properties>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

3.3starter场景启动器

一、Spring对于各种模块抽取了各种starter,比如我们上面要做web开发,直接导入了
spring-boot-starter-web这个starter,里面包含了web开发的所有相关依赖

二、Spring的starter的命名 spring-boot-starter=*
	我们自定义的要命名为 *-spring-boot-starter

三、所有场景启动器最底层的依赖
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter</artifactId>
  <version>xxx</version>
  <scope>compile</scope>
 </dependency>

3.4默认规则与初识自动配置

一、SpringBoot有默认的包扫描规则,我们写的类,必须在主启动类所在类的包或者子包中才能被扫描到,扫描到了指定的注解后自动的创建对象,当然也可以手动包扫描,但是一般不去手动指定
(约定大于配置)

二、SpringBoot做了很多的自动配置,并且为这些配置指定了默认值

三、只有引入了这个场景的场景启动器(starter)对应的自动配置才会生效

四、我们可以在properties或者yml配置文件中自定义配置

五、这些配置文件都是与对应的配置类进行绑定的,这个类在容器中创建对象

3.5自定义starter

码云地址:

https://gitee.com/starth/customer-starter.git

3.5.1自动配置原理

根据SpringBoot自动配置的原理,在项目启动的时候,主启动类上的@SpringBootApplication注解中的@EnableAutoConfiguration会扫描所有的依赖下的/META-INF/spring.factories这个配置文件,将这些配置类全部加载到IOC容器中,但是组件不一定生效,因为使用了@ConditionalXX注解判断是否导入了相关场景的依赖,

所以,根据这个原理,我们就可以自定义一个starter

3.5.2编写自定义starter

1.starter工程

starter工程,就是一个是普通的Maven工程 主要作用就是引入自动配置的依赖

这个就命名为shwshs-spring-boot-starter ,这个就是场景启动器, 注意:·要把pom文件中的maven插件删除

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.shwsh</groupId>
    <artifactId>shwshs-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
	
       <!--引入自定义自动配置依赖-->
    <dependencies>
        <dependency>
            <groupId>com.shwsh</groupId>
            <artifactId>shwsh-spring-boot-starter-autoconfigure</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>
	
             
</project>
2.自动配置工程

创建一个SpringBoot工程,主启动类测试包以及pom文件中测试的依赖全部删除,

按照一般的规则,我们也写一个组件,ShwshService

public class ShwshService {

    private String username;
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

定义一个配置文件绑定类,前缀配置成shwsh

@ConfigurationProperties(prefix = "shwsh")
public class ShwshTestProperties {

    private String username;
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

创建一个自动配置类,注入组件并开启配置类的绑定

@EnableConfigurationProperties(ShwshTestProperties.class)
@Configuration
public class ShwshTestAutoConfiguration {
	
    @Autowired
    ShwshTestProperties shwshTestProperties;
	
    //容器中没有时此组件才生效
    @ConditionalOnMissingBean(ShwshService.class)
    @Bean
    public ShwshService service(){
        ShwshService shwshService = new ShwshService();
        shwshService.setUsername(shwshTestProperties.getUsername());
        shwshService.setPassword(shwshTestProperties.getPassword());
        return shwshService;
    }
}

根据上面的原理,我们也在resources下创建一个META-INF文件夹,然后定义一个spring.factories文件,将我们此工程的自动配置类的全类名写上,这样主启动类就会扫描并加载此自动配置类

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.shwsh.aucon.config.ShwshTestAutoConfiguration

pom文件

注意:要将pom文件最后的maven插件删除,否则打包失败

<?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.3.12.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.shwsh</groupId>
    <artifactId>shwsh-spring-boot-starter-autoconfigure</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
    </dependencies>
</project>

3.5.3安装到本地Maven仓库

先安装自动配置包,在安装starter包

注意两个pom文件的maven插件必须进行删除,否则打包失败

3.5.4测试

@RestController
public class ShwshController {
	
    //直接自动注入自动配置类中的组件
   @Autowired
   ShwshService shwshService;


    @GetMapping("/shwsh")
    public String go(){
        String username = shwshService.getUsername();
        String password = shwshService.getPassword();
        return username+"__"+password;
    }
	
    //测试成功
}

替换组件

@Configuration
public class DataSourceConfig {
	
    //自定配置类使用了@ConditionalOnMissingBean,我们配置了,就用我们的,
    @Bean
    public ShwshService shwshService(){
        ShwshService shwshService = new ShwshService();
        shwshService.setUsername("xxxx");
        shwshService.setPassword("sdad");
        return shwshService;
    }
}

再次测试 发现使用的是我们自定义的,成功

3.5.5总结

核心就是编写自动配置的工程,要在META-INF/spring.factories中声明自动配置类,保证工程在启动时加载自动配置类,然后组件使用@Conditionalxx注解进行判断,以及使用配置文件绑定类进行赋值等

4、底层核心注解

参考Spring注解版

4.1@Configuration+@Bean

4.1.1基本使用

具体参考Spring注解版笔记

就是一个配置类,功能跟spring的配置文件相同,可以定义Bean

//类上标注一个@Configuration注解,表示这是一个配置类
@Configuration
public class ApplciationConfig {
    
    @Bean
    public User user(){
        return new User("张三",1);
    }
}

4.1.2原理及proxyBeanMethods属性

@Configuration(proxyBeanMethods = true/false)
  • proxyBeanMethods属性可以设置为true或者false,默认是true

  • 我们知道Spring默认的Bean都是单例的,即scope属性为singleton,我们自定义的配置类本身来说也是一个Bean,默认情况下Spring在创建配置类对象时,实际上底层使用了CGLIB动态代理,也就是说,我们的配置类在容器中实际上是其代理对象,这也就是proxyBeanMethods = true时的情况,所以我们不管是调用配置类的方法获取内部的bean也好或者是直接从容器中获取bean获取的都是相同的bean对象,因为调用@Bean方法是会判断如果是单例的直接就将容器中的返回

  • 但是,如果我们指定proxyBeanMethods = false,那么Spring在创建这个配置类对象时就不会使用CGLib动态代理,创建的就是此配置类的对象,并且在调用@Bean注解的方法时,每次都会创建新的对象但是容器中只有一个Bean(默认是单例) 适用于快速创建对象,没有依赖关系

4.1.3lite模式与full模式

 Full(proxyBeanMethods = true),保证每个@Bean方法被调用多少次返回的组件都是单实例的
 Lite(proxyBeanMethods = false) 每个@Bean方法被调用多少次返回的组件都是新创建的      

4.2@Component及其衍生注解

@Service @Controller @Repository等,只需在对应的类上标注对应的注解即可,SpringBoot的主程序会自动扫描

4.3@Import

作用:给容器中导入一些指定的组件,默认组件的名字就是全类名

自动调用无参构造器,创建对象

@Configuration(proxyBeanMethods = false)
@Import(User.class)
public class ApplciationConfig {

}

4.4@Conditional注解

按照条件进行操作,常用的有下面几个,用法都一样

注意:当标注在方法上时,Bean在配置类中的声明顺序会影响判断

  • @ConditionalOnBean 当容器中有某个Bean时 、、、、
  • @ConditionalOnMissingBean当容器中没有某个Bean时 、、、、
  • @ConditionalOnMissingClass() 当没有指定的类时 、、、、
  • @ConditionalOnClass() 当有指定的类时 、、、、
@Configuration
//标注在类上,当容器中有这个Bean时这个配置类中的所有Bean才会注入到容器中 
@ConditionalOnBean(name = "usd")
public class ApplciationConfig {


    @Bean("usd")
    public User user1(){
        return new User("张三",1);
    }
	
    //标注在方法上,当容器中有id是usd的bean时,将这个User对象注入到容器中,即执行这个方法
    @ConditionalOnBean(name = "usd")
    @Bean
    public User user(){
        return new User("张三",1);
    }

}

4.5@ImportResource

将Spring的XML配置文件中的Bean导入到容器中

@Configuration
@ImportResource("Spring配置文件的位置")
public class ApplciationConfig {

}

4.6@ConfigurationProperties

配置文件绑定

4.6.1@Component+@ConfigurationProperties

application.properties

mycar.id=1
mycar.name=okc

Java类

注意:要求此Java类必须在容器中才能使用配置绑定 且后缀跟属性名必须相同(如果是驼峰命名法,使用-替换也可以 比如asdDsd asd-dsd也可以注入) 必须有set方法

@Component
        //指定配置文件的配置前缀是mycar 跟其相同的属性名进行绑定
@ConfigurationProperties(prefix = "mycar")
public class Car {

    private Integer id;
    private String name;
    
      public void setId(Integer id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }
}

4.6.2@ConfigurationProperties+@EnableConfigurationProperties

//指定配置文件的前缀信息 根据相同属性名进行绑定
@ConfigurationProperties(prefix = "mycar")
public class Car {

    private Integer id;
    private String name;

    public void setId(Integer id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name= name;
    }
}

在配置类上或者主启动类上标注@EnableConfigurationProperties

@SpringBootApplication
/*
有两个功能,1、开启指定类与配置进行文件绑定 2、将Car这个组件注入到容器中
*/
@EnableConfigurationProperties(Car.class)
public class Application {
    public static void main(String[] args) {
        
      SpringApplication.run(Application.class, args);
        
    }
}

4.6.3@ConfigurationProperties+@Bean

也可以在@Bean方法上标注@ConfigurationProperties注解,只要属性名一致,就能绑定

@ConfigurationProperties(prefix = "user")
@Bean
public User user(){
    User user = new User();
    return user;
}
user.username=zhangsan
user.password=123456

经过测试,绑定成功

4.7@Value+@PropertySource

进行基本数据类型的注入

//必须也在容器中
@Component
//引入配置文件
@PropertySource("classpath:application.properties")
public class Car {
	
    //注入指定的key的value值
    @Value("${mycar.id}")
    private Integer id;

    @Value("${mycar.name}")
    private String name;

    @Override
    public String toString() {
        return "Car{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

5、自动配置原理及应用

5.1@SpringBootApplication

一个@SpringBootApplication等价于:是下面三个注解的合成注解
    ||
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.xx")
    

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan()
public @interface SpringBootApplication { }

5.1.1@SpringBootConfiguration

本质上就是@Configuration,即主启动类也是一个配置类

@Configuration
public @interface SpringBootConfiguration {
}

5.1.2@EnableAutoConfiguration(核心)

源码

由两个注解合成,一个是@Import,一个是@AutoConfigurationPackage

@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {

}

@Import({AutoConfigurationImportSelector.class})

  • 主要的作用:就是在容器启动时,就加载 spring-boot-autoconfigure包下spring.factories配置文件下的127个自动配置类
  • 注意:这些配置类最终都会按需加载,底层大量使用了@Conditional类型的注解,就是判断你是否导入了对应的依赖,只有你导入了某些依赖,对应的自动配置类才会生效
1、利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件
2、调用List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类
3、利用工厂加载 Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的组件
4、从META-INF/spring.factories位置来加载一个文件。
  默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件
---->主要扫描的就是这个jar包下的127个自动配置类
    spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories
文件里面写死了spring-boot一启动就要给容器中加载的所有配置类
spring-boot-autoconfigure-2.3.4.RELEASE.jar/META-INF/spring.factories

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,
............

@AutoConfigurationPackage注解

作用:扫描主程序类所在的包及其子包

-----------------@AutoConfigurationPackage注解---------------------------------
作用:使用导入的Registrar将指定的一个包下(主程序所在包及其子包)下的所有组件进行注册
    //此注解也是给容器中导入组件 导入一个Registrar组件 
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};
    Class<?>[] basePackageClasses() default {};
}

5.1.3@ComponentScan

进行包扫描的注解:详细见Spring注解版笔记

5.2自动配置的核心

1、在IOC容器启动的时候就会将所有的自动配置类xxxAutoConfiguration全部进行加载

2、按需加载:根据@Conditionalxx注解判断是否导入了一些依赖,如果导入了对应的依赖,那么其对应的自动配置类才会生效

3、每一个自动配置类一般都会绑定一个xxxProperties类,这个xxxProperties类绑定了SpringBoot的application.properties/yml配置文件,每一个配置类都对应自己的前缀,生效的配置类都会读取application.properties/yml中对应前缀的配置,然后设置到组件的属性中

4、自动配置类给容器中导入组件时,有些也会进行判断,如果说你自定义了这个类型的组件,那么我自动配置类给容器中就不导入了 使用了@Conditional注解判断,所有说,我们不想使用自动配置类中的组件时,就可以自己@Bean来自定义组件

举例

/*
1、开启ServerProperties类跟application.properties/yml配置文件进行绑定
2、将ServerProperties这个类导入到IOC容器中
*/
@EnableConfigurationProperties({ServerProperties.class})
@ConditionalOnClass({CharacterEncodingFilter.class})
public class HttpEncodingAutoConfiguration {
    
    @Bean
    //这个进行判断是否你自定义了CharacterEncodingFilter,如果你自定义了,就用你的,这个      就不起作用
    @ConditionalOnMissingBean
    public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
        //设置字符编码,从ServerProperties类中获取
filter.setEncoding(this.properties.getCharset().name());       
	   都是从ServerProperties中获取       filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.RESPONSE));
        return filter;
    }
        
            .....
}

------------------------ServerProperties-------------------------------------

 //跟application.properties/yml进行配置绑定 读取配置文件中的配置信息
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
	private Integer port;

	private InetAddress address;


	private final ErrorProperties error = new ErrorProperties();
    
    ........
    
}

5.3自动配置核心

经过上面两节的探究,总结自动配置,通俗的讲就是,你只要导入了相关场景的依赖,比如说你想要做缓存,导入了缓存的starter,那么SpringBoot的对应的缓存的自动配置类就会起作用,会将所有跟缓存相关的配置以及各种组件全部导入到容器中,直接拿来就可以用,如果想要修改一些配置,可以在application.properties/yml中配置缓存指定前缀的配置信息或者使用@Bean自定义组件

5.4最佳实践

1、引入场景依赖
     https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter
 
2、查看自动配置了哪些(选做)
   2.1自己分析,引入场景对应的自动配置一般都生效了
   2.2配置文件中debug=true开启自动配置报告。Negative(不生效)\Positive(生效)
 
3、是否需要修改
-->3.1参照文档修改配置项
  https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#common-application-properties
  
-->3.2自己分析。xxxxProperties绑定了配置文件的哪些。
  
-->3.3自定义加入或者替换组件
    @Bean、@Component。。。
    
-->3.4自定义器  XXXXXCustomizer;
 -......

6、实用工具

6.1lombok

//简化JavaBean开发
@NoArgsConstructor  //生成一个无参构造器
@AllArgsConstructor //生成一个全参构造器
@Data               //生成set和get方法
@ToString           //重写toString方法
@EqualsAndHashCode  //重写equals和HashCode方法
public class User {

    private String name;
    private Integer age;
   
}
================================简化日志开发===================================
//使用@Slf4j注解
@Slf4j
@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String handle01(@RequestParam("name") String name){
        
        //日志记录
        log.info/error/debug("请求进来了....");
        
        return "Hello, Spring Boot 2!"+"你好:"+name;
    }
}

6.2dev-tools

导入依赖,直接使用CTRL+F9直接自动,静态页面不重启

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>

7、yml配置文件的使用

7.1基本语法

  • key: value;kv之间有空格

  • 大小写敏感

  • 使用缩进表示层级关系

  • 缩进不允许使用tab,只允许空格

  • 缩进的空格数不重要,只要相同层级的元素左对齐即可

  • '#'表示注释

  • 字符串无需加引号

7.2基本使用

@Data
//跟application.peoperties/yml进行绑定 前缀为person
@ConfigurationProperties(prefix = "person")
public class Person {

    private String pname;
    private Integer page;
    private Double money;
    private Date birth;
    private Dog dog;
    private String[] strs;
    private List<String> liststr;
    private Set<Double> setd;
    private Map<String,Object> map;
    private Map<String,List<Dog>> mapdog;
}


@SpringBootApplication
//在主启动类上开启Person类的配置绑定 并将Person注入到容器中
@EnableConfigurationProperties(Person.class)
public class Application {
    public static void main(String[] args) {
         SpringApplication.run(Application.class, args);
    }
}
person:
  pname: 凯里欧文  ------------->  #简单数据类型 key和value的冒号后必须有空格
  page: 123
  money: 123.3
  birth: 2010/10/20 --------------->#日期类型 Spring默认可以转换的格式是 xxxx/xx/xx
  dog:   -------------------------->#自定义类型
    dname: 字母狗
    dage: 4
  strs: [张三三,李四四,王五五] -------------------------->#String数组类型
  liststr: -------------------------->#list集合类型 也可以写成数组的形式
    - list李六六
    - list赵七七
  setd: --------------------------->#Set集合 写法跟数组 list集合相同
    - 123.23
    - 123213.12312
  map: --------------------------->#Map<String,Object>的写法 
    kkk1: vvv1
    kkk2: vvv2
  mapdog: --------------------------->#Map<String,List<Dog> mapdog>的写法  
    mlk:
      - {dname: 字母}
      - {dname:,dage: 123}
    bkn:
      - {dname: 字母他爹}

7.3进行配置文件的提示

导入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

SpringBoot打包时排除此jar包

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <excludes>
                    <!--打包时排除此依赖-->
                    <exclude>
                        <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-configuration-processor</artifactId>
                    </exclude>
                </excludes>
            </configuration>
        </plugin>
    </plugins>
</build>

>>>>Web开发<<<<<<

笔记

参考

8、静态资源规则与使用

8.1静态资源的默认存放位置与访问路径

8.1.1默认存放位置

SpringBoot默认的静态资源的存放位置有四处

分别是类路径下的下面四个文件夹下,其中一般使用的都是static
/static 
/public
/resources
/META-INF/resources

如果存在了相同的文件名,优先级
/META-INF/resources > /resources > /static  > /resources

在这里插入图片描述

8.1.2默认的访问路径

  • 简单原理: 静态映射/**

    请求进来,先去找Controller看能不能处理。不能处理的所有请求又都交给静态资源处理器。静态资源也找不到则响应404页面

只要是在这四个文件夹下的静态资源,比如在static目录下有一个abc.txt,那么默认的访问路径就是 http://localhost:8080/abc.txt ,
如果这些目录下有子目录,比如static下有一个okc目录,okc目录下有一个abc.txt,那么访问路径就是http://lxxx:8080/okc/abc.txt

8.1.3自定义存放位置

/*上面四种默认的存放位置是声明在ResourceProperties这个类里定义的,这个类与application.properties/yml配置文件进行了绑定,*/
@ConfigurationProperties(
    prefix = "spring.resources",
    ignoreUnknownFields = false
)
public class ResourceProperties {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{                           "classpath:/META-INF/resources/",                                                "classpath:/resources/",
                       "classpath:/static/",                                                           "classpath:/public/"};
  //主要是这个属性,可以指定静态资源存放的位置,默认的有上面的四种,可以在配置文件中配置
  private String[] staticLocations; 

这个类在配置文件中的前缀是spring.resources,我们可以配置它的staticLocations属性,自定义我们的静态资源的存放位置,注意:我们自定义存放路径之后,默认的就不起作用

#此属性是一个数组,可以指定多个目录,
spring.resources.static-locations=[classpath:/kd/,....]

8.1.4自定义访问路径

通过8.1.2节我们知道,直接通过项目路径/静态资源名就可以访问静态资源,默认是没有前缀的,可以通过下面的设置给所有的静态资源指定一个访问前缀,默认是/**访问所有的

注意:只要指定了访问前缀,那么访问静态资源下的所有资源都必须加上指定的前缀

spring:
  mvc:
    static-path-pattern: /指定的前缀/**

8.2欢迎页

可以在静态资源路径写一个index.html(只能是这个命名),然后在访问项目时,不加任何路径直接就能访问,不能放在子目录下

注意:配置的静态资源访问前缀会导致index.html不能被默认方法

8.3自定义Favicon

跟欢迎页的规则一样,直接在静态资源的目录下放一个名为favicon.ico的图标,必须以favicon.ico命名,且不能放到子目录下,配置的静态资源访问前缀也会导致favicon.ico不能生效

正确配置之后,访问网站时就自动的生效

8.4静态资源与Webjars的访问原理(源码)

  • 静态资源的访问:直接获取到默认的静态资源目录或者是自定义的静态资源目录,请求进来如果controller处理不了,直接交给静态资源处理器处理,从各个静态资源目录下找文件,找到了直接返回资源
  • webjars的访问: webjars的规则 如果访问/webjars/** 下的请求 去类路径下的/METAINF/resources/webjars/ 注:webjars下的资源一般是前端框架如jq等搞成的maven依赖,方便使用,直接导入对应的依赖即可使用
@Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
      if (!this.resourceProperties.isAddMappings()) {
        logger.debug("Default resource handling disabled");
        return;
      }
      Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
      CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
      //webjars的规则 如果访问/webjars/** 下的请求 去/META-INF/resources/webjars/
      //去找
            if (!registry.hasMappingForPattern("/webjars/**")) {
        customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
            .addResourceLocations("classpath:/META-INF/resources/webjars/")
            .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
      }
     
===============静态资源的访问规则==============================================   
//看一下是否自定义了静态资源目录,如果自定义了,就使用自定义的,没有自定义就使用默认的,static META-INF/resources .... 
      String staticPathPattern = this.mvcProperties.getStaticPathPattern();
      if (!registry.hasMappingForPattern(staticPathPattern)) {  
          
      // 访问的静态资源直接从上面获取的静态资源目录下直接获取
       customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)    .addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
            .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
      }
    }      

8.5欢迎页访问原理(源码)

  • 流程:访问项目时不加任何路径,默认先去找Controller中是否有路径为/的请求映射,有就调用Controller的/请求,
  • 如果Controller中没有这个/请求,就去找静态资源下是否有index.html页面,有的话直接访问,
  • 如果静态资源下没有index.html,那么就直接跳转到非静态文件目录templates下的index.html(导入了thymeleaf的前提下),如果没有直接404
  HandlerMapping:处理器映射。保存了每一个Handler能处理哪些请求。  

    @Bean
    public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
	
    //核心源码在这里
      ApplicationContext applicationContext, Optional<Resource> welcomePage, String staticPathPattern) {
      
    if (welcomePage.isPresent() && "/**".equals(staticPathPattern)) {
 //要用欢迎页功能,必须是/** 只在静态资源路径下寻找
      logger.info("Adding welcome page: " + welcomePage.get());
      setRootViewName("forward:index.html");
    }
      
    else if (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) {
            // 直接跳转到templates目录下的index页面去
      logger.info("Adding welcome page template: index");
      setRootViewName("index");
    }
  }

9、请求处理及源码分析

9.1SpringBoot处理Rest风格的请求

9.1.1概述

  • 开发时表单只能发get 和 post请求,如果我们想要使用Rest风格发送 put请求或者是delete请求需要使用一个HiddenHttpMethodFilter过滤器,在表单中还是使用post请求,但是指定一个参数默认是_method ,值就是put或者delete,经过过滤器的包装之后就能转成指定的请求。
  • 如果是客户端直接就发送put delete请求,如Postman 安卓app等

SpringBoot的自动配置已经为我们在容器中注入了一个HiddenHttpMethodFilter,直接使用即可,前提是我们没有自定义HiddenHttpMethodFilter,如果我们自己写了,就用我们自己写的,就是这个注解@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)判断容器中有没有。

@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
   return new OrderedHiddenHttpMethodFilter();
}

9.1.2使用

表单写法

<form action="/user" method="post">
    <!--指定一个—_method属性 值就是要转的请求 put/delete-->
    <input name="_method" value="put" type="hidden">
    <button type="submit" value="put请求"></button>
</form>

controller

@ResponseBody
//指定为PutMapping或者DeleteMapping
@PutMapping("/user")
public String putUser(){
    return "PUT-张三";
}

配置文件中开启

spring:
  mvc:
    hiddenmethod:
      filter:
        enabled: true

9.1.3源码

将post请求转换为指定的put/post/patch请求源码流程

整个流程:

判断是否是post请求,经过过滤器拦截请求,判断是否携带了_method字段,然后获取_method的值put/post等,然后将原生的request包装为转换为指定请求的请求,然后放行执行后续流程

public class HiddenHttpMethodFilter extends OncePerRequestFilter {
    
    private static final List<String> ALLOWED_METHODS =
			Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(),
					HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));
    
    //传来的参数的名字默认是_method 
	public static final String DEFAULT_METHOD_PARAM = "_method";
	private String methodParam = DEFAULT_METHOD_PARAM;
	
    //自定义过滤器时可以修改这个参数名
    public void setMethodParam(String methodParam) {
        Assert.hasText(methodParam, "'methodParam' must not be empty");
        this.methodParam = methodParam;
    }
    
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
      throws ServletException, IOException {
	
    //传入原生request请求 
   HttpServletRequest requestToUse = request;
	
    //判断是否是post请求
   if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
       //获取到传来的_method参数
      String paramValue = request.getParameter(this.methodParam);
       //判断这个参数是否有值
      if (StringUtils.hasLength(paramValue)) {
          //不管你是大小写 都给你转成大写
         String method = paramValue.toUpperCase(Locale.ENGLISH);
          //判断是否是指定的put delete patch 是的话将原生的request请求包装为
          //指定请求方式的请求
         if (ALLOWED_METHODS.contains(method)) {
            requestToUse = new HttpMethodRequestWrapper(request, method);
         }
      }
   }
    //放行
   filterChain.doFilter(requestToUse, response);
 }   
}    

9.1.4自定义HiddenHttpMethodFilter

看了9.1.2的源码,如果我们想要自定义HiddenHttpMethodFilter,直接给容器中注入即可,然后SpringBoot自动配置的就不会生效,自定义了HiddenHttpMethodFilter还可以修改默认携带的参数名

@Configuration
public class MyHiddenMethodFilter {

    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
        //指定携带的参数名为_changeTo
        hiddenHttpMethodFilter.setMethodParam("_changeTo");
        return hiddenHttpMethodFilter;
    }
}

表单

<form action="/user" method="post">
    <!--上面定义了参数名为_changeTo 这里的参数必须也是_changeTo-->
    <input name="_changeTo" value="put" type="hidden">
    <button type="submit" value="put请求"></button>
</form>

9.2源码分析-各种参数的解析

参考

大致流程

根据参数判断是否有该类型的参数解析器HandlerMethodArgumentResolver,然后获取参数,

10、视图解析与模板引擎

10.1Thymeleaf基本语法

参考

10.2编码步骤及测试

1.导入thymeleaf场景启动器(starter)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

2.SpringBoot自动配置

thymeleaf的自动配置类

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(ThymeleafProperties.class)
@ConditionalOnClass({ TemplateMode.class, SpringTemplateEngine.class })
@AutoConfigureAfter({ WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class })
public class ThymeleafAutoConfiguration {

    xxx

}

主要配置了:

  • 1、视图解析器(ThymeleafViewResolver)
  • 2、模板引擎(SpringTemplateEngine)

这个配置类对应的配置是ThymeleafProperties,ThymeleafProperties与我们的application配置文件进行绑定,前缀是spring.thymeleaf

//ThymeleafProperties配置文件绑定类
@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {
	
	private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
	
    //视图解析器的默认的前缀就是/templates下,即我们的页面都放在这个下面
	public static final String DEFAULT_PREFIX = "classpath:/templates/";
	
    //都必须是html页面
	public static final String DEFAULT_SUFFIX = ".html";

所以说,我们的页面默认放在templates下,是html页面

3.页面导入命名空间

<!DOCTYPE html>
                <!--导入命名空间-->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
 <body>
     
 </body>
</html>

4.controller

@GetMapping("/user")
public String cu(){
    //如果是直接在templates目录下的,没在子目录下,直接就可以使用去掉前后缀的html页面名
    //访问/user请求,直接跳转到success页面
    return "success";
}

表单重复提交解决--->重定向

11、定制SpringMVC

11.1.拦截器

1.原理及概述

原理参考SpringMVC笔记

2.登录检查拦截器实现

实现Interceptor接口

public class LoginInterceptor implements HandlerInterceptor {
    
    /*
    *  目标方法执行之前
    * */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        Object loginUser = session.getAttribute("loginUser");
        if (loginUser!=null){
            return true;
        }
        response.sendRedirect("/");
        return false;
    }

    /*
    *  目标方法执行之后  页面跳转之前
    *
    * */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    /*
    * 页面跳转之后
    * */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

注册拦截器

@Configuration
public class MyWebConfig implements WebMvcConfigurer {


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //拦截所有请求 除了去登陆页
        registry.addInterceptor(new LoginInterceptor()).
            							 //拦截所有请求 默认静态资源也会拦截
                                         addPathPatterns("/**").
            							//配置不拦截静态资源与登录页面
excludePathPatterns("/css/**","/fonts/**","/images/**","/js/**").                                         excludePathPatterns("/","/toLogin","/login");

    }
}

3.拦截器中自动注入

一般我们开发拦截器时不会将拦截器注入到IOC容器中,但是如果我们需要在拦截器中进行注入一些组件,那么我们就必须将这个拦截器注入到容器中 使用@Component或者@Bean

实际场景:统计一个url被访问的次数

//将这个拦截器放到容器中
@Component
public class CountInterceptor implements HandlerInterceptor {
	
    //拦截器需要注入一个redisTemplate,那么这个拦截器必须放到容器中
    @Autowired
    StringRedisTemplate redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String uri = request.getRequestURI();
        redisTemplate.opsForValue().increment(uri);
        return true;
    }
}

拦截器的注册

@Configuration
public class MyWebConfig implements WebMvcConfigurer {
	
    //先将这个拦截器注入进来
    @Autowired
    CountInterceptor countInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
//这里不进行new 拦截器对象 使用容器中的配置的拦截器对象
  registry.addInterceptor(countInterceptor).addPathPatterns("/**").excludePathPatterns("/css/**","/fonts/**","/images/**","/js/**").
                excludePathPatterns("/","/toLogin","/login");

    }
}

11.2文件上传

11.2.1自动配置

基本流程跟SpringMVC一致,不同的是SpringBoot已经为我们自动配置了MultipartResolver,并且开启MultipartProperties这个类的配置文件绑定,我们可以在配置文件中配置上传的文件大小等属性

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, StandardServletMultipartResolver.class, MultipartConfigElement.class })
@ConditionalOnProperty(prefix = "spring.servlet.multipart", name = "enabled", matchIfMissing = true)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(MultipartProperties.class)
public class MultipartAutoConfiguration {

   private final MultipartProperties multipartProperties;

   public MultipartAutoConfiguration(MultipartProperties multipartProperties) {
      this.multipartProperties = multipartProperties;
   }

   @Bean
   @ConditionalOnMissingBean({ MultipartConfigElement.class, CommonsMultipartResolver.class })
   public MultipartConfigElement multipartConfigElement() {
      return this.multipartProperties.createMultipartConfig();
   }

   @Bean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
   @ConditionalOnMissingBean(MultipartResolver.class)
   public StandardServletMultipartResolver multipartResolver() {
      StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
      multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());
      return multipartResolver;
   }

}

跟配置文件绑定的类

@ConfigurationProperties(prefix = "spring.servlet.multipart", ignoreUnknownFields = false)
public class MultipartProperties {

   
   private boolean enabled = true;

   private String location;

    //单个文件大小
   private DataSize maxFileSize = DataSize.ofMegabytes(1);

	//一次请求上传的所有文件大小
   private DataSize maxRequestSize = DataSize.ofMegabytes(10);

   private DataSize fileSizeThreshold = DataSize.ofBytes(0);
		
    。。。

}

11.2.2编码

HTML代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<form  action="/upload" method="post" enctype="multipart/form-data">
  单个文件:用户头像    <input type="file" name="headimg">
    					<!--加上multiple 可以上传多个文件-->
  多个文件:一些照片    <input type="file" name="imgs" multiple>
    <button type="submit"></button>
</form>

</body>
</html>

controller代码

    @ResponseBody
    @PostMapping("/upload")
    public String getFile(
        				//使用专门处理的文件的注解 比@RequestParam更强
                          @RequestPart("headimg")MultipartFile headimg,
        					//多文件 用数组
                          @RequestPart("imgs") MultipartFile[] imgs) throws IOException {
        	//判断是否为null 然后保存
            if (!headimg.isEmpty()){
                headimg.transferTo(new File("D:\\Desk\\uploadfile\\"+headimg.getOriginalFilename()));
            }
            if (imgs.length != 0){
                for (MultipartFile img : imgs) {
                    img.transferTo(new File("D:\\Desk\\uploadfile\\"+img.getOriginalFilename()));
                }
            }

        return "ok";
    }

}

配置文件配置上传文件大小限制

# 经过11.2.1的分析 这个配置的前缀是spring.servlet.multipart
spring.servlet.multipart.max-file-size=1000
spring.servlet.multipart.max-request-size=10000

11.3异常处理

默认的规则

  • 默认情况下,Spring Boot提供/error处理所有错误的映射
  • 对于机器客户端,它将生成JSON响应,其中包含错误,HTTP状态和异常消息的详细信息。对于浏览器客户端,响应一个“ whitelabel”错误视图,以HTML格式呈现相同的数据

在这里插入图片描述

自定义错误页面

我们可以自定义出现4xx或者5xx时的页面 页面命名就是4xx.html 5xx.html,一般都是放在templates下的error文件夹下,这样出现4xx的异常时,自动显示4xx.html,出现5xx异常时,自动显示5xx.html

11.4全面接管SpringMVC

使用@EnableWebMvc注解,即可全面接管SpringMVC,即SpringBoot的关于MVC的自动配置类WebMvcAutoConfiguration不再生效,我们需要自己写配置。

@EnableWebMvc原理

这个注解的核心作用就是给容器中导入了一个DelegatingWebMvcConfiguration组件


@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

我们在来看一下WebMvcAutoConfiguration这个配置类生效的条件,其中有这样一个注解,意思就是如果容器中没有WebMvcConfigurationSupport时这个MVC的自动配置类才会生效,而@EnableWebMVC注解给容器中导入了一个DelegatingWebMvcConfiguration,点开DelegatingWebMvcConfiguration我们发现,他继承了WebMvcConfigurationSupport,所以经过判断容器中有WebMvcConfigurationSupport组件,所以说我们的MVC自动配置类就不生效

@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
public class WebMvcAutoConfiguration {
}   

11.5Web开发总结

如果我们想要自定义一些MVC的配置,我们可以实现WebMvcConfigurer接口,重写它的方法,可以定义拦截器 viewController 文件上传等。。

使用方式

@Configuration
public class MyWebConfig implements WebMvcConfigurer {

		。。。。
    
    
}

11.6SpringBoot开发套路总结

场景启动器starter-->xxxAutoConfiguration-->对应xxxProperties-->配置文件绑定

所以说,我们做SpringBoot开发对应的场景时,只需要导入对应的starter,自动配置类就会生效,如果对自动配置的组件不满意,我们可以自定义组件. 修改配置只需要点开对应的自动配置类,然后找到它对应的xxxProperties类,看一下可以配置的内容以及在application.p/y中的配置前缀进行配置即可

比如我们想要在配置文件中配置关于文件上传相关的一些配置信息,我们点开文件上传的自动配置类MultipartAutoConfiguration,然后看到它开启了MultipartProperties这个类跟配置文件绑定的功能,点开这个类发现前缀是"spring.servlet.multipart" ,所以说我们只需要在application.p/y里配置spring.servlet.multipart.xx即可修改配置信息

@EnableConfigurationProperties(MultipartProperties.class)
public class MultipartAutoConfiguration {
@ConfigurationProperties(prefix = "spring.servlet.multipart", ignoreUnknownFields = false)
public class MultipartProperties {

12、整合Dao

12.1整合jdbc

1.导入jdbc场景启动器+数据库驱动

jdbc的starter,主要包括了 事务+数据源HikarCP+jdbc

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>

starter中没有数据库驱动的依赖,因为SpringBoot不知道我们要使用什么数据库,所有数据库驱动需要我们自己手动导入

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

2.自动配置

主要为自动我们配置了

  • 数据源(DataSourceAutoConfiguration)
  • 事务(DataSourceTransactionManagerAutoConfiguration)
  • jdbcTemplate(后续不使用 使用mybatis)

数据源的自动配置

数据源的自动配置与之对应的配置就是DataSourceProperties这个类,绑定了application配置文件,

@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
		
}    

配置类的前缀是spring.datasource ,可以配置连接数据库的配置信息

@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
    
    //数据源的类型,默认是HikarCP
   	private Class<? extends DataSource> type;

	
    //数据库驱动名
	private String driverClassName;

	//url
	private String url;
	
    //用户名
	private String username;

	//密码
	private String password;
    
    。。。
}

3.配置数据库连接信息

spring:
  datasource:
    url: jdbc:mysql://ip:3306/lfymybatis?useSSL=false
    username: root
    password: 2741404507
    driver-class-name: com.mysql.jdbc.Driver

12.2整合Druid

12.2.1自定义方式

12.2.2starter场景启动器

1.导入starter
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.17</version>
</dependency>
2.编写配置文件

详细参考官方文档

spring:
  datasource:
    url: jdbc:mysql://ip:3306/lfymybatis?useSSL=false
    username: root
    password: 2741404507
    driver-class-name: com.mysql.jdbc.Driver

    druid:
      aop-patterns: com.atguigu.admin.*  #监控SpringBean
      filters: stat,wall     # 底层开启功能,stat(sql监控),wall(防火墙)
		
		#配置了这个 在项目中访问/druid请求即可进入监控页 
      stat-view-servlet: # 配置监控页功能
        enabled: true
        login-username: admin
        login-password: admin
        resetEnable: false

      web-stat-filter: # 监控web
        enabled: true
        urlPattern: /*
        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'


      filter:
        stat: # 对上面filters里面的stat的详细配置
          slow-sql-millis: 1000
          logSlowSql: true
          enabled: true
        wall:
          enabled: true
          config:
            drop-table-allow: false

12.3整合Mybatis

12.3.1回顾

Spring与Mybatis的整合,Spring为我们提供了两个类

  • SqlSessionFactoryBean 可以用于SqlSessionFactory的,并且可以扫描mapper.xml文件位置以及Mybatis主配置文件中可以配置的一些内容,比如别名,数据源等。。。
  • MapperScannerConfig 解决了创建SqlSession以及为我们创建Mapper(Dao)接口的实现类

我们在Spring的配置文件中配置这两个类,然后就可以在IOC容器中直接获取到dao接口的实现类,然后就可以调用方法实现增删改查

12.3.2SpringBoot整合Mybatis

1.引入mybatis的场景启动器

由于这个starter不是SpringBoot官方的starter,(官方的starter都是以spring-boot-starter-xx,第三方的starter都是以xx-spring-boot-starter) 所以说SpringBoot也就不会对此设置版本号管理,我们需要手动指定版本

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.4</version>
</dependency>

这个场景启动器为我们导入了三个依赖

  • 1、mybatis的依赖
  • 2、mybatis-spring的依赖
  • 3、spring-jdbc的依赖
2.分析Mybatis的自动配置

Mybatis的自动配置类叫做MybatisAutoConfiguration

里面自动配置了SqlSessionFactory等,并且开启了MybatisProperties这个类与application配置文件的绑定,在自动配置类里会读取MybatisProperties类中配置的一些信息,

@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@EnableConfigurationProperties({MybatisProperties.class})
public class MybatisAutoConfiguration implements InitializingBean {
   
}

配置文件绑定类

前缀是mybatis,主要配置的是mapperLocations,扫描mapperxml文件的位置

@ConfigurationProperties(prefix = "mybatis")
public class MybatisProperties {
    public static final String MYBATIS_PREFIX = "mybatis";
    private static final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
    private String configLocation;
    //扫描mapperxml文件的位置
    private String[] mapperLocations;
    private String typeAliasesPackage;
    private Class<?> typeAliasesSuperType;
    private String typeHandlersPackage;
    private boolean checkConfigLocation = false;
    private ExecutorType executorType;
    private Class<? extends LanguageDriver> defaultScriptingLanguageDriver;
    private Properties configurationProperties;
    
    //这个就是mybatis启动时封装的主配置文件信息以及mapperxml文件的信息
    //也可以在application配置文件中 通过mybatis.configuration.xx配置一些属性
    @NestedConfigurationProperty
    private Configuration configuration;
3.编写配置文件
#配置数据源
spring:
  datasource:
    url: jdbc:mysql://ip:3306/lfymybatis?useSSL=false
    username: root
    password: 2741404507
    driver-class-name: com.mysql.jdbc.Driver
    
mybatis:
	#扫描mapperxml文件的位置
  mapper-locations: classpath:mapper/*.xml
  	#配置一个驼峰命名法
  configuration:
    map-underscore-to-camel-case: true
4.编写dao接口和mapperxml文件
//dao接口可以使用@Mapper注解,也可以在某一个配置类上配置一个@MapperScan("dao接口所在包")
//作用就是扫描次注解然后为其生成代理对象
@Mapper
public interface UserDao {
    User getUser(Integer id);
}

对应的mapper文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.shwsh.boot.mapper.UserDao">
    <select id="getUser" resultType="com.shwsh.boot.entity.User">
            select * from user where id = #{id}
    </select>
</mapper>
5.测试
@SpringBootTest
class SpringBootWebApplicationTests {

    @Autowired
    UserDao userDao;

    @Test
    void contextLoads() {
        User user = userDao.getUser(1);
        System.out.println(user);
        //打印出数据,测试成功
    }
}

12.3.3整合总结

整合Mybatis,只需要导入Mybatis的场景启动器,然后编写配置文件配置数据源 mapper文件的位置等,然后在使用@MapperScan注解扫描dao接口位置即可。

12.4整合MybatisPlus

12.4.1编码步骤

1.导入statrer

由于这个starter也是第三方的,所以我们需要指定版本号。这个场景启动器里包括了mybatis的starter里的依赖,所以我们只需要引入一个mybatisplus的starter即可

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.0.5</version>
</dependency>
2.分析自动配置

MybatisPlus的自动配置类是MybatisPlusAutoConfiguration,对应的配置类MybatisPlusProperties前缀是mybatis.plus.xx mybatis的自动配置为我们配置的plus的自动配置都为我们配了,

@Configuration
//配置类MybatisPlusProperties
@EnableConfigurationProperties({MybatisPlusProperties.class})
public class MybatisPlusAutoConfiguration {
  
}

配置文件绑定类

//前缀mybatis-plus
@ConfigurationProperties(prefix = "mybatis-plus")
public class MybatisPlusProperties {
   
    //mybatis主配置文件
    private String configLocation;
    //mapperxml文件的位置
    private String[] mapperLocations;
    //别名包扫描
    private String typeAliasesPackage;
  
    private Properties configurationProperties;
    
    //这个就是主配置文件中的一些属性配置类,跟mybatis一样,可以设置驼峰命名等
    @NestedConfigurationProperty
    private MybatisConfiguration configuration; 
3.默认配置
  • MybatisPlusAutoConfiguration 配置类,MybatisPlusProperties 配置项绑定。mybatis-plus:xxx 就是对****mybatis-plus的定制

  • SqlSessionFactory 自动配置好。底层是容器中默认的数据源

  • **mapperLocations 自动配置好的。有默认值。***classpath*:/mapper/*/*.xml;任意包的类路径下的所有mapper文件夹下任意路径下的所有xml都是sql映射文件 建议mapper.xml文件都放在resources文件夹下的mapper文件夹下,所以我们可以不配置mapperLocations属性

  • @Mapper 标注的接口也会被自动扫描;建议直接 @MapperScan(“com.atguigu.admin.mapper”) 批量扫描就行

12.4.2常用注解

//当实体类中的某些字段不存在于数据库的表中时 可以使用此注解,使用exist=false指定
@TableField(exist=false)

//标注在主键属性上,可以指定数据库主键的生成策略
@TableId(IdType=IdType.xx)

//标注在实体类上,值写对应的数据库表名。表示此实体类对应的是数据库中的哪种表
@TableName("User")

12.4.3三个基类

我们使用MybatisPlus,dao接口 service接口 serviceImpl类 只需要分别实现MybatisPlus为我们提供的三个基本的类或者几口我们实现或者继承即可获得基本的增删改查功能

dao接口

//我们写的mapper(dao)接口只需要继承BaseMapper接口,泛型就是对应的实体类
@Mapper
public interface UserDao extends BaseMapper<User> {
    
}

BaseMapper源码

public interface BaseMapper<T> {

    /**
     * <p>
     * 插入一条记录
     * </p>
     *
     * @param entity 实体对象
     */
    int insert(T entity);
    
    。。。。

}

service接口

//我们写的Service接口直接继承MybatisPlus提供的IService接口即可,泛型也是要操作的实体类型
public interface UserService extends IService<User> {

    
}

IServie源码

public interface IService<T> {

    /**
     * <p>
     * 插入一条记录(选择字段,策略插入)
     * </p>
     *
     * @param entity 实体对象
     */
    boolean save(T entity);
}

ServiceImpl

//Service的实现类要继承MybatisPlus提供的ServiceImpl,两个泛型,一个是dao接口(内部要注入dao,调用基本的增删改查方法),一个是实体类,然后实现我们自己定义的UserService接口
@Service
public class UserServiceImpl extends ServiceImpl<UserDao, User> implements UserService {
    
}

ServiceImpl源码

/**
 * <p>
 * IService 实现类( 泛型:M 是 mapper 对象,T 是实体 , PK 是主键泛型 )
 * </p>
 *
 * @author hubin
 * @since 2018-06-23
 */
//传入的M泛型就是Dao接口的类型,因为要使用dao进行DB的访问,
@SuppressWarnings("unchecked")
public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {
	
    //将Mapper(dao)接口注入
    @Autowired
    protected M baseMapper;

13、整合Redis

具体Redis内容参考Redis笔记

1.导入starter

由artifactId可以看出这是官方的启动器,在父pom中管理了版本,所以无序指定版本

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>

2.分析自动配置

Redis的自动配置类就是RedisAutoConfiguration,配置类RedisProperties绑定application配置文件,前缀是spring.redis

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(RedisProperties.class)
//为容器导入了两个客户端
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
    
}

配置文件绑定类

//前缀是spring.redis
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
	
   
   private int database = 0;
   
    //连接url
   private String url;
	
    //主机名
   private String host = "localhost";
	
    //密码
   private String password;
	
    //端口
   private int port = 6379;

   private boolean ssl;
		
   。。。
}      

并且为我们抽取了两个操作Redis的模板

  • RedisTemplate 可以操作key和value是Object的
  • StringRedisTemplate 只能操作key和value都是String的

我们在项目中要使用注解@Autowired自动注入即可

3.可以切换为jedis

         <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <exclusions>
                <!--排除lettuce的依赖-->
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
	
		<!--手动导入jedis的依赖,SpringBoot做了版本管理 无需指定版本号-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

4.配置文件

#配置连接
spring:
  redis:
      host: xxx
      port: 6379
      。。。

5.测试

 @SpringBootTest
class SpringBootWebApplicationTests {


    @Autowired
    StringRedisTemplate redisTemplate;

    @Test
    void contextLoads() {

        ValueOperations<String, String> ops = redisTemplate.opsForValue();
        ops.set("username", "张三");
        String username = ops.get("username");
        System.out.println(username);
        //测试成功
    }
}

6.StringRedisTemplate

特点:key和value都是String类型的。

6.1key的相关操作

//查看所有的key
Set<String> keys = stringRedisTemplate.keys("*");

//查询指定key的value数据类型
DataType username = stringRedisTemplate.type("username");

//设置key的过期时间
Boolean pwd = stringRedisTemplate.expire("pwd", Duration.ofDays(1));

//获取key的过期时间 --->ttl    -1永不超时 -2key不存在 大于0表示过期时间
Long password = stringRedisTemplate.getExpire("password");

//判断key是否存在
Boolean hasKey = stringRedisTemplate.hasKey("username");

//获取stringRedisTemplate的key的序列化方式
RedisSerializer<?> keySerializer = stringRedisTemplate.getKeySerializer();

//获取stringRedisTemplate的value的序列化方式
RedisSerializer<?> valueSerializer = stringRedisTemplate.getValueSerializer();

//重命名key username--->user 要求这个key必须存在 不存在报错
stringRedisTemplate.rename("username", "user");

//随机获取一个key
String key = stringRedisTemplate.randomKey(); 

//将key移动的指定的库
Boolean username1 = stringRedisTemplate.move("username", 2);

6.2操作String

//调用opsForValue方法获取一个专门操作String的对象 
ValueOperations<String, String> operation = stringRedisTemplate.opsForValue();

//set方法
   operation.set("username", "张三");

//set方法 直接设置过期时间
  operation.set("pwd", "123456", 2, TimeUnit.DAYS);

//get方法 
   String username = operation.get("username");

//append 在指定key的value后追加
 stringOperation.append("username", "牛逼7777");

//不存在key不设置 存在设置key ----》present:存在的
  Boolean password = stringOperation.setIfPresent("password", "43");

//存在key设置 并设置过期时间
Boolean password2 = stringOperation.setIfPresent("password", "23", Duration.ofSeconds(20));

//如果不存在key设置key 存在key不设置key    absent:不存在
  Boolean password1 = stringOperation.setIfAbsent("password", "23");

//如果不存在key设置并设置过期时间 存在不设置
Boolean password3 = stringOperation.setIfAbsent("password", "23",Duration.ofSeconds(20));

//获取key的长度
 Long size = stringOperation.size("username")

//批量设置 批量查询。。。。

6.2操作List

//获取操作List列表数据的对象
      ListOperations<String, String> op =stringRedisTemplate.opsForList();

//添加数据 第一个是key 后面都是value
       listOperations.leftPushAll("username", "张三", "李四", "王五");


//添加数据 以集合的方式
        List<String> strs = new ArrayList<>();
        strs.add("赵四");
        strs.add("张飞");
        listOperations.leftPushAll("username", strs);

//遍历指定索引范围的数据 0 -1 表示遍历所有
        List<String> usernames = listOperations.range("username", 0, -1);
        usernames.forEach(System.out::println);

//从集合的最左边取出一个元素
        String username = listOperations.leftPop("username");

//将集合指定索引范围的数据截断 
        listOperations.trim("username", 1, 2);

//返回指定索引位置的元素 
        String username1 = listOperations.index("username", 2);

//如果key存在添加元素 不存在不添加
       listOperations.leftPushIfPresent("username", "kk");

//返回指定value在集合中第一次出现的位置索引 (redis需要6.0以上版本)
     Long index = listOperations.indexOf("username", "kk");
     

//返回指定value在集合中最后一次出现的位置索引  (redis需要6.0以上版本)
        Long aLong1 = listOperations.lastIndexOf("username", "kk");     

6.3操作Set

//获取操作set的对象
SetOperations<String, String> operations = stringRedisTemplate.opsForSet();

//添加一个set集合
operations.add("usernames", "张三","李素","张三","王五");

//遍历指定key的set集合 
Set<String> usernames = operations.members("usernames");

//判断是否包含指定元素
Boolean member = operations.isMember("usernames", "张三");

//查看指定key的元素个数
Long size = operations.size("usernames");


。。。交集 并集 

6.4操作ZSet

//获取操作Zset的对象
ZSetOperations<String, String> setOperations =stringRedisTemplate.opsForZSet();

//添加 单个添加
setOperations.add("username", "张姆斯", 22);


//构造一个set集合 添加
Set<ZSetOperations.TypedTuple<String>> tuples = new HashSet<>();
DefaultTypedTuple<String> t1 = new DefaultTypedTuple<String>("李四",4d);
DefaultTypedTuple<String> t2 = new DefaultTypedTuple<String>("杜兰特",42d);
tuples.add(t1);
tuples.add(t2);
setOperations.add("username", tuples);


//遍历指定范围元素  (自动已经排完序根据score)
Set<String> username = setOperations.range("username", 0, -1);

//获取指定value的score
Double score = setOperations.score("username", "张姆斯");

//获取元素个数
Long size = setOperations.size("username")

//遍历 并获取score
Set<ZSetOperations.TypedTuple<String>> username1 = setOperations.rangeByScoreWithScores("username", 2, 100);
username1.forEach(name->{
    System.out.println(name.getValue());
    System.out.println(name.getScore());
});

//删除指定元素
 setOperations.remove("username", "张姆斯");

//返回指定元素的排名
Long rank = setOperations.rank("username", "杜兰特");

6.5操作Hash

//获取一个操作hash的对象
HashOperations<String, Object, Object> hashOperations = stringRedisTemplate.opsForHash();


//创建一个hash类型的key并设置值
        hashOperations.put("user","name","张三");

//批量赋值 直接使用map
        Map<String,String> map = new HashMap<>();
        map.put("age", "12");
        map.put("gender", "男");
        map.put("height","1.9");
        hashOperations.putAll("user",map);
		
//判断一个key中是否包含innerkey
        Boolean hasKey = hashOperations.hasKey("user", "name");

//获取key的这个innerkey-value键值对
        Map<Object, Object> user = hashOperations.entries("user");

//删除指定的innerkey
        hashOperations.delete("user", "name","age");

//批量获取innerkey的value
 List<Object> values = hashOperations.multiGet("user", Arrays.asList("gender", "height"));

//获取所有的innerkey
        Set<Object> keys = hashOperations.keys("user");

//获取所有的value
        List<Object> user1 = hashOperations.values("user");

//获取键值对的个数
        Long size = hashOperations.size("user");

7.RedisTemplate

与StringRedisTemplate的APi一样,只是RedisTemplate可以操作Object类型的k-v,只是它的key的value的序列化方式都是JDK序列化方式,可以通过配置改变序列化方式

8.绑定API

当对一个key进行多次操作时,可以使用boundxx,然后可以根据返回值操作的就是这个key,不用在每次操作时都写key的名字,后续的API都是一样的,只是不用写key的名字而已

//boundValueOps就是操作String数据类型的 根据返回值进行后续操作
BoundValueOperations user = redisTemplate.boundValueOps("user");
user.set("zhangsna");
user.append("xxx");
...

有各种数据类型的绑定

在这里插入图片描述

9.配置文件及自定义配置

9.1默认的序列化方式

数据存到Redis时key和value都会被序列化,取的时候会先将key也序列化去redis中查,然后查询的结果进行反序列化转成存时的数据

  • StringRedisTemplate的key和value默认都是String的序列化方式
  • RedisTemplate的key和value默认都是JDK的序列化方式

设置key和value的序列化方式

//设置key的序列化方式 (Key一般使用String序列化)
redisTemplate.setKeySerializer(RedisSerializer rs);

//设置value的序列化方式 (Value一般使用JSON序列化)
redisTemplate.setValueSerializer(RedisSerializer rs);

//针对hash数据类型的innerkey的序列化方式 注意:Hash类型的innerkey要根据序列化方式决定
// (hash的innerkey一般使用String序列化)
redisTemplate.setHashKeySerializer(RedisSerializer rs);

//针对hash数据类型的innerkey对应的value的序列化方式
//  (hash的value一般使用JSON序列化)
redisTemplate.setHashValueSerializer(RedisSerializer rs);

可选的序列化方式

RedisSerializer是一个接口,下面有7中实现类,代表不同的序列化方式

//7种序列化方式
OxmSerializer
ByteArrayRedisSerializer
GenericJackson2JsonRedisSerializer  //JSON序列化方式
GenericToStringSerializer            
StringRedisSerializer          //String序列化方式 使用要求数据类型必须是String
JdkSerializationRedisSerializer     //JDK序列化方式
Jackson2JsonRedisSerializer         //JSON序列化方式

9.2简单配置自定义序列化方式

最简单的写法

直接将容器中的redisTemplate设置一下kv的序列化方式即可,然后放在容器中

@Configuration
public class RedisTemplateConfig {

   @Bean("hasSeriaRedisTemplate")
   RedisTemplate redisTemplate(RedisTemplate redisTemplate) {
   redisTemplate.setKeySerializer(new StringRedisSerializer());
   redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

   //下面都是针对hash数据类型的innerkey和value进行的序列化设置
       
   //修改的是innerkey的序列化方式
  redisTemplate.setHashKeySerializer(new StringRedisSerializer());

  //修改的是innerkey对应的value的序列化方式
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); 
        return redisTemplate;
    }

}

10.SpringCache

参考《谷粒商城上》笔记

11.Redis的应用 Mybatis的二级缓存等参考

参考

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shstart7

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值