Spring Boot的概述

特性

  • 能够快速创建基于Spring的应用程序
  • 能够直接使用java main方法启动内嵌的Tomcat服务器运行SpringBoot程序,不需要部署war包文件
  • 提供约定的starter POM来简化Maven配置,让Maven的配置变得简单
  • 自动化配置,根据项目的Maven依赖配置,Springboot自动配置Spring、Springmvc等
  • 提供了程序的健康检查等功能
  • 基本可以完全不使用XML配置文件,采用注解配置

@面:什么是SpringBoot

SpringBoot是Spring开源组织下的子项目,是Spring组件一站式解决方案,主要是简化了使用Spring的难度,简省了繁重的配置,提供了各种启动器,开发者能快速上手。

四大核心

  • 自动配置
  • 起步依赖
  • Actuator
  • 命令行界面

@面:什么是 JavaConfig

Spring JavaConfig是Spring社区的产品,它提供了配置Spring IoC容器的纯Java方法。因此它有助于避免使用XML配置。
使用JavaConfig的优点在于:
1)面向对象的配置。由于配置被定义为JavaConfig中的类,因此用户可以充分利用Java中的面向对象功能。一个配置类可以继承另一个,重写它的@Bean方法等。
2)减少或消除XML配置。JavaConfig为开发人员提供了一种纯Java方法来配置与XML配置概念相似的Spring容器。
3)类型安全和重构友好。JavaConfig提供了一种类型安全的方法来配置Spring容器。由于Java5.0对泛型的支持,现在可以按类型而不是按名称检索bean,不需要任何强制转换或基于字符串的查找。

@面:理解SpringBoot配置加载顺序

1)properties文件;
2)YAML文件;
3)系统环境变量;
4)命令行参数;

@面:SpringBoot是否可以使用XML配置

SpringBoot推荐使用Java配置而非XML配置,但是SpringBoot中也可以使用XML配置,通过@ImportResource注解可以引入一个XML配置。

@面:springboot核心配置文件,bootstrap.properties和application.properties区别

bootstrap(.yml或者.properties):boostrap由父ApplicationContext加载的,比applicaton优先加载,配置在应用程序上下文的引导阶段生效。一般来说我们在SpringCloudConfig中会用到它,且boostrap里面的属性不能被覆盖。

application(.yml或者.properties):由ApplicatonContext加载,用于springboot项目的自动化配置。

@面:什么是SpringProfiles

SpringProfiles允许用户根据配置文件(dev,test,prod等)来注册bean。
因此,当应用程序在开发中运行时,只有某些bean可以加载,而在生产中,某些其他 bean可以加载。
这可以使用配置文件来完成。SpringBoot使得使用配置文件非常简单。

@面:自定义端口上运行SpringBoot

可以在application.properties中指定端口。
server.port = 8090

@面:实现SpringBoot应用程序的安全性

为了实现SpringBoot的安全性,我们使用spring-boot-starter-security依赖项,并且必须添加安全配置。它只需要很少的代码。
配置类将必须扩展WebSecurityConfigurerAdapter并覆盖其方法。

@面:什么是CSRF攻击

CSRF代表跨站请求伪造。这是一种攻击,迫使最终用户在当前通过身份验证的Web应用程序上执行不需要的操作。CSRF攻击专门针对状态改变请求,而不是数据窃取,因为攻击者无法查看对伪造请求的响应。

@面:SpringBoot中的监视器是什么

Springboot actuator是spring启动框架中的重要功能之一。Springboot监视器可帮助您访问生产环境中正在运行的应用程序的当前状态。有几个指标必须在生产环境中进行检查和监控。即使一些外部应用程序可能正在使用这些服务来向相关人员触发警报消息。监视器模块公开了一组可直接作为HTTP URL访问的REST端点来检查状态。

@面:什么是WebSockets

WebSocket是一种计算机通信协议,通过单个TCP连接提供全双工通信信道。
1、WebSocket 是双向的-使用WebSocket客户端或服务器可以发起消息发送。
2、WebSocket 是全双工的-客户端和服务器通信是相互独立的。
3、单个TCP连接-初始连接使用HTTP,然后将此连接升级到基于套接字的连接。然后这个单一连接用于所有未来的通信
4、与http相比,WebSocket消息数据交换要轻得多。

@面:什么是 SpringData

Spring Data是Spring的一个子项目。用于简化数据库访问,支持NoSQL和关系数据存储。其主要目标是使数据库的访问变得方便快捷。SpringData具有如下特点:

SpringData项目支持NoSQL存储:
1.MongoDB (文档数据库)
2.Neo4j(图形数据库)
3.Redis(键/值存储)
4.Hbase(列族数据库)

SpringData项目所支持的关系数据存储技术:
1.JDBC
2.JPA

@面:重新加载 SpringBoot 更改,无需重新启动服务器,SpringBoot项目热部署

这可以使用DEV工具来实现。通过这种依赖关系,可以节省任何更改,嵌入式tomcat将重新启动。
SpringBoot有一个开发工具(DevTools)模块,它有助于提高开发人员的生产力。
Java开发人员面临的一个主要挑战是将文件更改自动部署到服务器并自动重启服务器。开发人员可以重新加载SpringBoot上的更改,而无需重新启动服务器。这将消除每次手动部署更改的需要。

@面:spring-boot-starter-parent作用

1.定义了Java编译版本为 1.8 。
2.使用UTF-8格式编码。
3.继承自spring-boot-dependencies,这个里边定义了依赖的版本,也正是因为继承了这个依赖,所以我们在写依赖时才不需要写版本号。
4.执行打包操作的配置。
5.自动化的资源过滤。
6.自动化的插件配置。
7.针对application.properties和application.yml的资源过滤,包括通过profile定义的不同环境的配置文件,例如application-dev.properties和application-dev.yml。

@面:SpringBoot打成的jar和普通的jar区别

SpringBoot项目最终打包成的jar是可执行jar,这种jar可以直接通过java-jar xxx.jar命令来运行,这种jar不可以作为普通的jar被其他项目依赖,即使依赖了也无法使用其中的类。

SpringBoot的jar无法被其他项目依赖,主要还是他和普通jar的结构不同。普通的jar包,解压后直接就是包名,包里就是我们的代码,而SpringBoot打包成的可执行jar解压后,在\BOOT-INF\classes目录下才是我们的代码,因此无法被直接引用。如果非要引用,可以在pom.xml文件中增加配置,将SpringBoot项目打包成两个jar,一个可执行,一个可引用。

@面:运行SpringBoot方式

1)打包用命令或者放到容器中运行
2)用Maven/Gradle插件运行
3)直接执行main方法运行

@面:开启SpringBoot特性的方式

1)继承spring-boot-starter-parent项目
2)导入spring-boot-dependencies项目依赖

@面:SpringBoot 实现分页和排序

使用Spring Data-JPA可以实现将可分页的传递给存储库方法。

@面:微服务中实现 session 共享

常见的方案就是Spring Session + Redis来实现session共享。将所有微服务的 session统一保存在Redis上,当各个微服务对session有相关的读写操作时,都去操作Redis上的session。这样就实现了session共享,SpringSession基于Spring中的代理过滤器实现,使得session的同步操作对开发人员而言是透明的,非常简便。

入门案例

项目结构

static:存放静态资源,如图片、CSS、JavaScript 等
templates:存放 Web页面的模板文件
application.properties/application.yml:用于存放程序的各种依赖模块的配置信息,比如服务端口,数据库连接配置等

POM文件

<!--springboot父工程GAV-->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.5.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<!--当前项目GAV-->
<groupId>com.bjpowernode.springboot</groupId>
	<artifactId>002-springboot-springmvc</artifactId>
<version>1.0.0</version>

<properties>
    <java.version>1.8</java.version>
</properties>

<!--依赖-->
<dependencies>
    <!--SpringBoot框架web项目起步依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!--SpringBoot框架测试起步依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

<build>
    <plugins>
        <!--SpringBoot项目编译打包的插件-->
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

自动装配原理

1、新创建的类一定要位于 Application 同级目录或者下级目录,否则 SpringBoot 加载不到。

2、@SpringBootApplication注解是 Spring Boot 项目的核心注解,主要作用是开启Spring 自动配置,如果在 Application 类上去掉该注解,那么不会启动 SpringBoot程序

//标注这个类是一个springboot的应用
@SpringBootApplication
public class SpringbootStudyApplication {
   

    public static void main(String[] args) {
   
        //将springboot应用启动
        SpringApplication.run(SpringbootStudyApplication.class, args);
    }
}

SpringBootApplication分析:

@SpringBootApplication
	@SpringBootConfiguration
		@Configuration
			@Component
	@EnableAutoConfiguration
		@AutoConfigurationPackage
			@Import(AutoConfigurationPackages.Registrar.class)
		@Import(AutoConfigurationImportSelector.class)
@SpringBootConfiguration	
//springboot的配置类

@Configuration 	 
//spring配置类(配置类就是对应Spring的xml配置文件)
        	
@Component	
//说明这也是一个spring的组件(说明启动类本身也是Spring中的一个组件而已,负责启动应用)
	
@EnableAutoConfiguration	
//自动配置:开启自动配置功能,SpringBoot帮我们配置。这个@EnableAutoConfiguration注解通过@SpringBootApplication被间接的标记在了Spring Boot的启动类上。在SpringApplication.run(…)的内部就会执行selectImports()方法,找到所有JavaConfig自动配置类的全限定名对应的class,然后将所有自动配置类加载到Spring容器中。

@AutoConfigurationPackage 	
//自动配置包
			
@Import(AutoConfigurationPackages.Registrar.class)	
//自动注册包。Spring底层注解@import作用是给容器中导入一个组件。Registrar.class的作用是将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器

@Import(AutoConfigurationImportSelector.class)	
//自动配置导入选择器,给容器导入组件。
1、而这个注解也是一个派生注解,其中的关键功能由@Import提供,其导入的AutoConfigurationImportSelectorselectImports()方法通过SpringFactoriesLoader.loadFactoryNames()扫描所有具有META-INF/spring.factories的jar包。spring-boot-autoconfigure-x.x.x.x.jar里就有一个这样的spring.factories文件。
2AutoConfigurationImportSelector:自动配置导入选择器,那么它会导入哪些组件的选择器呢?类中的getCandidateConfigurations方法调用了SpringFactoriesLoader类的静态方法loadFactoryNames() 
3、继续点击查看loadSpringFactories方法,发现一个多次出现的文件spring.factories,文件里面写死了springboot一启动就要给容器中加载的所有配置类,这就是自动配置根源所在,文件也是一组一组的key=value的形式,其中一个key是EnableAutoConfiguration类的全类名,而它的value是一个xxxxAutoConfiguration的类名的列表,将所有自动配置类加载到Spring容器中
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//获取所有的配置
getCandidateConfigurations方法:
    
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
   
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());
    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.");
    return configurations;
}

@面:SpringBoot的核心注解,由哪几个注解组成

启动类上面的注解是@SpringBootApplication,它也是SpringBoot的核心注解,主要组合包含了以下3个注解:
1)@SpringBootConfiguration:组合了@Configuration注解,实现配置文件功能。
2)@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项。
如关闭数据源自动配置功能:
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。
3)@ComponentScan:Spring组件扫描。

3、结论

springboot所有的自动配置都是在启动的时候扫描并加载,扫描了spring.properties配置文件,所有的自动配置类都在这里面,但是不一定生效,因为要判断条件是否成立,只要导入了对应的start,就有对应的启动器,有了启动器我们自动装配就会生效,然后就配置成功

步骤:
1)SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
2)将这些值作为自动配置类导入容器,自动配置类就生效,帮我们进行自动配置工作;
3)以前需要我们配置的文件,springboot帮我们配置了!
4)整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
5)它会给容器中导入非常多的自动配置类(xxxAutoConfiguration)@Bean, 就是给容器中导入这个场景需要的所有组件,并配置好这些组件@Configuration;
6)有了自动配置类,免去了我们手动编写配置注入功能组件等的工作;

@面:SpringBoot自动配置原理

注解@EnableAutoConfiguration,@Configuration,@ConditionalOnClass就是自动配置的核心,
@EnableAutoConfiguration给容器导入META-INF/spring.factories里定义的自动配置类。
筛选有效的自动配置类。
每一个自动配置类结合对应的xxxProperties.java读取配置文件进行自动配置功能

4、SpringApplication.run分析

分析该方法主要分两部分,一是SpringApplication的实例化,二是run方法的执行;
SpringApplication这个类主要做了以下四件事情:
1、推断应用的类型是普通的项目还是Web项目
2、查找并加载所有可用初始化器 , 设置到initializers属性中
3、找出所有的应用程序***,设置到listeners属性中
4、推断并设置main方法的定义类,找到运行的主类

public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
   
    // ......
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.setInitializers(this.getSpringFactoriesInstances();
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = this.deduceMainApplicationClass();
}

5、HttpEncodingAutoConfiguration例子

//表示这是一个配置类,和以前编写的配置文件一样,也可以给容器中添加组件;
@Configuration 

//启动指定类的ConfigurationProperties功能;
	//进入这个HttpProperties查看,将配置文件中对应的值和HttpProperties绑定起来;
	//并把HttpProperties加入到ioc容器中
@EnableConfigurationProperties({
   HttpProperties.class}) 

//Spring底层@Conditional注解
  //根据不同的条件判断,如果满足指定的条件,整个配置类里面的配置就会生效;
  //这里的意思就是判断当前应用是否是web应用,如果是,当前配置类生效
@ConditionalOnWebApplication(type = Type.SERVLET)

//判断当前项目有没有这个类CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器;
@ConditionalOnClass({
   CharacterEncodingFilter.class})

//判断配置文件中是否存在某个配置:spring.http.encoding.enabled;
  //如果不存在,判断也是成立的
  //即使我们配置文件中不配置spring.http.encoding.enabled=true,也是默认生效的;
@ConditionalOnProperty(
    prefix = "spring.http.encoding",
    value = {
   "enabled"},
    matchIfMissing = true
)

public class HttpEncodingAutoConfiguration {
   
    //它已经和SpringBoot的配置文件映射了
    private final Encoding properties;
    //只有一个有参构造器的情况下,参数的值就会从容器中拿
    public HttpEncodingAutoConfiguration(HttpProperties properties) {
   
        this.properties = properties.getEncoding();
    }

    //给容器中添加一个组件,这个组件的某些值需要从properties中获取
    @Bean
    @ConditionalOnMissingBean //判断容器没有这个组件?
    public CharacterEncodingFilter characterEncodingFilter() {
   
        CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
        filter.setEncoding(this.properties.getCharset().name());
       	filter.setForceRequestEncoding(
           this.properties.shouldForce(
           org.springframework.boot.autoconfigure. http.HttpProperties.Encoding.Type.REQUEST));
        filter.setForceResponseEncoding(
            this.properties.shouldForce(
                org.springframework.boot.autoconfigure. http.HttpProperties.Encoding.Type.RESPONSE));
        return filter;
    }
    //。。。。。。。
}
一句话总结 :根据当前不同的条件判断,决定这个配置类是否生效!

一但这个配置类生效;这个配置类就会给容器中添加各种组件;
这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的;这样就可以形成我们的配置文件可以动态的修改springboot的内容。
所有在配置文件中能配置的属性都是在xxxxProperties类中封装着;
配置文件能配置什么就可以参照某个功能对应的这个属性类

配置文件

1、SpringBoot 的核心配置文件用于配置SpringBoot程序,名字必须以application开始

当两种格式配置文件同时存在,使用的是.properties 配置文件。

1)properties文件

#设置内嵌Tomcat端口号
server.port=9090
#配置项目上下文根
server.servlet.context-path=/

2).yml 文件

#字面量:单个的、不可再分的值。date、boolean、string、number、null
k: v
注意:
双引号,不会转义字符串里面的特殊字符 , 特殊字符会作为本身想表示的意思
比如:name: “kuang \n shen” 输出 :kuang 换行 shen
单引号,会转义特殊字符 , 特殊字符最终会变成和普通字符一样输出
比如:name: ‘kuang \n shen’ 输出 :kuang \n shen

#对象:键值对的集合。map、hash、set、object 
行内写法: k: {
   k1:v1,k2:v2,k3:v3}
#或
k: 
  k1: v1
  k2: v2
  k3: v3
  
#数组:一组按次序排列的值。array、list、queue
行内写法: k: [v1,v2,v3]
#或者
k:
 - v1
 - v2
 - v3
示例:
@Data
public class Person {
   
    
    private String userName;
    private Boolean boss;
    private Date birth;
    private Integer age;
    private Pet pet;
    private String[] interests;
    private List<String> animal;
    private Map<String, Object> score;
    private Set<Double> salarys;
    private Map<String, List<Pet>> allPets;
}

@Data
public class Pet {
   
    private String name;
    private Double weight;
}
# yaml表示以上对象
person:
  userName: zhangsan
  boss: false
  birth: 2019/12/12 20:12:33
  age: 18
  pet: 
    name: tomcat
    weight: 23.4
  interests: [篮球,游泳]
  animal: 
    - jerry
    - mario
  score:
    english: 
      first: 30
      second: 40
      third: 50
    math: [131,140,148]
    chinese: {
   first: 128,second: 136}
  salarys: [3999,4999.98,5999.99]
  allPets:
    sick:
      - {
   name: tom}
      - {
   name: jerry,weight: 47}
    health: [{
   name: mario,weight: 47}]

2、配置文件加载方式

1@configurationProperties:默认从全局配置文件中获取值;

2)加载指定配置文件用@PropertySource
@PropertySource(value = "classpath:person.properties")
@Component //注册bean
public class Person {
   
    @Value("${name}")//有点像jsp了哦,EL表达式
    private String name;
    ......  
}

3、Properties和Yaml的对比

1、@ConfigurationProperties只需要写一次即可,@Value则需要每个字段都添加
2、松散绑定:比如我的yaml中写的last-name,这个和lastName是一样的, - 后面跟着的字母默认是大写的。这就是松散绑定。
3、JSR303数据校验,这个就是我们可以在字段是增加一层过滤器验证,可以保证数据的合法性
4、复杂类型封装,yaml中可以封装对象,使用value就不支持。
springboot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件:

优先级1:项目路径下的config文件夹配置文件
优先级2:项目路径下配置文件
优先级3:资源路径下的config文件夹配置文件
优先级4:资源路径下配置文件

@面:YAML 配置的优势

1.配置有序,在一些特殊的场景下,配置有序很关键
2.支持数组,数组中的元素可以是基本数据类型也可以是对象
3.简洁

相比properties配置文件,YAML还有一个缺点,就是不支持@PropertySource注解导入自定义的YAML配置。

多环境配置

为每个环境创建一个配置文件,命名必须以 application-环境标识.properties|yml

在总配置文件 application.properties 进行环境的激活

#SpringBoot 的总配置文件
#激活开发环境
#spring.profiles.active=dev
#激活测试环境
#spring.profiles.active=test
#激活生产环境
spring.profiles.active=product

#springboot 总配置文件
#激活开发环境
spring:
	profiles:
		active: dev
yaml的多文档块

server:
  port: 8081
#选择要激活那个环境块
spring:
  profiles:
    active: prod
---
server:
  port: 8083
spring:
  profiles: dev #配置环境的名称
---
server:
  port: 8084
spring:
  profiles: prod  #配置环境的名称

自定义配置

1、@Value 注解

用于逐个读取application.properties 中的配置

在 SpringBootController 中定义属性,并使用@Value 注解或者自定义配置值。

school.name=bjpowernode
websit=http://www.bjpowernode.com
@Controller
public class SpringBootController {
    
    @Value("${school.name}")
    private String schoolName;
    
    @Value("${websit}")  
    private String websit; 
    
    @RequestMapping(value = "/springBoot/say")
    public @ResponseBody String say() {
   
        return schoolName + "------" + websit;
    }
}

2、@ConfigurationProperties

将整个文件映射成一个对象,用于自定义配置项比较多的情况

school.name=bjpowernode
school.websit=http://www.bjpowernode.com
@Component//将此类交给spring容器进行管理
@ConfigurationProperties(prefix = "school")
public class School {
   
    private String name;
    private String websit;

    public String getName() {
   
        return name;
    }
    public void setName(String name) {
   
        this.name = name;
    }
    public String getWebsit() {
   
        return websit;
    }
    public void setWebsit(String websit) {
   
        this.websit = websit;
    }
}

警告解决

使用了 ConfigurationProperties 注解后,IDEA 会出现一个警告,不影响程序的执行

<!--解决使用@ConfigurationProperties注解出现警告问题-->
<dependency>
 	<groupId>org.springframework.boot</groupId>
 	<artifactId>spring-boot-configuration-processor</artifactId>
	<optional>true</optional>
</dependency>

中文乱码

前端使用JSP

1、pom

<dependencies>
    <!--SpringBoot框架web项目起步依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!--引入SpringBoot内嵌Tomcat对jsp的解析依赖,不添加解析不了jsp-->
    <!--仅仅只是展示jsp页面,只添加以下一个依赖-->
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
    </dependency>
</dependencies>

<build>
    <!-- Springboot项目默认推荐使用的前端引擎是thymeleaf 现在我们要使用springboot集成jsp,手动指定jsp最后编译的路径 而且springboot集成jsp编译jsp的路径是springboot规定好的位置 META-INF/resources -->
    <resources>
        <resource>
            <!--源文件夹-->
            <directory>src/main/webapp</directory>
            <!--指定编译到META-INF/resources-->
            <targetPath>META-INF/resources</targetPath>
            <!--指定源文件夹中的哪个资源要编译进行-->
            <includes>
                <include>*.*</include>
            </includes>
        </resource>
    </resources>

    <plugins>
        <!--SpringBoot项目编译打包的插件-->
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

2、application.properties 文件配置SpringMVC的视图展示为jsp

#配置 SpringMVC 视图解析器
#其中:/表示目录为src/main/webapp
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp

3、在 src/main下创建一个webapp目录,然后在该目录下新建index.jsp 页面

如果在webapp目录下右键,没有创建jsp的选项,可以在Project Structure中指定webapp为 Web Resource Directory

JSR303

JSR303数据校验是用来校验输入内容的。
Springboot中可以用@validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。我们这里来写个注解让我们的name只能支持Email格式

@Component //注册bean
@ConfigurationProperties(prefix = "person")
@Validated  //数据校验
public class Person {
   
    @Email(message="邮箱格式错误") //name必须是邮箱格式
    private String name;
}
@NotNull(message="名字不能为空")
private String userName;
@Max(value=120,message="年龄最大不能查过120")
private int age;
@Email(message="邮箱格式错误")
private String email;

空检查
@Null       验证对象是否为null
@NotNull    验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank   检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty   检查约束元素是否为NULL或者是EMPTY. Booelan检查
@AssertTrue     验证 Boolean 对象是否为 true  
@AssertFalse    验证 Boolean 对象是否为 false  
    
长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内  
@Length(min=, max=) string is between min and max included.

日期检查
@Past       验证 DateCalendar 对象是否在当前时间之前  
@Future     验证 DateCalendar 对象是否在当前时间之后  
@Pattern    验证 String 对象是否符合正则表达式的规则

Web开发

集成 MyBatis

1、思路:通过 SpringBoot +MyBatis 实现对数据库学生表的查询操作

2、实现步骤:

1)在 pom.xml 中添加相关 jar 依赖

<!--MySQL驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <!--<version>5.1.9</version>-->
</dependency>

<!--MyBatis整合SpringBoot框架的起步依赖-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.0.0</version>
</dependency>

2)核心配置文件 application.properties 中配置数据源

3)开发代码

在 Mybatis 反向工程生成的 StudentMapper 接口上加一个 Mapper 注解。

@Mapper 作用:mybatis 自动扫描数据持久层的映射文件及DAO接口的关系

@Mapper
public interface StudentMapper {
   

注意:默认情况下,Mybatis 的 xml 映射文件不会编译到 target 的 class 目录下,所以我们需要在 pom.xml 文件中配置 resource

<!--手动指定文件夹为resources-->
<resources>
    <resource>
        <directory>src/main/java</directory>
        <includes>
            <include>**/*.xml</include>
        </includes>
    </resource>
</resources>

3、其他方式开发

1)在运行的主类添加注解包扫描**@MapperScan**(“com.abc.springboot.mapper”)

注释掉 StudentMapper 接口上的@Mapper 注解,在运行主类 Application上加@MapperScan(“com.abc.springboot.mapper”)

@SpringBootApplication
//Mybatis 提供的注解:扫描数据持久层的mapper映谢配置文件,DAO接口上就不用加@Mapper
//basePackages 通常指定到数据持久层包即可
@MapperScan(basePackages = "com.abc.springboot.mapper")
public class Application {
   

2)将接口和映射文件分开

SpringBoot 不能自动编译接口映射的 xml 文件,需要手动在 pom 文件中指定,可以直接将映射文件放到 resources 目录下。在 resources 目录下新建目录 mapper 存放映射文件,将 StudentMapper.xml 文件移到 resources/mapper 目录下

在 application.properties 配置文件中指定映射文件的位置,这个配置只有接口和映射文件不在同一个包的情况下才需要指定

#指定Mybatis映射文件的路径
mybatis.mapper-locations=classpath:mapper/*.xml

事务支持

Spring Boot 使用事务非常简单,底层依然采用的是Spring本身提供的事务管理

- 在入口类中使用注解 @EnableTransactionManagement 开启事务支持
- 在访问数据库的 Service 方法上添加注解 @Transactional 即可

SpringMVC注解

1、@Controller

Spring MVC 的注解,处理http请求

2、@RestController

用于返回字符串或json对象

是 @Controller 与@ResponseBody 的组合注解。如果一个 Controller 类添加了@RestController,那么该 Controller 类下的所有方法都相当于添加了@ResponseBody 注解

3、@RequestMapping

支持 Get 请求,也支持 Post 请求

4、@PostMapping

RequestMapping 和 Post 请求方法的组合
只支持 Post 请求
Post 请求主要用户新增数据

5、@DeleteMapping

RequestMapping 和 Delete 请求方法的组合
只支持 Delete 请求
通常用于删除数据

6、@PutMapping

RequestMapping 和 Put 请求方法的组合
只支持 Put 请求
Put 通常用于修改数据

7、@GetMapping

RequestMapping 和 Get 请求方法的组合
只支持 Get 请求
Get 请求主要用于查询操作
//@Controller
@RestController  
//相当于控制层类上加@Controller + 方法上加@ResponseBody
//意味着当前控制层类中所有方法返还的都是JSON对象
public class StudentController {
   
    @RequestMapping(value = "/student")
	//@ResponseBody
    public Object student() {
   
        Student student = new Student();
        student.setId(1001);
        student.setName("zhangsan");
        return student;
    }

    //该方法请求方式支持:GET和POST请求
    @RequestMapping(value = "/queryStudentById",method = {
   RequestMethod.GET,RequestMethod.POST})
    public Object queryStudentById(Integer id) {
   
        Student student = new Student();
        student.setId(id);
        return student;
    }

	//@RequestMapping(value = "/insert",method = RequestMethod.POST)
    @PostMapping(value = "/insert") //相当于一句话
    //该注解通常在新增数据的时候使用 -> 新增
    public Object insert() {
   
        return "Insert success";
    }

	//@RequestMapping(value = "/delete",method = RequestMethod.DELETE)
    @DeleteMapping(value = "/delete")//相当于上一句话
    //该注解通常在删除数据的时候使用 -> 删除
    public Object delete() {
   
        return "delete Student";
    }

	//@RequestMapping(value = "/update",method = RequestMethod.PUT)
    @PutMapping(value = "/update") //相当于上一句话
    //该注解通常在修改数据的时候使用 -> 更新
    public Object update() {
   
        return "update student info1";
    }
    
    //@RequestMapping(value = "/queryStudentById2",method = RequestMethod.GET)
    //相当于上一句话,只接收GET请求,如果请求方式不对会报405错误
    @GetMapping(value = "/queryStudentById2") 
    //该注解通过在查询数据的时候使用 -> 查询
    public Object queryStudentById2() {
   
        return "Ony GET Method";
    }
}

实现RESTful

我们要访问一个http接口:http: //localhost:8080/boot/order?id=1021&status=1
采用RESTFul风格则http地址为:http: //localhost:8080/boot/order/1021/1

1、常用注解

1)@PathVariable

获取 url 中的数据
该注解是实现 RESTFul 最主要的一个注解

2) @PostMapping

接收和处理Post 方式的请求

3) @DeleteMapping

接收delete方式的请求,可以使用 GetMapping 代替

4) @PutMapping

接收put方式的请求,可以用PostMapping代替

5) @GetMapping

接收get方式的请求

2、案例

@RestController
public class StudentController {
   
    @RequestMapping(value = "/student")
    public Object student(Integer id,Integer age) {
   
        Student student = new Student();
        student.setId(id);
        student.setAge(age);
        return student;
    }

	//@RequestMapping(value = "/student/detail/{id}/{age}")
    @GetMapping(value = "/student/detail/{id}/{age}")
    public Object student1(@PathVariable("id") Integer id,
                           @PathVariable("age") Integer age) {
   
        Map<String,Object> retMap = new HashMap<>();

        retMap.put("id",id);
        retMap.put("age",age);
        return retMap;
    }

	//@RequestMapping(value = "/student/detail/{id}/{status}")
    @DeleteMapping(value = "/student/detail/{id}/{status}")
    public Object student2(@PathVariable("id") Integer id,
                           @PathVariable("status") Integer status) {
   
        Map<String,Object> retMap = new HashMap<>();

        retMap.put("id",id);
        retMap.put("status",status);
        return retMap;
    }

    //以上代码student1和student2会出现请求路径冲突问题
    //通常在RESTful风格中方法的请求方式会按增删改查的请求方式来区分
    //修改请求路径
    //RESUful请求风格要求路径中使用的单词都是名称,最好不要出现动词

    @DeleteMapping(value = "/student/{id}/detail/{city}")
    public Object student3(@PathVariable("id") Integer id,
                           @PathVariable("city") Integer city) {
   
        Map<String,Object> retMap = new HashMap<>();

        retMap.put("id",id);
        retMap.put("city",city);
        return retMap;
    }

    @PostMapping(value = "/student/{id}")
    public String addStudent(@PathVariable("id") Integer id) {
   
        return "add student ID:" + id;
    }

    @PutMapping(value = "/student/{id}")
    public String updateStudent(@PathVariable("id") Integer id) {
   
        return "update Student ID:" +id;
    }
}

3、RESTful 原则

1)增 post 请求、删 delete 请求、改 put 请求、查 get 请求

2)请求路径不要出现动词
例如:查询订单接口
/boot/order/1021/1(推荐)
/boot/queryOrder/1021/1(不推荐)

3)分页、排序等操作,不需要使用斜杠传参数
例如:订单列表接口
/boot/orders?page=1&sort=desc
一般传的参数不是数据库表的字段,可以不采用斜杠

集成 Redis

1、思路:

完善根据学生id查询学生的功能,先从redis缓存中查找,如果找不到,再从数据库中查找,然后放到redis缓存中

2、实现步骤:

1)添加redis依赖

<!-- 加载 spring boot redis 包 -->
<dependency>
 	<groupId>org.springframework.boot</groupId>
 	<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2)在 StudentServiceImpl 中注入RedisTemplate,并修改根据 id获取学生的方法

配置了上面的步骤,Spring Boot 将自动配置RedisTemplate,在需要操作redis的类中注入 redisTemplate 即可。

注意:Spring Boot 帮我们注入 RedisTemplate 类,泛型里面只能写 <String, String>、<Object, Object>或者什么都不写
@Service
public class StudentServiceImpl implements StudentService {
   
    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;

    @Override
    public void put(String key, String value) {
   
        redisTemplate.opsForValue().set(key,value);
    }

    @Override
    public String get(String key) {
   
        String count = (String) redisTemplate.opsForValue().get(key);
        return count;
    }
}

集成Dubbo

1.接口工程:存放实体类和业务接口
	maven java工程
2.服务提供者:接口的实现类,该工程集成MyBatis和Spring
	MyBatis依赖,MyBatis集成Spring依赖,MySQL驱动,Spring相关依赖,Dubbo依赖,
	注册中心zookeeper依赖,Spring集成redis依赖
	1.添加依赖
	2.配置核心配置文件(数据库配置,redis配置,dubbo配置)
	3.MyBatis逆向工程
	4.接口的实现类
3.服务消费者:处理用户浏览器的请求,该工程集成Spring和SpringMVC
	Spring相关依赖,Dubbo依赖,注册中心zookeeper依赖
	1.添加依赖
	2.配置核心配置文件(dubbo配置,配置视图解析器)
	3.编写控制层处理用户的请求

1、开发 Dubbo 服务接口

按照 Dubbo 官方开发建议,创建一个接口项目,该项目只定义接口和 model 类

2、开发 Dubbo 服务提供者

1)application.properties配置dubbo

#配置 dubbo 的服务提供者信息
#服务提供者应用名称(必须写,且不能重复)
spring.application.name=springboot-dubbo-provider
#设置当前工程为服务提供者
spring.dubbo.server=true
#设置注册中心
spring.dubbo.registry=zookeeper://localhost:2181

2)编写 Dubbo 的接口实现类

@Component
@Service(interfaceClass = StudentService.class,version = "1.0.0",timeout = 15000)
//dubbo:service interface="" version="" timeout=""
public class StudentServiceImpl implements StudentService {
   
    @Override
    public Integer queryAllStudentCount() {
   
        //调用数据持久层
        return 1250;
    }
}

3)在 SpringBoot 入口程序类上加开启 Dubbo 配置支持注解

@SpringBootApplication      //开启spring配置
@EnableDubboConfiguration   //开启dubbo配置
public class Application {
   
    public static void main(String[] args) {
   
        SpringApplication.run(Application.class, args);
    }
}

3、开发 Dubbo 服务消费者

1)application.properties 格式-配置 dubbo

#设置 dubbo 配置
#设置服务消费者名称
spring.application.name=springboot-dubbo-consumer
#配置 dubbo 注册中心
spring.dubbo.registry=zookeeper://localhost:2181

2)编写一个 Controller 类,调用远程的 Dubbo 服务

@Controller
public class StudentController {
   
    //dubbo:reference interface="" version="" check=false
    @Reference(interfaceClass = StudentService.class,version = "1.0.0",check = false)
    private StudentService studentService;

    @RequestMapping(value = "/student/count")
    public @ResponseBody Object studentCount() {
   
        Integer allStudentCount = studentService.queryAllStudentCount();
        return "学生总人数为:"+allStudentCount;
    }
}

静态资源

"classpath:/META-INF/resources/"
"classpath:/resources/"
"classpath:/static/"
"classpath:/public/"

非web应用程序

在 Spring Boot 框架中,要创建一个非 Web 应用程序(纯 Java 程序),有两种方式。

方式一

直接在 main 方法中,根据 SpringApplication.run()方法获取返回的 Spring 容器对象,再获取业务 bean 进行调用。

@SpringBootApplication
public class Application {
   
    public static void main(String[] args) {
   
        /** * Springboot程序启动后,返回值是ConfigurableApplicationContext,它也是一个Spring容器 * 它其实相当于原来Spring容器中启动容器ClasspathXmlApplicationContext */

        SpringApplication.run(Application.class, args);

        //获取Springboot容器
        ConfigurableApplicationContext applicationContext = SpringApplication.run(Application.class, args);

        //从spring容器中获取指定bean对象
        StudentService studentService = (StudentService) applicationContext.getBean("studentServiceImpl");

        //调用业务方法
        String sayHello = studentService.sayHello();
        System.out.println(sayHello);
    }
}

方式二

Spring boot 的入口类实现 CommandLineRunner 接口

将原有 Application 类的@SpringBootApplication 注解注释掉,复制一个新的 Application 取名为 Application2,实现 CommandLineRunner 接口

@SpringBootApplication  //开启spring配置
public class Application implements CommandLineRunner {
   
    @Autowired
    private StudentService studentService;

    public static void main(String[] args) {
   
        //SpringBoot启动程序,会初始化Spring容器
        SpringApplication.run(Application.class, args);
    }
    
    //重写CommandLineRunner类中的run方法
    @Override
    public void run(String... args) throws Exception {
   

        //调用业务方法
        String sayHello = studentService.sayHello("World");
        System.out.println(sayHello);
    }
}

拦截器

拦截器步骤

1、实现 HandlerInterceptor 接口

public class UserInterceptor implements HandlerInterceptor {
   

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
   

        System.out.println("进入拦截器-------------------");
        //编写业务拦截的规则
        //从session中获取用户的信息
        User user = (User) request.getSession().getAttribute("user");

        //判断用户是否登录
        if (null == user) {
   
            //未登录
            response.sendRedirect(request.getContextPath() + "/user/error");
            return false;
        }
        return true;
    }

    @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 {
   
    }
}

2、创建一个控制层

@Controller
@RequestMapping(value = "/user")
public class UserController {
   
    //用户登录的请求,需要排除
    @RequestMapping(value = "/login")
    public @ResponseBody Object login(HttpServletRequest request) {
   
        //将用户的信息存放到session中
        User user = new User();
        user.setId(1001);
        user.setUsername("zhangsan");
        request.getSession().setAttribute("user",user);

        return "login SUCCESS";
    }

    //该请求需要用户登录之后才可访问
    @RequestMapping(value = "/center")
    public @ResponseBody Object center() {
   
        return "See Center Message";
    }

    //该请求不登录也可访问
    @RequestMapping(value = "/out")
    public @ResponseBody Object out() {
   
        return "Out see anytime";
    }

    //如果用户未登录访问了需要登录才可访问的请求,之后会跳转至该请求路径
    //该请求路径不需要用户登录也可访问
    @RequestMapping(value = "/error")
    public @ResponseBody Object error() {
   
        return "error";
    }
}

3、@Configuration 定义配置类

在项目中创建 一个config 包 , 创 建 一 个配置类InterceptorConfig,并实现WebMvcConfigurer接口,覆盖接口中的addInterceptors方法,并为该配置类添加@Configuration 注解,标注此类为配置类,让SpringBoot扫描到,这里的操作相当于SpringMVC的注册拦截器,@Configuration就相当于一个applicationContext-mvc.xml

@Configuration  //定义此类为配置文件(即相当于之前的xml配置文件)
public class InterceptorConfig implements WebMvcConfigurer {
   
    //mvc:interceptors
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
   
        //要拦截user下的所有访问请求,必须用户登录后才可访问,
        // 但是这样拦截的路径中有一些是不需要用户登录也可访问的
        String[] addPathPatterns = {
    "/user/**" };

        //要排除的路径,排除的路径说明不需要用户登录也可访问
        String[] excludePathPatterns = {
   
            "/user/out", "/user/error","/user/login"
        };

        //mvc:interceptor bean class=""
        registry.addInterceptor(new UserInterceptor()).addPathPatterns(addPathPatterns).excludePathPatterns(excludePathPatterns);
    }
}

使用Servlet

通过注解扫描方式

1、通过注解方式创建一个 Servlet

@WebServlet(urlPatterns = "/myservlet")
public class MyServlet extends HttpServlet {
   
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
   
       resp.getWriter().println("My SpringBoot Servlet-1");
       resp.getWriter().flush();
       resp.getWriter().close();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
   
        doGet(req, resp);
    }
}

2、在主应用程序Application类上添加@ServletComponentScan(“com.abc.springboot.servlet”)

@SpringBootApplication  //开启spring配置
@ServletComponentScan(basePackages = "com.bjpowernode.springboot.servlet")
public class Application {
   
    public static void main(String[] args) {
   
        SpringApplication.run(Application.class, args);
    }
}

通过SpringBoot的配置类

1、编写一个Spring Boot的配置类,在该类中注册Servlet

@Configuration  //该注解将此类定义为配置类(相当一个xml配置文件)
public class ServletConfig {
   
    //@Bean是一个方法级别上的注解,主要用在配置类里
    //相当于一个
    // <beans>
    // <bean id="" class="">
    // </beans>
    @Bean
    public ServletRegistrationBean myServletRegistrationBean() {
   
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new MyServlet(),"/myservlet");
        return servletRegistrationBean;
    }
}

使用Filter

注解方式实现

1、通过注解方式创建一个 Filer

@WebFilter(urlPatterns = "/myfilter")
public class MyFilter implements Filter {
   
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
   
        System.out.println("---------您已进入过滤器---------");
        filterChain.doFilter(servletRequest, servletResponse);
    }
}

2、在主应用程序Application类上添加@ServletComponentScan("basePackages = “com.abc.springboot.filter”)

@SpringBootApplication
@ServletComponentScan(basePackages = "com.bjpowernode.springboot.filter")
public class Application {
   
    public static void main(String[] args) {
   
        SpringApplication.run(Application.class, args);
    }
}

配置类实现

1、编写一个 Spring Boot 的配置类,在该类中注册Filter

@Configuration  //定义此类为配置类
public class FilterConfig {
   
    @Bean
    public FilterRegistrationBean myFilterRegistrationBean() {
   
        //注册过滤器
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new MyFilter());
        //添加过滤路径
        filterRegistrationBean.addUrlPatterns("/user/*");
        return filterRegistrationBean;
    }
}

配置字符编码

方式一

使用传统的Spring提供的字符编码过滤器

1、创建一个 Servlet

@WebServlet(urlPatterns = "/myservlet")
public class MyServlet extends HttpServlet {
   
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
   
        resp.getWriter().println("世界您好,Hello World!");
        //统一设置浏览器编码格式
        resp.setContentType("text/html;character=utf-8");
        resp.getWriter().flush();
        resp.getWriter().close();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
   
        doGet(req, resp);
    }
}

2、创建配置类 SystemConfig

@Configuration  //将此类定义为配置文件
public class SystemConfig {
   
    @Bean
    public FilterRegistrationBean characterEncodingFilterRegistrationBean() {
   
        //创建字符编码过滤器
        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
        //设置强制使用指定字符编码
        characterEncodingFilter.setForceEncoding(true);
        //设置指定字符编码
        characterEncodingFilter.setEncoding("UTF-8");

        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();

        //设置字符编码过滤器
       filterRegistrationBean.setFilter(characterEncodingFilter);
        //设置字符编码过滤器路径
        filterRegistrationBean.addUrlPatterns("/*");

        return filterRegistrationBean;
    }
}

3、关闭 SpringBoot 的 http 字符编码支持

#关闭 springboot 的 http 字符编码支持
#只有关闭该选项后,spring 字符编码过滤器才生效
spring.http.encoding.enabled=false

方式二

在 application.properties 中配置字符编码(推荐)

1、SpringBoot 核心配置文件添加字符编码设置

#设置请求响应字符编码
spring.http.encoding.enabled=true
spring.http.encoding.force=true
spring.http.encoding.charset=utf-8

集成logback日志

Spring Boot 官方推荐优先使用带有 -spring 的文件名作为你的日志配置(如使用logback-spring.xml ),命名为 logback-spring.xml 的日志配置文件。

默认的命名规则,并且放在 src/main/resources 下如果你即想完全掌控日志配置,但又不想用 logback.xml 作为 Logback 配置的名字,application.yml 可以通过logging.config 属性指定自定义的名字:

<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为 WARN,则低于 WARN 的信息都不会输出 -->

<!-- scan:当此属性设置为 true 时,配置文件如果发生改变,将会被重新加载,默认值为true -->

<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当 scan 为 true 时,此属性生效。默认的时间间隔为 1 分钟。 -->

<!-- debug:当此属性设置为 true 时,将打印出 logback 内部日志信息,实时查看 logback运行状态。默认值为 false。通常不打印 -->
<configuration scan="true" scanPeriod="10 seconds">
    <!--输出到控制台-->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <!--此日志 appender 是为开发使用,只配置最底级别,控制台输出的日志级别是大 于或等于此级别的日志信息-->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>debug</level>
        </filter>
        <encoder>
            <Pattern>%date [%5p] [%thread] %logger{60} [%file : %line] %msg%n
            </Pattern>
            <!-- 设置字符集 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!--单个定义-->
    <logger name="com.bjpowernode.springboot.mapper" level="trace"/>

    <!--如果root标签指定的日志级别那么以根日志级别为准,如果没有则已当前追加器日志级别为准-->
    <!--全部-->
    <!-- appender trace trace root trace appender trace debug root debug appender trace debug root 空 如果root没有值默认root级别是debug appender debug info root info -->
    <root level="info">
        <appender-ref ref="CONSOLE"/>
        <!--<appender-ref ref="FILE"/>-->
    </root>
</configuration>

集成Thymeleaf模板

集成Thymeleaf

在 Spring boot 的核心配置文件application.properties 中对Thymeleaf 进行配置

#thymeleaf页面的缓存开关,默认true开启缓存
#建议在开发阶段关闭thymeleaf页面缓存,目的实时看到页面
spring.thymeleaf.cache=false

Springboot使用thymeleaf作为视图展示,约定将模板文件放置在src/main/resource/templates目录下,静态资源放在 src/main/resource/static 目录下

表达式

1、标准变量表达式 ${}

标准变量表达式用于访问容器(tomcat)上下文环境中的变量,功能和EL中的${}相同。Thymeleaf 中的变量表达式使用 ${变量名} 的方式获取 Controller 中 model 其中的数据

<h2>展示 User 用户信息:</h2>
用户编号:<span th:text="${user.id}"></span><br/>
用户姓名:<span th:text="${user.name}"></span><br/>
用户手机号:<span th:text="${user.phone}"></span><br/>
用户地址:<span th:text="${user.address}"></span><br/>

th:text="" 是Thymeleaf的一个属性,用于文本的显示

*2、选择变量表达式 {} ------不推荐

选择变量表达式,也叫星号变量表达式,使用 th:object 属性来绑定对象
选择表达式首先使用 th:object 来绑定后台传来的 User 对象,然后使用 * 来代表这个对象,后面 {} 中的值是此对象中的属性。
选择变量表达式执行时在选择的对象上求解,而${…}是在上下文的变量Model上求解。

<h2>展示User用户信息(星号表达式,仅在 div 范围内有效):</h2>
<div th:object="${user}">
    用户编号:<span th:text="*{id}"></span><br/>
    用户姓名:<span th:text="*{name}"></span><br/>
    用户手机号:<span th:text="*{phone}"></span><br/>
    用户地址:<span th:text="*{address}"></span><br/>
</div>

3、URL 表达式 @{}

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>URL路径表达式</title>
</head>
<body>
<h1>URL路径表达式:@{....}</h1>
<h2>a标签中的绝对路径(没有参数)</h2>
<a href="http://www.baidu.com">传统写法:跳转至百度</a><br/>
<a th:href="@{http://www.bjpowernode.com}">路径表达式:路径到动力节点</a><br/>
<a th:href="@{http://localhost:8080/user/detail1}">跳转至:/user/detail1</a><br/>
<a href="http://localhost:8080/user/detail1">传统写法跳转至:/user/detail1</a><br/>

<h2>绝对路径(带参数)(不推荐使用)</h2>
<a href="http://localhost:8080/test?username='zhangsan'">绝对路径,带参数:/test,并带参数username</a><br/>
<a th:href="@{http://localhost:8080/test?username=zhangsan}">路径表达工写法,带参数:/test,并带参数username</a><br/>  
    
<h2>URL路径表达式,相对路径[没有参数](实际开发中推荐使用的)</h2>
<a th:href="@{/user/detail1}">跳转至:/user/detail1</a><br/>

<h2>相对路径(带参数)</h2>
<a th:href="@{/test?username=lisi}">相对路径,带参数</a>

<h2>相对路径(带参数:后台获取的参数值)</h2>
<!--/test?username=1001-->
<a th:href="@{
    '/test?username='+${id}}">相对路径:获取后台参数值</a>

<h2>相对路径(带多个参数:后台获取的参数值)</h2>
<!-- /test1?id=1001&username=zhaoliu&age=28 -->
<a th:href="@{
    '/test1?id='+${id}+'&username='+${username}+'&age='+${age}}">相对路径(带多个参数:后台获取的参数值)</a>
<a th:href="@{/test1(id=${id},username=${username},age=${age})}">强烈推荐使用:@{}相对路径(带多个参数:后台获取的参数值)</a><br/>
<a th:href="@{
    '/test2/'+${id}}">请求路径为RESTful风格</a><br/>
<a th:href="@{
    '/test3/'+${id}+'/'+${username}}">请求路径为RESTful风格</a><br/>
</body>
</html>

常见属性

1、th:action

th:action **定义后台控制器的路径,**类似< form>标签的action属性,主要结合URL表达式获取动态变量

<h1>th:action 属性的使用</h1>
<h2>请求路径中需要动态获取变量数据时,必须添加th前缀</h2>
<form th:action="@{
    '/user/login?id='+${user.id}}"></form>

<h2>以下两种方式获取不到用户 id</h2>
<form action="'/user/login?id='+${user.id}"></form>
<form action="/user/login"+${user.id}></form>

因为我们 Thymeleaf 是以 html 为载体的,所以 html 不会认识${}语法。

我们请求的流程是,发送请求给服务器,服务器接收请求后,处理请求,跳转到指定的静态 html 页面,在服务器端,Thymeleaf 模板引擎会按照它的语法,对动态数据进行处理,所以如果要是 th 开头,模板引擎能够识别,会在服务器端进行处理,获取数据;如果没有以 th 开头,那么 Thymeleaf 模板引擎不会处理,直接返回给客户端了。

2、th:method

设置请求方法

<h1>th:method 属性的使用</h1>
<form th:action="@{/user/login}" th:method="post"></form>

3、th:href

定义超链接,主要结合 URL 表达式,获取动态变量

<h1>th:href使用</h1>
<a href="http://www.baidu.com">超链接百度</a><br/>
<a th:href="'http://www.baidu.com?id=' + ${user.id}">th:href 链接</a>

4、th:src

用于外部资源引入,比如

<h1>th:src属性的使用</h1>
<!--以下方式无法引入 js-->
<script src="/static/js/jquery-1.7.2.min.js"></script>
<!--该方法是常用方法-->
<script type="text/javascript" th:src="@{/jquery-1.7.2.min.js}"> </script>
<script> $(function () {
      alert("引入 js 文件"); }); </script>

5、th:id

类似 html 标签中的 id 属性

<span th:id="${hello}">aaa</span>

6、th:name

设置名称

7、th:value

类似 html 标签中的 value 属性,能对某元素的 value 属性进行赋值

8、th:attr

该属性也是用于给 HTML 中某元素的某属性赋值,好处是可以给 html 中没有定义的属性动态的赋值。

<h1>th:attr 属性的使用</h1>
<span zhangsan="${user.name}"></span>
<!--通过 th:attr 对自定义的属性赋值-->
<span th:attr="zhangsan=${user.name}"></span>

9、th:text

用于文本的显示,该属性显示的文本在标签体中,如果是文本框,数据会在文本框外显示,要想显示在文本框内,使用 th:value

<input type="text" id="realName" name="reaName" th:text="${realName}">

10、th:object

用于数据对象绑定,通常用于选择变量表达式(星号表达式)

11、th:onclick

<h1>th:onclick 的使用</h1>
<!--目前 thymeleaf 版本要求只能传递数字和布尔值-->
<a th:onclick="'show('+${user.id}+')'">点击:显示学生编号</a>
<script type="text/javascript"> function show(id) {
      alert("用户编号为:" + id); } </script>

12、th:each(常用)

这个属性非常常用,比如从后台传来一个对象集合那么就可以使用此属性遍历输出,它与JSTL 中的<c: forEach>类似,此属性既可以循环遍历集合,也可以循环遍历数组及 Map

1)遍历 List 集合

@RequestMapping("/each/list")
public String eachList(Model model) {
   
    List<User> userList = new ArrayList<User>();

    for (int i = 0; i < 10; i++) {
   
        User user = new User();
        user.setId(100 + i);
        user.setNick("张" + i);
        user.setPhone("1361234567" + i);
        user.setAddress("北京市大兴区" + i);
        userList.add(user);
    }
    model.addAttribute("userList", userList);
    model.addAttribute("data", "SpringBoot");
    return "eachList";
}
<!-- user 当前循环的对象变量名称(随意) userStat 当前循环对象状态的变量(可选默认就是对象变量名称+Stat) ${userList} 当前循环的集合 -->
<div th:each="user,userStat:${userList}">
    <span th:text="${userStat.index}"></span>
    <span th:text="${userStat.count}"></span>
    <span th:text="${user.id}"></span>
    <span th:text="${user.nick}"></span>
    <span th:text="${user.phone}"></span>
    <span th:text="${user.address}"></span>
</div>

2)遍历 Map 集合

@RequestMapping(value = "/each/map")
public String eachMap(Model model) {
   
    Map<Integer, Object> userMaps = new HashMap<Integer, Object>();

    for (int i = 0; i < 10; i++) {
   
        User user = new User();
        user.setId(i);
        user.setNick("李四" + i);
        user.setPhone("1390000000" + i);
        user.setAddress("天津市" + i);
        userMaps.put(i, user);
    }
    model.addAttribute("userMaps", userMaps);
    return "eachMap";
}
<body>
<!-- Map集合结构 key value 0 user 1 user 2 user 3 user .. ... -->
<div th:each="userMap,userMapStat:${userMaps}">
    <span th:text="${userMapStat.count}"></span>
    <span th:text="${userMapStat.index}"></span>
    <span th:text="${userMap.key}"></span>
    <span th:text="${userMap.value}"></span>
    <span th:text="${userMap.value.id}"></span>
    <span th:text="${userMap.value.nick}"></span>
    <span th:text="${userMap.value.phone}"></span>
    <span th:text="${userMap.value.address}"></span>
</div>

3)遍历 Array 数组

@RequestMapping(value = "/each/array")
public String eachArray(Model model) {
   

    User[] userArray = new User[10];

    for (int i = 0; i < 10; i++) {
   

        User user = new User();
        user.setId(i);
        user.setNick("赵六" + i);
        user.setPhone("1380000000" + i);
        user.setAddress("深圳市" + i);
        userArray[i] = user;

    }
    model.addAttribute("userArray", userArray);
    return "eachArray";
}
<body>
<h1>循环遍历Array数组(使用方法同list一样)</h1>
<div th:each="user,userStat:${userArray}">
    <span th:text="${userStat.index}"></span>
    <span th:text="${userStat.count}"></span>
    <span th:text="${user.id}"></span>
    <span th:text="${user.nick}"></span>
    <span th:text="${user.phone}"></span>
    <span th:text="${user.address}"></span>
</div>
</body>

13、th:if;th:unless;th:switch/th:case

<body>
<h1>th:if 用法:如果满足条件显示(执行),否则相反</h1>
<div th:if="${sex eq 1}"></div>
<div th:if="${sex eq 0}"></div>

<h1>th:unless 用法:与th:if用法相反,即条件判断取反</h1>
<div th:unless="${sex ne 1}"></div>

<h1>th:switch/th:case用法</h1>
<div th:switch="${productType}">
    <span th:case="0">产品0</span>
    <span th:case="1">产品1</span>
    <span th:case="*">无此产品</span>
</div>
</body>
一旦某个 case 判断值为 true,剩余的 case 默认不执行,“*”表示默
认的 case,前面的 case 都不匹配时候,执行默认的 case

15、th:inline

th:inline 有三个取值类型 (text, javascript 和 none),值为 none 什么都不做,没有效果

1)内敛文本(th:inline=”text”)

内敛文本表达式不依赖于 html 标签,直接使用内敛表达式[[表达式]]即可获取动态数据,但必须要求在父级标签上加 th:inline = “text”属性

一般我们将 th:inline="text"放到< body th:inline=“text”>标签中

<body th:inline="text">
<div th:text="${data}">xxx</div>
<h3 th:text="${data}">xxx</h3>

<h1>内敛文本: th:inline="text"</h1>
<div th:inline="text">

    数据:[[${data}]]

</div>

数据outside:[[${data}]]
</body>

2)内敛脚本(th:inline=”javascript”)

th:inline=”javascript”在 js 代码中获取后台的动态数据

<h1>内敛脚本 th:inline="javascript"</h1>
<script type="text/javascript"  th:inline="javascript">
    function showData() {
        alert([[${data}]]);
        alert("----");
    }
</script>
<button th:onclick="showData()">展示数据</button>

字面量

字面量:对应数据类型的合法取值,可以在 html 页面直接使用,不需要后台传递

1)文本字面量

用单引号’…'包围的字符串为文本字面量

<h1>文本字面量:用单引号'...'包围的字符串</h1>
<a th:href="@{
    '/user/info?id=' + ${user.id}}">查看用户:文本字面的路径使用</a><br/>
<span th:text="您好"></span>

2)数字字面量

<h1>数字字面量</h1>
今年是<span th:text="2019">1949</span><br/>
20年后,将是<span th:text="2019 + 20">1969</span><br/>

3)boolean 字面量

<h1>boolean 字面量</h1>
<div th:if="${success}">执行成功</div>
<div th:unless="${flag}">执行不成功</div>

4)null 字面量

<h1>null 字面量</h1>
<span th:if="${user ne null}">用户不为空</span><br/>
<span th:unless="${user eq null}">用户不为空(使用 th:unless 取反)</span><br/>

字符串拼接

<h1>文本字面量使用"+"拼接字符串</h1>
<span th:text="''+${totalRows}+''+${totalPage}+'页,当前第'+${currentPage}+''"></span>
<h1>另一种更优雅的方式:使用"|要拼接的内容|"减少字符串拼接的加号</h1>
<span th:text="|共${totalRows}条${totalPage}页,当前第${currentPage}页|"></span>

运算符

三元运算:表达式?”正确结果”:”错误结果”
算术运算:+ , - , * , / , %
关系比较::> , < , >= , <= ( gt , lt , ge , le )
相等判断:== , != ( eq , ne )

<body>
<h1>三元运算符</h1>
<span th:text="${sex eq 1 ? '':''}"></span><br/>
<span th:text="${sex == 1 ? '':''}"></span><br/>
20*8=<span th:text="20 * 8"></span><br/>
20/8=<span th:text="20 / 8"></span><br/>
20+8=<span th:text="20 + 8"></span><br/>
20-8=<span th:text="20 - 8"></span><br/>
<div th:if="5 > 2">5>2 是真的</div>
<div th:if="5 gt 2">5 gt 2 是真的</div>
</body>

表达式基本对象

模板引擎提供了一组内置的对象,这些内置的对象可以直接在模板中使用,这些对象由#号开始引用,我们比较常用的内置对象

1)#request

#request 相 当 于 httpServletRequest 对 象 , 这 是 3.x 版 本 , 若 是 2.x 版 本 使 用 #httpServletRequest,在页面获取应用的上下文根,一般在 js 中请求路径中加上可以避免 404

<script type="text/javascript" th:inline="javascript"> // http://localhost:8080/springboot/user/detail //获取协议名称 var scheme = [[${
     #request.getScheme()}]]; //获取服务器名称 var serverName = [[${
     #request.getServerName()}]]; //获取服务器端口号 var serverPort = [[${
     #request.getServerPort()}]]; //获取上下文根 var contextPath = [[${
     #request.getContextPath()}]]; var allPath = scheme + "://" +serverName+":"+serverPort+contextPath; var requestURL = [[${
     #httpServletRequest.requestURL}]]; var queryString = [[${
     #httpServletRequest.queryString}]]; var requestAddress = requestURL + "?" +queryString; alert(requestAddress); </script>

2)#session

相当于 HttpSession 对象,这是 3.x 版本,若是 2.x 版本使用#httpSession

<h1>从SESSION中获取值</h1>
<span th:text="${#session.getAttribute('data')}"></span><br/>
<span th:text="${#httpSession.getAttribute('data')}"></span><br/>
<span th:text="${session.data}"></span><br/>

自动配置原理

组件添加

1、@Configuration

#############################Configuration使用示例###################################
/**
 * 1、配置类里面使用@Bean标注在方法上给容器注册组件,默认也是单实例的
 * 2、配置类本身也是组件
 * 3、proxyBeanMethods:代理bean的方法
 *      Full(proxyBeanMethods = true)、【保证每个@Bean方法被调用多少次返回的组件都是单实例的】
 *      Lite(proxyBeanMethods = false)【每个@Bean方法被调用多少次返回的组件都是新创建的】
 *      组件依赖必须使用Full模式默认。其他默认是否Lite模式
 */
@Configuration(proxyBeanMethods = false) //告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig {
    /**
     * Full:外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象
     * @return
     */
    @Bean //给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例
    public User user01(){
        User zhangsan = new User("zhangsan", 18);
        //user组件依赖了Pet组件
        zhangsan.setPet(tomcatPet());
        return zhangsan;
    }

    @Bean("tom")
    public Pet tomcatPet(){
        return new Pet("tomcat");
    }
}

2、@Import

//给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名
@Import({User.class, DBHelper.class})
@Configuration(proxyBeanMethods = false) //告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig {
}

3、@Conditional

条件装配:满足Conditional指定的条件,则进行组件注入

原生配置文件引入

1、@ImportResource

用于引入.xml配置文件

@ImportResource("classpath:beans.xml")
public class MyConfig {}

配置绑定

如何使用Java读取到properties文件中的内容,并且把它封装到JavaBean中,以供随时使用;

1、@ConfigurationProperties

2、@EnableConfigurationProperties + @ConfigurationProperties

3、@Component + @ConfigurationProperties

引导加载自动配置类

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication{}

@EnableAutoConfiguration解释:

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

1、@AutoConfigurationPackage
自动配置包?指定了默认的包规则
@Import(AutoConfigurationPackages.Registrar.class)  //给容器中导入一个组件
public @interface AutoConfigurationPackage {}
//利用Registrar给容器中导入一系列组件
//将指定的一个包下的所有组件导入进来?MainApplication 所在包下。

2、@Import(AutoConfigurationImportSelector.class)
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位置的文件
    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,\	
...

总结

• SpringBoot先加载所有的自动配置类  xxxxxAutoConfiguration
• 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties里面拿。xxxProperties和配置文件进行了绑定
• 生效的配置类就会给容器中装配很多组件
• 只要容器中有这些组件,相当于这些功能就有了
• 定制化配置
	用户直接自己@Bean替换底层的组件
	用户去看这个组件是获取的配置文件什么值就去修改。

xxxxxAutoConfiguration ---> 组件  ---> xxxxProperties里面拿值  ----> application.properties

Lombok

===============================简化JavaBean开发===================================
@NoArgsConstructor
//@AllArgsConstructor
@Data
@ToString
@EqualsAndHashCode
public class User {}
================================简化日志开发===================================
@Slf4j
@RestController
public class HelloController {}

dev-tools

项目或者页面修改以后:Ctrl+F9;