Spring Boot是Pivotal团队在Spring的基础上供给的一套全新的开源结构,其意图是为了简化Spring运用的搭建和开发过程。Spring Boot去除了许多的XML装备文件,简化了杂乱的依靠办理。

官网地址:spring.io/projects/sp…

Spring Boot入门

简介

Spring Boot是简化Spring运用开发的一个结构、整个Spring技能栈的一个大整合(Spring全家桶年代)、J2EE开发的一站式处理方案(Spring Cloud是分布式全体处理方案)。
优点:
– 快速创立独立运转的Spring项目以及与干流结构集成
– 运用嵌入式的Servlet容器,运用无需打成WAR包
– starters主动依靠与版别操控
– 许多的主动装备,简化开发,也可修正默许值
– 无需装备XML,无代码生成,开箱即用
– 准出产环境的运转时运用监控
– 与云计算的天然集成

单体运用与微服务

– 单体运用:ALL IN ONE(一切内容都在一个运用里边)
– 微服务:每一个功用元素最终都是一个可独立替换和独立晋级的软件单元
微服务是一种架构风格(服务微化),一个运用应该是一组小型服务,能够经过HTTP的办法进行互通

HelloWorld事例

工程创立及事例能够参阅文章进行操作:在IDEA中创立SpringBoot项目

POM文件

父项目是Spring Boot的版别仲裁中心(他来真正办理Spring Boot运用里边的一切依靠版别),今后咱们导入依靠默许是不需求写版别(没有在dependencies里边办理的依靠天然需求声明版别号)

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.11</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

发动器 spring-boot-starter(spring-boot场景发动器),spring-boot-starter-web 帮咱们导入了web模块正常运转所依靠的组件。

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

Spring Boot将一切的功用场景都抽取出来,做成一个个的starters(发动器),只需求在项目里边引进这些starter相关场景的一切依靠都会导入进来。要用什么功用就导入什么场景的发动器。

主程序类

// 主动生成的
@SpringBootApplication
public class SpringBootDemo0Application {
	public static void main(String[] args) {
		SpringApplication.run(SpringBootDemo0Application.class, args);
	}
}

@SpringBootApplication: Spring Boot运用标示在某个类上阐明这个类是SpringBoot的主装备类,SpringBoot 就应该运转这个类的main办法来发动SpringBoot运用。

Spring Boot装备

装备文件

SpringBoot运用一个大局的装备文件,装备文件名固定:application.properties 或许 application.yml。装备文件放在 src/main/resources目录 或许 类途径/config 下。作用是修正SpringBoot主动装备的默许值。

YAML

YAML(YAML Ain’t Markup Language),.yml为结尾,以数据为中心,比json、xml等更适合做装备文件。

YAML装备比如
server:
    port: 8081
等价于XML装备:
<server>
    <port>8081</port>
</server>

【语法】
key: value(留意冒号后边有个空格)
以空格的缩进来操控层级联系,只需是左对齐的一列数据,都是同一个层级

【值写法】

(1)字面量:一般的值(数字,字符串,布尔)

  • k: v,字面量直接写
  • 字符串默许不必加上单引号或许双引号
  • “”(双引号),name: “zhangsan \n lisi” 会输出 zhangsan 换行 lisi
  • ”(单引号),name: ‘zhangsan \n lisi’ 会输出 zhangsan \n lisi

(2)目标、Map

  • k: v,鄙人一行来写目标的特点和值
friends:
    lastName: zhangsan
	age: 20
或许:
friends: {lastName:zhangsan,age:18}

(3)数组(List、Set)

  • 用- 值表明数组中的一个元素
pets:
    ‐ cat
    ‐ dog
    ‐ pig
pets: [cat,dog,pig]

装备文件值注入

@ConfigurationProperties

1)导入装备文件处理器

<!‐‐导入装备文件处理器,装备文件进行绑定就会有提示‐‐>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-configuration-processor</artifactId>
	<optional>true</optional>
</dependency>

2)javaBean目标
@ConfigurationProperties(prefix = "person") 会将装备文件和类进行绑定:

/**
 * 将装备文件中装备的每一个特点的值,映射到这个组件中
 * @ConfigurationProperties:告诉SpringBoot将本类中的一切特点和装备文件中相关的装备进行绑定;
 *      prefix = "person":装备文件中哪个下面的一切特点进行逐个映射
 * 只需这个组件是容器中的组件,才能容器供给的@ConfigurationProperties功用;
 */
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
    private String lastName;
    private Integer age;
    private Boolean boss;
    private Date birth;
    private Map<String, Object> maps;
    private List<Object> lists;
    private Dog dog;
	....
}

3)装备文件 application.yml

person:
  lastName: haha
  age: 18
  boss: false
  birth: 2022/01/01
  maps: {k1: v1,k2: v2}
  lists:
    - lisi
    - wangwu
  dog:
    name: 芒果
    age: 1

或许装备文件application.properties

#处理乱码问题
spring.message.encoding=UTF-8
#person
person.last-name=haha
person.age=20
person.birth=2022/01/02
person.boss=true
person.maps.k1=v1
person.maps.k2=v2
person.lists=a,b,c
person.dog.name=丸子
person.dog.age=5

乱码问题还需求装备:

万字SpringBoot学习笔记|菜鸟版

4)单元测验,先将内容注入(@Autowired)然后运用
万字SpringBoot学习笔记|菜鸟版

与@Value的区别

@ConfigurationProperties@Value 的区别:

  • @ConfigurationProperties 是批量注入装备文件中的特点,@Value 是一个个指定
  • @ConfigurationProperties 支撑松懈绑定(松懈语法) 、不支撑SpEL(表达式如#{2*4})、支撑JSR303数据校验 、支撑杂乱类型封装(如map)
  • @Value 不支撑松懈绑定(松懈语法) 、支撑SpEL、不支撑JSR303数据校验 、不支撑杂乱类型封装
@Component
// @ConfigurationProperties(prefix = "person")
public class Person {
    @Value("${person.last-name}")
    private String lastName;
    @Value("#{2*4}")
    private Integer age;
    @Value("true")
    private Boolean boss;
    @Value("${person.birth}")
    private Date birth;
    ...

松懈绑定:
– person.firstName:运用规范办法
– person.first-name:大写用-
– person.first_name:大写用_
– PERSON_FIRST_NAME:引荐体系特点运用这种写法
JSR303数据校验:

万字SpringBoot学习笔记|菜鸟版

运用规矩:

  • 假如说,咱们只是在某个事务逻辑中需求获取一下装备文件中的某项值,运用@Value
@Value("${febase.api.host}")
private String febaseHost;
  • 假如说,咱们专门编写了一个javaBean来和装备文件进行映射,咱们就直接运用@ConfigurationProperties

读取外部装备文件

@PropertySource:加载指定的装备文件

@PropertySource("classpath:person.properties")
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
    private String lastName;
    private Integer age;
    private Boolean boss;
    ...
}

@ImportResource:导入Spring的装备文件,让装备文件里边的内容收效--标示在一个装备类上
如下咱们自己编写的装备文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  <bean id="helloService" class="com.stydyspring.spring_boot_demo0.service.HelloService"></bean>
</beans>

咱们能够标示在主装备类上:

@SpringBootApplication
// 导入Spring的装备文件让其收效
@ImportResource(locations = {"classpath:beans.xml"})
public class SpringBootDemo0Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootDemo0Application.class, args);
    }
}

测验:

@SpringBootTest
class SpringBootDemo0ApplicationTests {
    @Autowired
    ApplicationContext ioc;
    @Test
    public void testHelloService(){
        boolean containsBean = ioc.containsBean("helloService");
        System.out.println(containsBean); 
        // 上一步没加@ImportResource之前回来false
        // 增加@ImportResource之后回来true
    }
}

SpringBoot引荐给容器中增加组件的办法,引荐运用全注解的办法 @Configuration

/**
 * @Configuration:指明当时类是一个装备类,便是来代替之前的Spring装备文件
 * 
 * 在装备文件中用<bean><bean/>标签增加组件。在装备类中运用@Bean注解
 */
@Configuration
public class MyAppConfig {
    // 将办法的回来值增加到容器中;容器中这个组件默许的id便是办法名
    @Bean
    public HelloService helloService(){
        System.out.println("装备类@Bean给容器中增加组件了");
        return new HelloService();
    }
}

装备文件占位符

随机数

${random.value}、${random.int}、${random.long}、${random.uuid}
${random.int(10)}、${random.int[1024,65536]}

占位符获取之前装备的值,假如没有能够是用:指定默许值

#person
person.last-name=haha${random.uuid}
person.age=${random.int}
person.birth=2222/02/02
person.boss=false
person.maps.k1=v11111
person.maps.k2=v22222
person.lists=a,b,c,d,e,f
person.dog.name=${person.hello:hello}_dog
person.dog.age=1

Profile

Profile是Spring对不同环境供给不同装备功用的支撑,能够经过激活、指定参数等办法快速切换环境。
多profile文件办法格局如:application-{profile}.properties/yml,如 application-dev.properties、application-prod.properties

默许运用application.properties的装备

激活办法:

  • 指令行 --spring.profiles.active=dev
    • java -jar spring-boot-02-config-0.0.1-SNAPSHOT.jar –spring.profiles.active=dev;
  • 装备文件 spring.profiles.active=dev
  • jvm参数 –Dspring.profiles.active=dev

万字SpringBoot学习笔记|菜鸟版

万字SpringBoot学习笔记|菜鸟版

yml支撑多文档块办法:

server:
	port: 8081
spring:
	profiles:
	active: prod
‐‐‐
server:
	port: 8083
spring:
	profiles: dev
‐‐‐
server:
	port: 8084
spring:
	profiles: prod #指定归于哪个环境

装备文件加载方位

spring boot 发动会扫描以下方位的application.properties或许application.yml文件作为Spring boot的默许装备文件

  1. file:./config/
  2. file:./
  3. classpath:/config/
  4. classpath:/

以上是依照优先级从高到低的次序,一切方位的文件都会被加载,高优先级装备内容会掩盖低优先级装备内容。
能够经过装备spring.config.location来改动默许装备。项目打包好今后,能够运用指令行参数的办法,发动项意图时分来指定装备文件的新方位;指定装备文件和默许加载的这些装备文件一起起作用构成互补装备:
java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar –spring.config.location=G:/application.properties

外部装备加载次序

Spring Boot支撑多种外部装备办法,优先级从高到低。高优先级的装备掩盖低优先级的装备,一切的装备会构成互补装备:

  1. 指令行参数
    1. 一切的装备都能够在指令行上进行指定:java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar –server.port=8087 –server.context-path=/abc
    2. 多个装备用空格分开; --装备项=值
  2. 来自java:comp/env的JNDI特点
  3. Java体系特点(System.getProperties())
  4. 操作体系环境变量
  5. RandomValuePropertySource装备的random.*特点值

由jar包外向jar包内进行寻找。优先加载带profile

  1. jar包外部的application-{profile}.properties或application.yml(带spring.profile)装备文件
  2. jar包内部的application-{profile}.properties或application.yml(带spring.profile)装备文件

再来加载不带profile

  1. jar包外部的application.properties或application.yml(不带spring.profile)装备文件
  2. jar包内部的application.properties或application.yml(不带spring.profile)装备文件
  3. @Configuration注解类上的@PropertySource
  4. 经过SpringApplication.setDefaultProperties指定的默许特点

主动装备原理

装备文件能够装备的特点:docs.spring.io/spring-boot…

主动装备原理:
1)Spring Boot发动时加载主装备类(带有@SpringBootApplication),其里边敞开了主动装备功用@EnableAutoConfiguration
2)@EnableAutoConfiguration利用@Import(AutoConfigurationImportSelector.class)给容器导入一些组件。导入的组件是经过List configurations = getCandidateConfigurations(annotationMetadata, attributes);获取到的。里边经过SpringFactoriesLoader.loadFactoryNames 扫描一切jar包类途径下”META-INF/spring.factories”,把扫描到的这些文件的内容包装成properties目标,从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们增加在容器中。其实便是将类途径下 META-INF/spring.factories 里边装备的一切EnableAutoConfiguration的值加入到了容器中。每一个这样的 xxxAutoConfiguration 类都是容器中的一个组件,都加入到容器中;用他们来做主动装备;
3)每一个主动装备类进行主动装备功用
4)以HttpEncodingAutoConfiguration装备类进行剖析:

// 表明这是一个装备类,以前编写的装备文件一样,也能够给容器中增加组件
@AutoConfiguration
// 发动指定类的ConfigurationProperties功用,将装备文件中对应的值和xxxProperties绑定起来,并把xxxProperties加入到ioc容器中
@EnableConfigurationProperties(ServerProperties.class)
// Spring底层@Conditional注解(Spring注解版),依据不同的条件,假如满意指定的条件,整个装备类里边的装备就会收效;
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
// 判别当时项目有没有这个类CharacterEncodingFilter-SpringMVC中进行乱码处理的过滤器
@ConditionalOnClass(CharacterEncodingFilter.class)
// 判别装备文件中是否存在某个装备 spring.servlet.encoding.enabled;假如不存在,判别也是建立的
// 即便咱们装备文件中不装备spring.servlet.encoding.enabled=true,也是默许收效的;
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
    // 他现已和SpringBoot的装备文件映射了
    private final Encoding properties;
    // 只需一个有参构造器的情况下,参数的值就会从容器中拿
    public HttpEncodingAutoConfiguration(ServerProperties properties) {
        this.properties = properties.getServlet().getEncoding();
    }
    @Bean  // 给容器中增加一个组件,这个组件的某些值需求从properties中获取
    @ConditionalOnMissingBean // 判别容器没有这个组件,就给装备一个
    public CharacterEncodingFilter characterEncodingFilter() {
        CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
        filter.setEncoding(this.properties.getCharset().name());
        filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
        filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
        return filter;
    }
	...
}

依据当时不同的条件判别,决议这个装备类是否收效
一但这个装备类收效,这个装备类就会给容器中增加各种组件,这些组件的特点是从对应的properties类中获取的,这些类里边的每一个特点又是和装备文件绑定的
5)、一切在装备文件中能装备的特点都是在xxxxProperties类中封装着,装备文件能装备什么就能够参照某个功用对应的这个特点类

运用精华:
1)、SpringBoot发动会加载许多的主动装备类 ;
2)、咱们看咱们需求的功用有没有SpringBoot默许写好的主动装备类;
3)、咱们再来看这个主动装备类中到底装备了哪些组件(只需咱们要用的组件有,咱们就不需求再来装备了)
4)、给容器中主动装备类增加组件的时分,会从properties类中获取某些特点。咱们就能够在装备文件中指定这些特点的值;
xxxxAutoConfigurartion:主动装备类; 给容器中增加组件
xxxxProperties:封装装备文件中相关特点

@Conditional注解

作用:有必要是@Conditional指定的条件建立,才给容器中增加组件,装备配里边的一切内容才收效
也便是说,主动装备类有必要在必定的条件下才能收效

@Conditional扩展注解 作用(判别是否满意当时指定条件)
@ConditionalOnJava 体系的java版别是否符合要求
@ConditionalOnBean 容器中存在指定Bean
@ConditionalOnMissingBean 容器中不存在指定Bean
@ConditionalOnExpression 满意SpEL表达式指定
@ConditionalOnClass 体系中有指定的类
@ConditionalOnMissingClass 体系中没有指定的类
@ConditionalOnSingleCandidate 容器中只需一个指定的Bean,或许这个Bean是首选Bean
@ConditionalOnProperty 体系中指定的特点是否有指定的值
@ConditionalOnResource 类途径下是否存在指定资源文件
@ConditionalOnWebApplication 当时是web环境
@ConditionalOnNotWebApplication 当时不是web环境
@ConditionalOnJndi JNDI存在指定项

想要检查收效的主动装备类,能够在装备文件中装备debug=true,positive为发动的,negative没启用的

万字SpringBoot学习笔记|菜鸟版

Spring Boot与日志

日志结构

市场上存在十分多的日志结构:JUL(java.util.logging),JCL(Apache Commons Logging),Log4j,Log4j2,Logback、SLF4j、jboss-logging等。 Spring Boot在结构内容部运用JCL,spring-boot-starter-logging选用了 slf4j+logback的办法,Spring Boot也能主动适配(jul、log4j2、logback) 并简化装备
SpringBoot底层是Spring结构,Spring结构默许是用JCL。SpringBoot选用SLF4j(日志笼统层)和logback(日志完成)

SLF4j

开发时日志记载办法的调用,不该该来直接调用日志的完成类,而是调用日志笼统层里边的办法:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
  public static void main(String[] args) {
    Logger logger = LoggerFactory.getLogger(HelloWorld.class);
    logger.info("Hello World");
  }
}

每一个日志的完成结构都有自己的装备文件。运用slf4j今后,装备文件还是做成日志完成结构自己自身的装备文件。
怎么让体系中一切的日志都一致到slf4j:

  1. 将体系中其他日志结构先排除出去
  2. 用中间包来替换原有的日志结构
  3. 咱们导入slf4j其他的完成

SpringBoot日志联系

增加依靠:

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

万字SpringBoot学习笔记|菜鸟版

SpringBoot能主动适配一切的日志,而且底层运用slf4j+logback的办法记载日志,引进其他结构的时分,只需求把这个结构依靠的日志结构排除去即可

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring‐core</artifactId>
    <exclusions>
        <exclusion>
            <groupId>commons‐logging</groupId>
            <artifactId>commons‐logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>

默许日志装备

日志级别由低到高:trace<debug<info<warn<error
SpringBoot默许给咱们运用的是info级别的(日志就只会在这个级别及今后的高档别收效),没有指定级别的就用SpringBoot默许规矩的级别。
日志输出格局:

  • %d –表明日期时刻
  • %thread –表明线程名
  • %‐5level –级别从左显现5个字符宽度
  • %logger{50} –表明logger姓名最长50个字符,不然依照句点分割
  • %msg –日志音讯
  • %n –是换行符
public class Hello {
    // 记载器
    Logger logger = LoggerFactory.getLogger(getClass());
    @RequestMapping("/world")
    public String hello() {
        logger.trace("trace日志");
        logger.debug("debug日志");
        // 默许
        logger.info("info日志");
        logger.warn("warn日志");
        logger.error("error日志");
        return "Hello World~";
    }
}
默许是info,所以只会输出:
2023-05-23 11:44:27.419 INFO 98527 --- [nio-8080-exec-2] c.s.spring_boot_demo3.controller.Hello   : info日志
2023-05-23 11:44:27.419 WARN 98527 --- [nio-8080-exec-2] c.s.spring_boot_demo3.controller.Hello   : warn日志
2023-05-23 11:44:27.419 ERROR 98527 --- [nio-8080-exec-2] c.s.spring_boot_demo3.controller.Hello   : error日志

修正默许级别:

logging.level.com.study=trace
2023-05-23 11:50:00.774 TRACE 98971 --- [nio-8080-exec-1] c.s.spring_boot_demo3.controller.Hello   : trace日志
2023-05-23 11:50:00.774 DEBUG 98971 --- [nio-8080-exec-1] c.s.spring_boot_demo3.controller.Hello   : debug日志
2023-05-23 11:50:00.774  INFO 98971 --- [nio-8080-exec-1] c.s.spring_boot_demo3.controller.Hello   : info日志
2023-05-23 11:50:00.774  WARN 98971 --- [nio-8080-exec-1] c.s.spring_boot_demo3.controller.Hello   : warn日志
2023-05-23 11:50:00.774 ERROR 98971 --- [nio-8080-exec-1] c.s.spring_boot_demo3.controller.Hello   : error日志

日志装备:

logging.file.name(建议) logging.file.path 比如 补白
只在操控台输出
指定文件名 my.log 输出日志到my.log文件
指定目录 /var/log 输出到指定目录的 spring.log 文件中
# 日志
# logging.file.name=my.log
# 装备日志途径,默许在此目录下生成一个名为:spring.log的日志文件
logging.file.path=/test/log
# 在操控台输出的日志的格局
logging.pattern.console=%d{yyyy‐MM‐dd}[%thread]%‐5level%logger{50}‐%msg%n
# 指定文件中日志输出的格局
logging.pattern.file=%d{yyyy‐MM‐dd}===[%thread]===%‐5level===%logger{50}====%msg%n

指定日志装备

给类途径下放上每个日志结构自己的装备文件即可,SpringBoot就不运用他默许装备的了

日志体系 自界说装备文件
Logback logback-spring.xml , logback-spring.groovy, logback.xml or logback.groovy
Log4j2 log4j2-spring.xml or log4j2.xml
JDK (Java Util Logging) logging.properties

logback.xml:直接就被日志结构识别了;
logback-spring.xml:日志结构就不直接加载日志的装备项,由SpringBoot解析日志装备,能够运用SpringBoot的高档Profile功用(激活对应环境下收效)

<springProfilename="staging">
  <!‐‐ configuration to be enabled when the "staging" profile is active ‐‐>
  能够指定某段装备只在某个环境下收效
</springProfile>
<appendername="stdout" class="ch.qos.logback.core.ConsoleAppender">
  <layout class="ch.qos.logback.classic.PatternLayout">
    <springProfile name="dev">
      <pattern>%d{yyyy‐MM‐dd HH:mm:ss.SSS} ‐‐‐‐> [%thread] ‐‐‐> %‐5level
        %logger{50} ‐ %msg%n</pattern>
    </springProfile>
    <springProfile name="!dev">
      <pattern>%d{yyyy‐MM‐dd HH:mm:ss.SSS} ==== [%thread] ==== %‐5level
        %logger{50} ‐ %msg%n</pattern>
    </springProfile>
  </layout>
</appender>

Spring Boot与Web开发

静态资源映射规矩

1)一切 /webjars/** ,都去 classpath:/META-INF/resources/webjars/ 找资源

webjars:是以jar包的办法引进静态资源(网址:www.webjars.org/)


万字SpringBoot学习笔记|菜鸟版

引进后拜访:http://localhost:8080/webjars/jquery/3.3.1/src/jquery.js,就能够找到资源:
万字SpringBoot学习笔记|菜鸟版

2) /** 拜访当时项意图任何资源,都去「静态资源的文件夹」找映射

  • “classpath:/META‐INF/resources/”
  • “classpath:/resources/”
  • “classpath:/static/”
  • “classpath:/public/”
  • “/”:当时项意图根途径

如,localhost:8080/abc,会去静态资源文件夹里边找abc

万字SpringBoot学习笔记|菜鸟版

3)主页映射,静态资源文件夹下的一切index.html页面,被”/**”映射
localhost:8080/ ,会找index页面
万字SpringBoot学习笔记|菜鸟版

4)一切的 **/favicon.ico 都是在静态资源文件下找

Thymeleaf模板引擎

thymeleaf运用

默许规矩:只需咱们把HTML页面放在classpath:/templates/,thymeleaf就能主动烘托

// 源码
@ConfigurationProperties(prefix="spring.thymeleaf")
public class ThymeleafProperties{
	private static final Charset DEFAULT_ENCODING = Charset.forName("UTF‐8");
	private static final MimeType DEFAULT_CONTENT_TYPE = MimeType.valueOf("text/html");
	public static final String DEFAULT_PREFIX = "classpath:/templates/";
	public static final String DEFAULT_SUFFIX = ".html";

第一步)增加依靠

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

第二步)特点装备

# 将缓存封闭
spring.thymeleaf.cache=false

第三步)创立thymeleaf模板文件
创立success.html,放入classpath:/templates/文件夹下

<!DOCTYPE html>
<!-- 导入thymeleaf的称号空间 -->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h2>成功页面!</h2>
		<!-- th:text将div里边的文本内容设置为name代表的数据 -->
    <div th:text="${name}"></div>
</body>
</html>

第四步)编写操控器

// 这儿需求运用@Controller,而不是@RestController
@Controller
@RequestMapping("/api")
public class Hello {
    @ResponseBody
    @RequestMapping("/hello")
    public String hello() {
        return "hello";
    }
    @RequestMapping("/success")
    public String success(Model model) {
        // classpath:/templates/success.html
        model.addAttribute("name","alice");
        return "success";
    }
}

第五步)拜访页面
拜访http://localhost:8080/api/success,能够看到html页面内容

thymeleaf语法规矩

1)th:text:改动当时元素里边的文本内容(th:任意html特点:来替换原生特点的值)

万字SpringBoot学习笔记|菜鸟版

2)表达式

【 Simpleexpressions:(表达式语法) 】
    1、Variable Expressions: ${...}:获取变量值(OGNL)
        1)、获取目标的特点、调用办法
        2)、运用内置的根本目标:
            #ctx : the context object.
            #vars: the context variables.
            #locale : the context locale.
            #request : (only in Web Contexts) the HttpServletRequest object.
            #response : (only in Web Contexts) the HttpServletResponse object.
            #session : (only in Web Contexts) the HttpSession object.
            #servletContext : (only in Web Contexts) the ServletContext object.
        3)、内置的一些东西目标:
            #execInfo : information about the template being processed.
            #messages : methods for obtaining externalized messages inside variables expressions, in the same way as they would be obtained using #{...} syntax.
            #uris : methods for escaping parts of URLs/URIs
            #conversions : methods for executing the configured conversion service (if any).
            #dates : methods for java.util.Date objects: formatting, component extraction, etc.
            #calendars : analogous to #dates , but for java.util.Calendar objects.
            #numbers : methods for formatting numeric objects.
            #strings : methods for String objects: contains, startsWith, prepending/appending, etc.
            #objects : methods for objects in general.
            #bools : methods for boolean evaluation.
            #arrays : methods for arrays.
            #lists : methods for lists.
            #sets : methods for sets.
            #maps : methods for maps.
            #aggregates : methods for creating aggregates on arrays or collections.
            #ids : methods for dealing with id attributes that might be repeated (for example, as aresult of an iteration).
        2、Selection Variable Expressions: *{...}:挑选表达式。和${}在功用上是一样(弥补:合作th:object="${session.user})
       	比如:
        <div th:object="${session.user}">
            <p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
            <p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
            <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
        </div>
        3、Message Expressions: #{...}:获取国际化内容 
        4、Link URL Expressions: @{...}:界说URL;
        比如:@{/order/process(execId=${execId},execType='FAST')}
        5、Fragment Expressions: ~{...}:片段引证表达式
        比如:<div th:insert="~{commons :: main}">...</div>
【 Literals(字面量) 】
    Text literals: 'one text' , 'Another one!' ,... 
    Number literals: 0 , 34 , 3.0 , 12.3 ,... 
    Boolean literals: true , false
    Null literal: null
    Literal tokens: one , sometext , main ,...
【Text operations:(文本操作)】
    String concatenation: +
    Literal substitutions: |The name is ${name}|
【Arithmetic operations:(数学运算)】
    Binary operators: + , ‐ , * , / , %
    Minus sign (unary operator): ‐
【Booleanoperations:(布尔运算)】
    Binary operators: and , or
    Boolean negation (unary operator): ! , not
【Comparisonsandequality:(比较运算)】
    Comparators: > , < , >= , <= ( gt , lt , ge , le )
    Equality operators: == , != ( eq , ne )
【Conditionaloperators:条件运算(三元运算符)】
    If‐then: (if) ? (then)
    If‐then‐else: (if) ? (then) : (else)
    Default: (value) ?: (defaultvalue)
【Specialtokens:(特殊操作) 】
    No‐Operation: _  代表空操作,如在三元运算符的冒号后边运用

SpringMVC主动装备

SpringMVC主动装备

Spring Boot 主动装备好了SpringMVC。以下是SpringBoot对SpringMVC的默许装备(WebMvcAutoConfiguration):

  • Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans
    • 主动装备了ViewResolver(视图解析器:依据办法的回来值得到视图目标(View),视图目标决议怎么烘托(转发或重定向))
    • ContentNegotiatingViewResolver:组合一切的视图解析器的
    • 假如需求定制视图解析器:咱们能够自己给容器中增加一个视图解析器,ContentNegotiatingViewResolver会主动的将其组合进来;
  • Support for serving static resources, including support for WebJars
    • 静态资源文件夹途径、webjars
  • Static index.html support
    • 静态主页拜访
  • Custom Favicon support
    • favicon.ico
  • 主动注册了 Converter , GenericConverter , Formatter beans.
    • Converter:转换器,public String hello(User user):类型转换运用Converter
    • Formatter:格局化器,2017.12.17===Date;
    • 自己增加的格局化器转换器,咱们只需求放在容器中即可(运用@Bean)
  • Support for HttpMessageConverters
    • HttpMessageConverter:SpringMVC用来转换Http恳求和呼应的;User—Json;
    • HttpMessageConverters是从容器中确定,获取一切的HttpMessageConverter;
    • 自己给容器中增加HttpMessageConverter,只需求将自己的组件注册容器中(**@Bean 或 @Component**)
  • Automatic registration of MessageCodesResolver:界说过错代码生成规矩
  • Automatic use of a ConfigurableWebBindingInitializer bean
    • 初始化web数据绑定器的,WebDataBinder。他的作用是把恳求数据转换为JavaBean
    • 咱们能够装备一个ConfigurableWebBindingInitializer来替换默许的;(增加到容器)

扩展SpringMVC

扩展办法:编写一个装备类(@Configuration),是WebMvcConfigurerAdapter类型,不能标示@EnableWebMvc。
既保留了一切的主动装备,也能用咱们扩展的装备(SpringMVC的主动装备和咱们的扩展装备都会起作用)

// 运用WebMvcConfigurerAdapter能够来扩展SpringMVC的功用
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
    // 增加视图映射
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // super.addViewControllers(registry);
        // 浏览器发送 /other 恳求来到 success
        registry.addViewController("/other").setViewName("success");
    }
}

浏览器拜访 http://localhost:8080/other , 能够看到成功映射到了success页面

全面接收SpringMVC

@EnableWebMvc(不引荐运用)
SpringBoot对SpringMVC的主动装备不需求了,一切都是咱们自己装备。一切的SpringMVC的主动装备都失效了。咱们需求在装备类中增加@EnableWebMvc即可

// 运用WebMvcConfigurerAdapter能够来扩展SpringMVC的功用
@Configuration
// 全面接收SpringMVC
@EnableWebMvc
public class MyMvcConfig extends WebMvcConfigurerAdapter {
    // 增加视图映射
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // super.addViewControllers(registry);
        // 浏览器发送 /other 恳求来到 success
        registry.addViewController("/other").setViewName("success");
    }
}

接收前经过http://localhost:8080/static.html能够拜访静态页面,全面接收后静态页面的规矩就失效了,咱们就无法直接拜访了

修正SpringBoot的默许装备

形式:
1)、SpringBoot在主动装备许多组件的时分,先看容器中有没有用户自己装备的 (@Bean、@Component)。假如有就用用户装备的,假如没有才主动装备。假如有些组件能够有多个(如ViewResolver),则将用户装备的和自己默许的组合起来;
2)、在SpringBoot中会有十分多的xxxConfigurer协助咱们进行扩展装备
3)、在SpringBoot中会有许多的xxxCustomizer协助咱们进行定制装备

CRUD事例

万字SpringBoot学习笔记|菜鸟版

1)默许拜访主页

办法1:在controller中增加拜访途径的匹配规矩

@RequestMapping({"/", "/index.html"})
public String index() {
    return "index";
}

办法2:在装备类中注册组件到容器

// 运用WebMvcConfigurerAdapter能够来扩展SpringMVC的功用
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
    // 增加视图映射
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/other").setViewName("success");
    }
    // 一切的WebMvcConfigurerAdapter组件都会一起起作用
    @Bean //将组件注册在容器
    public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){
        WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {
            @Override
            public void addViewControllers(ViewControllerRegistry registry) {
                registry.addViewController("/").setViewName("login");
                registry.addViewController("/index.html").setViewName("login");
            }
        };
        return adapter;
    }
}

2)国际化

第一步:编写国际化装备文件,抽取页面需求显现的国际化音讯

万字SpringBoot学习笔记|菜鸟版

第二步:SpringBoot主动装备好了办理国际化资源文件的组件
咱们的装备文件能够直接放在类途径下叫messages.properties,或许在application.properties里装备途径

# 国际化装备的途径
spring.messages.basename=i18n.login

第三步:运用#{}能够在页面上获取国际化的值

<!DOCTYPE html>
<html lang="en"  xmlns:th="http://www.thymeleaf.org">
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
		<meta name="description" content="">
		<meta name="author" content="">
		<title>Signin Template for Bootstrap</title>
		<!-- Bootstrap core CSS -->
		<link href="asserts/css/bootstrap.min.css" th:href="@{/webjars/bootstrap/3.3.5/css/bootstrap.css}" rel="stylesheet">
		<!-- Custom styles for this template -->
		<link href="asserts/css/signin.css" th:href="@{/asserts/css/signin.css}" rel="stylesheet">
	</head>
	<body class="text-center">
		<form class="form-signin" action="dashboard.html" th:action="@{/user/login}" method="post">
			<img class="mb-4" th:src="@{/asserts/img/bootstrap-solid.svg}" src="asserts/img/bootstrap-solid.svg" alt="" width="72" height="72">
			<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>
			<!--判别-->
			<p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
			<label class="sr-only" th:text="#{login.username}">Username</label>
			<input type="text"  name="username" class="form-control" placeholder="Username" th:placeholder="#{login.username}" required="" autofocus="">
			<label class="sr-only" th:text="#{login.password}">Password</label>
			<input type="password" name="password" class="form-control" placeholder="Password" th:placeholder="#{login.password}" required="">
			<div class="checkbox mb-3">
				<label>
          			<input type="checkbox" value="remember-me"/> [[#{login.remember}]]
        		</label>
			</div>
			<button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.btn}">Sign in</button>
			<p class="mt-5 mb-3 text-muted"> 2017-2018</p>
			<a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a>
			<a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>
		</form>
	</body>
</html>

第四步:点击链接切换国际化

万字SpringBoot学习笔记|菜鸟版

自己完成一个LocaleResolver,然后在装备类中注册组件到容器

@Bean
public LocaleResolver localeResolver(){
    return new MyLocaleResolver();
}

完成作用:

万字SpringBoot学习笔记|菜鸟版
万字SpringBoot学习笔记|菜鸟版

3)登录

@Controller
public class Login {
    // @RequestMapping(value = "/user/login", method = RequestMethod.POST)
    @PostMapping(value = "/user/login")
    public String login(@RequestParam("username") String username,
                        @RequestParam("password") String password,
                        Map<String, Object> map, HttpSession session) {
        if (!StringUtils.isEmpty(username) && "1234".equals(password)) {
            session.setAttribute("loginUser", username);
            return "redirect:/main.html";
        } else {
            map.put("msg", "用户名暗码过错");
            return "login";
        }
    }
}

装备类中增加一个试图映射

registry.addViewController("/main.html").setViewName("dashboard");

过错音讯显现:

<pstyle="color:red"th:text="${msg}"th:if="${not#strings.isEmpty(msg)}"></p>

4)拦截器进行登录检查

拦截器

// 登陆检查
public class LoginHandlerInterceptor implements HandlerInterceptor {
    //目标办法履行之前
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Object user = request.getSession().getAttribute("loginUser");
        if(user == null){
            //未登陆,回来登陆页面
            request.setAttribute("msg","没有权限请先登陆");
            request.getRequestDispatcher("/index.html").forward(request,response);
            return false;
        }else{
            //已登陆,放行恳求
            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 {}
}

注册拦截器

// 一切的WebMvcConfigurerAdapter组件都会一起起作用
@Bean //将组件注册在容器
public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){
    WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/").setViewName("login");
            registry.addViewController("/index.html").setViewName("login");
            registry.addViewController("/main.html").setViewName("dashboard");
        }
        //注册拦截器
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            //super.addInterceptors(registry);
            // 静态资源; *.css , *.js
            // SpringBoot现已做好了静态资源映射
            registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**")
                    .excludePathPatterns("/index.html","/","/user/login");
        }
    };
    return adapter;
}

5)职工列表

三种引进公共片段的th特点:

  • th:insert:将公共片段整个插入到声明引进的元素中
  • th:replace:将声明引进的元素替换为公共片段
  • th:include:将被引进的片段的内容包含进这个标签中
<body>
  <!--引进抽取的topbar-->
  <!--模板名:会运用thymeleaf的前后缀装备规矩进行解析-->
  <div th:replace="commons/bar::topbar"></div>
  <div class="container-fluid">
    <div class="row">
      <!--引进侧边栏-->
      <div th:replace="commons/bar::#sidebar(activeUri='emps')"></div>
      <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
        <h2><a class="btn btn-sm btn-success" href="emp" th:href="@{/emp}">职工增加</a></h2>
        <div class="table-responsive">
          <table class="table table-striped table-sm">
            <thead>
              <tr>
                <th>#</th>
                <th>lastName</th>
                <th>email</th>
                <th>gender</th>
                <th>department</th>
                <th>birth</th>
                <th>操作</th>
              </tr>
            </thead>
            <tbody>
              <tr th:each="emp:${emps}">
                <td th:text="${emp.id}"></td>
                <td>[[${emp.lastName}]]</td>
                <td th:text="${emp.email}"></td>
                <td th:text="${emp.gender}==0?'女':'男'"></td>
                <td th:text="${emp.department.departmentName}"></td>
                <td th:text="${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')}"></td>
                <td>
                  <a class="btn btn-sm btn-primary" th:href="@{/emp/}+${emp.id}">编辑</a>
                  <button th:attr="del_uri=@{/emp/}+${emp.id}" class="btn btn-sm btn-danger deleteBtn">删去</button>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      </main>
      <form id="deleteEmpForm"  method="post">
        <input type="hidden" name="_method" value="delete"/>
      </form>
    </div>
  </div>
  <!-- Bootstrap core JavaScript
  ================================================== -->
  <!-- Placed at the end of the document so the pages load faster -->
  <script type="text/javascript" src="asserts/js/jquery-3.2.1.slim.min.js" th:src="@{/webjars/jquery/3.3.1/jquery.js}"></script>
  <script type="text/javascript" src="asserts/js/popper.min.js" th:src="@{/webjars/popper.js/1.11.1/dist/popper.js}"></script>
  <script type="text/javascript" src="asserts/js/bootstrap.min.js" th:src="@{/webjars/bootstrap/4.0.0/js/bootstrap.js}"></script>
  <!-- Icons -->
  <script type="text/javascript" src="asserts/js/feather.min.js" th:src="@{/asserts/js/feather.min.js}"></script>
  <script>
    feather.replace()
  </script>
  <script>
    $(".deleteBtn").click(function(){
        //删去当时职工的
        $("#deleteEmpForm").attr("action",$(this).attr("del_uri")).submit();
        return false;
          });
  </script>
</body>

6)职工增加

恳求增加页面

//来到职工增加页面
@GetMapping("/emp")
public String toAddPage(Model model){
    //来到增加页面,查出一切的部分,在页面显现
    Collection<Department> departments = departmentDao.getDepartments();
    model.addAttribute("depts",departments);
    return "emp/add";
}

增加页面的表单(见下方修正)
职工增加功用

//职工增加
//SpringMVC主动将恳求参数和入参目标的特点进行逐个绑定;要求恳求参数的姓名和javaBean入参的目标里边的特点名是一样的
@PostMapping("/emp")
public String addEmp(Employee employee){
    //来到职工列表页面
    System.out.println("保存的职工信息:"+employee);
    //保存职工
    employeeDao.save(employee);
    // redirect: 表明重定向到一个地址  /代表当时项目途径
    // forward: 表明转发到一个地址
    return "redirect:/emps";
}

7)职工修正

查询职工信息并回显

//来到修正页面,查出当时职工,在页面回显
@GetMapping("/emp/{id}")
public String toEditPage(@PathVariable("id") Integer id,Model model){
    Employee employee = employeeDao.get(id);
    model.addAttribute("emp",employee);
    //页面要显现一切的部分列表
    Collection<Department> departments = departmentDao.getDepartments();
    model.addAttribute("depts",departments);
    //回到修正页面(add是一个修正增加二合一的页面);
    return "emp/add";
}

修正和增加是同一个页面:

<!--需求区别是职工修正还是增加;-->
<form th:action="@{/emp}" method="post">
    <!--发送put恳求修正职工数据-->
    <!--
    1、SpringMVC中装备HiddenHttpMethodFilter;(SpringBoot主动装备好的)
    2、页面创立一个post表单
    3、创立一个input项,name="_method";值便是咱们指定的恳求办法
    -->
    <input type="hidden" name="_method" value="put" th:if="${emp!=null}"/>
    <input type="hidden" name="id" th:if="${emp!=null}" th:value="${emp.id}">
    <div class="form-group">
        <label>LastName</label>
        <input name="lastName" type="text" class="form-control" placeholder="zhangsan" th:value="${emp!=null}?${emp.lastName}">
    </div>
    <div class="form-group">
        <label>Email</label>
        <input name="email" type="email" class="form-control" placeholder="zhangsan@atguigu.com" th:value="${emp!=null}?${emp.email}">
    </div>
    <div class="form-group">
        <label>Gender</label><br/>
        <div class="form-check form-check-inline">
            <input class="form-check-input" type="radio" name="gender" value="1" th:checked="${emp!=null}?${emp.gender==1}">
            <label class="form-check-label">男</label>
        </div>
        <div class="form-check form-check-inline">
            <input class="form-check-input" type="radio" name="gender" value="0" th:checked="${emp!=null}?${emp.gender==0}">
            <label class="form-check-label">女</label>
        </div>
    </div>
    <div class="form-group">
        <label>department</label>
        <!--提交的是部分的id-->
        <select class="form-control" name="department.id">
            <option th:selected="${emp!=null}?${dept.id == emp.department.id}" th:value="${dept.id}" th:each="dept:${depts}" th:text="${dept.departmentName}">1</option>
        </select>
    </div>
    <div class="form-group">
        <label>Birth</label>
        <input name="birth" type="text" class="form-control" placeholder="zhangsan" th:value="${emp!=null}?${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')}">
    </div>
    <button type="submit" class="btn btn-primary" th:text="${emp!=null}?'修正':'增加'">增加</button>
</form>

修正功用

//职工修正;需求提交职工id;
@PutMapping("/emp")
public String updateEmployee(Employee employee){
    System.out.println("修正的职工数据:"+employee);
    employeeDao.save(employee);
    return "redirect:/emps";
}

8)职工删去

<button th:attr="del_uri=@{/emp/}+${emp.id}" class="btn btn-sm btn-danger deleteBtn">删去</button>
<script>
	$(".deleteBtn").click(function(){ //删去当时职工。先修正action地址再提交
        $("#deleteEmpForm").attr("action",$(this).attr("del_uri")).submit();
        return false;
    });
</script>

删去功用办法:

//职工删去
@DeleteMapping("/emp/{id}")
public String deleteEmployee(@PathVariable("id") Integer id){
    employeeDao.delete(id);
    return "redirect:/emps";
}

过错处理机制

默许处理

浏览器回来一个默许的过错页面

Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Sun Jun 11 10:32:29 CST 2023
There was an unexpected error (type=Not Found, status=404).

客户端恳求默许回来JSON数据提示过错

{
    "timestamp": "2023-06-11T02:37:03.631+00:00",
    "status": 404,
    "error": "Not Found",
    "path": "/hello1"
}

一但体系呈现4xx或许5xx之类的过错,ErrorPageCustomizer就会收效(定制过错的呼应规矩),就会来到/error恳求,就会被BasicErrorController处理

@Value("${error.path:/error}")
private String path = "/error";
// 体系呈现过错今后来到error恳求进行处理;(web.xml注册的过错页面规矩)
private ModelAndView resolve(String viewName, Map<String, Object> model) {
    //默许SpringBoot能够去找到一个页面? error/404
	String errorViewName = "error/" + viewName;
	//模板引擎能够解析这个页面地址就用模板引擎解析
	TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
            .getProvider(errorViewName, this.applicationContext);
    if (provider != null) {
		//模板引擎可用的情况下回来到errorViewName指定的视图地址
        return new ModelAndView(errorViewName, model);
    }
	//模板引擎不可用,就在静态资源文件夹下找errorViewName对应的页面 error/404.html
    return resolveResource(errorViewName, model);
}

定制过错页面

1)有模板引擎的情况下:error/状况码。将过错页面命名为 过错状况码.html 放在模板引擎文件夹里边的error文件夹下,发生此状况码的过错就会来到对应的页面;
咱们能够运用 4xx5xx 作为过错页面的文件名来匹配这种类型的一切过错。精确优先(优先寻找精确的状况码.html)
页面能获取的信息:timestamp:时刻戳、status:状况码、error:过错提示、exception:反常目标、message:反常音讯、errors:JSR303数据校验的过错都在这儿
2)没有模板引擎(模板引擎找不到这个过错页面),静态资源文件夹下找;
3)以上都没有过错页面,便是默许来到SpringBoot默许的过错提示页面;

万字SpringBoot学习笔记|菜鸟版

定制过错JSON数据

1、办法1:自界说反常处理&回来定制json数据

@ControllerAdvice
public class MyExceptionHandler {
    @ResponseBody
    @ExceptionHandler({UserNotExistException.class})
    public Map<String, Object> handleException(Exception e) {
        Map<String, Object> map = new HashMap<>();
        map.put("code", "user.notexist");
        map.put("message", e.getMessage());
        return map;
    }
}

缺点:没有自适应作用(浏览器和客户端恳求回来的都是JSON数据)

万字SpringBoot学习笔记|菜鸟版
万字SpringBoot学习笔记|菜鸟版

2、转发到/error进行自适应呼应作用处理

@ControllerAdvice
public class MyExceptionHandler {
    @ExceptionHandler({UserNotExistException.class})
    public String handleException(Exception e, HttpServletRequest request) {
        Map<String, Object> map = new HashMap<>();
        request.setAttribute("javax.servlet.error.status_code", 500);
        map.put("code", "user.notexist");
        map.put("message", e.getMessage());
        // 转发到 /error
        return "forward:/error";
    }
}

装备嵌入式Servlet容器

SpringBoot默许运用Tomcat作为嵌入式的Servlet容器

定制修正Servlet容器的相关装备

办法1:修正和server有关的装备(ServerProperties【本质也是EmbeddedServletContainerCustomizer】)

server.port=8081
server.context‐path=/crud
server.tomcat.uri‐encoding=UTF‐8
//通用的Servlet容器设置
server.xxx
//Tomcat的设置
server.tomcat.xxx

办法2:编写一个EmbeddedServletContainerCustomizer:嵌入式的Servlet容器的定制器,来修正Servlet容器的装备

@Bean //必定要将这个定制器加入到容器中
publicEmbeddedServletContainerCustomizerembeddedServletContainerCustomizer(){
    return new EmbeddedServletContainerCustomizer() {
        //定制嵌入式的Servlet容器相关的规矩
        @Override
        public void customize(ConfigurableEmbeddedServletContainer container) {
            container.setPort(8083);
        }
    };
}

注册Servlet三大组件

因为SpringBoot默许是以jar包的办法发动嵌入式的Servlet容器来发动SpringBoot的web运用,没有web.xml文件。 注册三大组件(Servlet、Filter、Listener)用以下办法:
ServletRegistrationBean

@Configuration
public class MyServerConfig {
    // 注册三大组件
    @Bean
    public ServletRegistrationBean myServlet(){
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(new MyServlet(), "/myServlet");
        return registrationBean;
    }
}
// 恳求 http://localhost:8080/myServlet  就会呈现MyServlet中回来的内容

FilterRegistrationBean

@Bean
public FilterRegistrationBean myFilter(){
    FilterRegistrationBean registrationBean = new FilterRegistrationBean();
    registrationBean.setFilter(new MyFilter());
    registrationBean.setUrlPatterns(Arrays.asList("/hello", "/myServlet"));
    return registrationBean;
}
// 恳求 http://localhost:8080/myServlet  就会呈现MyFilter的doFilter()中输出的内容

ServletListenerRegistrationBean

@Bean
public ServletListenerRegistrationBean myListener(){
    ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean<>(new MyListener());
    return registrationBean;
}
// 输出成果:
contextInitialized---web运用发动
contextDestroyed---web运用封闭  // 这儿是在点击暂停的时分

其他嵌入式Servlet容器

Tomcat(默许运用)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring‐boot‐starter‐web</artifactId>
    // 引进web模块默许便是运用嵌入式的Tomcat作为Servlet容器;
</dependency>

Jetty(开发长连接运用)

<dependency>
    <artifactId>spring‐boot‐starter‐jetty</artifactId>
    <groupId>org.springframework.boot</groupId>
 </dependency>

Undertow(不支撑JSP)

<dependency>
   <artifactId>spring‐boot‐starter‐undertow</artifactId>
   <groupId>org.springframework.boot</groupId>
</dependency>

Spring Boot与Docker

Docker简介

Docker是一个开源的运用容器引擎,是一个轻量级容器技能。Docker支撑将软件编译成一个镜像,然后在镜像中各种软件做好装备,将镜像发布出去,其他运用者能够直接运用这个镜像;运转中的这个镜像称为容器,容器发动是十分快速的。

  • docker镜像(Images):Docker镜像是用于创立Docker容器的模板
  • docker容器(Container):容器是独立运转的一个或一组运用
  • docker客户端(Client):客户端经过指令行或许其他东西运用Docker API(docs.docker.com/reference/a…) 与 Docker 的看护进程通信,即:连接docker主机进行操作
  • docker主机(Host):一个物理或许虚拟的机器用于履行Docker看护进程和容器。即:装置了Docker程序的机器(Docker直接装置在操作体系之上)
  • docker库房(Registry):Docker库房用来保存镜像,能够理解为代码操控中的代码库房。Docker Hub(hub.docker.com) 供给了巨大的镜像集合供运用。

运用Docker的过程:
1)装置Docker
2)去Docker库房找到这个软件对应的镜像
3)运用Docker运转这个镜像,这个镜像就会生成一个Docker容器
4)对容器的发动中止便是对软件的发动中止

装置Docker

装置教程可参阅:www.runoob.com/docker/maco…

万字SpringBoot学习笔记|菜鸟版
$ docker info
万字SpringBoot学习笔记|菜鸟版

Docker常用操作

镜像操作

操作 指令 阐明
检索 docker search 关键字
eg:docker search redis
咱们经常去docker hub上检索镜像的详细信息,如镜像的TAG
拉取 docker pull 镜像名:tag :tag是可选的,tag表明标签,多为软件的版别,默许是latest
列表 docker images 检查一切本地镜像
删去 docker rmi image-id 删去指定的本地镜像

容器操作

流程:软件镜像(QQ装置程序)–>运转镜像–>发生一个容器(正在运转的软件,运转的QQ)

操作 指令 阐明
运转 docker run –name container-name -d image-name
eg:docker run –name myredis –d redis
-name:自界说容器名
-d:后台运转
image-name:指定镜像模板
列表 docker ps(检查运转中的容器) 加上-a能够检查一切容器
中止 docker stop container-name/container-id 中止当时运转的容器
发动 docker start container-name/container-id 发动容器
删去 docker rm container-id 删去指定容器
端口映射 -p 6379:6379
eg:docker run -d -p 6379:6379 –name myredis docker.io/redis
-p: 主机端口(映射到)容器内部的端口
‐d:后台运转
容器日志 docker logs container-name/container-id

更多指令可检查:docs.docker.com/engine/refe…

示例(tomcat):

% docker images //检查镜像列表
% docker search tomcat //搜索镜像
% docker pull tomcat //拉取镜像
% docker run --name myTomcat -d tomcat:latest //依据镜像发动容器
% docker ps //检查运转中的容器
    ------输出------
    CONTAINER ID   IMAGE           COMMAND             CREATED          STATUS          PORTS      NAMES
    700a4fa11db6   tomcat:latest   "catalina.sh run"   25 seconds ago   Up 24 seconds   8080/tcp   myTomcat
% docker stop 700a4fa11db6[容器ID] //中止运转中的容器
% docker ps -a //检查一切的容器
    ------输出------
    CONTAINER ID   IMAGE           COMMAND             CREATED         STATUS                            PORTS     NAMES
    700a4fa11db6   tomcat:latest   "catalina.sh run"   5 minutes ago   Exited (143) About a minute ago             myTomcat
% docker start 700a4fa11db6[容器ID] //发动容器
% docker rm 700a4fa11db6[容器ID] //删去一个容器
% docker run -d -p 8888:8080 tomcat //发动一个做了端口映射的tomcat
    ‐d:后台运转
    ‐p: 将主机的端口映射到容器的一个端口 主机端口:容器内部的端口
    ------docker ps 输出------
    CONTAINER ID   IMAGE     COMMAND             CREATED          STATUS          PORTS                    NAMES
	8dbc9df132b4   tomcat    "catalina.sh run"   19 seconds ago   Up 19 seconds   0.0.0.0:8888->8080/tcp   eloquent_moore
% dockerlogscontainer‐name/container‐id //检查容器的日志

示例(mysql):

% docker pull mysql
% docker run --name mysql01 -e MYSQL_ROOT_PASSWORD=123456 -d mysql //发动mysql
    ------输出------
	c9c10a720ba86f440737503396019c80ad0de88b8ae659e19214d8eda3253481
几个其他的高档操作:
docker run --name mysql03 ‐v /conf/mysql:/etc/mysql/conf.d ‐e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
把主机的/conf/mysql文件夹挂载到mysql docker容器的/etc/mysql/conf.d文件夹里边
改mysql的装备文件就只需求把mysql装备文件放在自界说的文件夹下(/conf/mysql)
docker run --name some‐mysql ‐e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag --character‐set‐server=utf8mb4 ‐‐collation‐server=utf8mb4 --collation -server=utf8mb4_unicode_ci
指定mysql的一些装备参数

Spring Boot与数据拜访

对于数据拜访层,无论是SQL还是NOSQL,Spring Boot默许选用整合Spring Data的办法进行一致处理,增加许多主动装备,屏蔽了许多设置。引进各种xxxTemplate、xxxRepository来简化咱们对数据拜访层的操作。对咱们来说只需求进行简略的设置即可。
JDBC、MyBatis、JPA

整合JDBC

万字SpringBoot学习笔记|菜鸟版

装备:

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/jdbc
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

测验代码:

@SpringBootTest
class SpringDemo08JdbcApplicationTests {
    @Autowired
    DataSource dataSource;
    @Test
    void contextLoads() {
        // 默许运用的是 class com.zaxxer.hikari.HikariDataSource 数据源
        System.out.println(dataSource.getClass());
    }
}

数据源的相关装备都在DataSourceProperties源代码里边

// 源码
@ConfigurationProperties(
    prefix = "spring.datasource"
)

SpringBoot默许能够支撑:org.apache.tomcat.jdbc.pool.DataSource、HikariDataSource、BasicDataSource、自界说数据源类型。

验证JDBC

装备文件里增加如下装备:

#spring.datasource.initialization-mode=always  此行已失效,运用下面的
spring.sql.init.mode=always

编写SQL并放在resources文件夹下面

万字SpringBoot学习笔记|菜鸟版

发动springboot工程,改写数据库,能够看到表成功创立(下次发动还是会创立,所以最好创立结束后删去sql文件
万字SpringBoot学习笔记|菜鸟版

编写测验查询代码

@SpringBootTest
class SpringDemo08JdbcApplicationTests {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Test
    void contextLoads() {
        List<Map<String, Object>> list = jdbcTemplate.queryForList("select * from Skynet_test");
        System.out.println(list);
        // [{id=46, date=2023-06-01, APPversion=1.2.3, uv=123456, tag=gray, platform=Android, 
        // create_time=2023-06-01, last_modify=2023-06-01, version=1.2}]
    }
}

整合Druid数据源

引进依靠

<!--引进自界说数据源druid-->
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.1.8</version>
</dependency>

修正装备文件

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/jdbc
spring.datasource.username=root
spring.datasource.password=root1234
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource // 切换数据源

测验代码

@Test
void contextLoads() {
    System.out.println(dataSource.getClass()); // class com.alibaba.druid.pool.DruidDataSource
}

装备收效:

@Configuration
public class DruidConfig {
    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource druid(){
        return new DruidDataSource();
    }
    // 这样在装备文件中装备druid的一些特点就能够收效了
}

整合Mybatis

万字SpringBoot学习笔记|菜鸟版

验证Mybatis

引进上方的druid数据源
装备文件:

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/jdbc
spring.datasource.username=root
spring.datasource.password=root1234
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

建表语句:

CREATE TABLE `department` (
  `id` int NOT NULL AUTO_INCREMENT,
  `departmentName` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
CREATE TABLE `employee` (
  `id` int NOT NULL AUTO_INCREMENT,
  `lastName` varchar(255) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `gender` int DEFAULT NULL,
  `d_id` int DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;

创立JavaBean:Employee & Department

注解版

@Mapper //指定这是一个操作数据库的mapper
public interface DepartmentMapper {
    @Select("select * from department where id=#{id}")
    public Department getDeptById(Integer id);
    @Delete("delete from department where id=#{id}")
    public int deleteDeptById(Integer id);
    @Options(useGeneratedKeys = true, keyProperty = "id")
    @Insert("insert into department(departmentName) values (#{departmentName})")
    public int insertDept(Department department);
    @Update("update department set departmentName=#{departmentName} where id=#{id}")
    public int updateDept(Department department);
}

测验验证:

@RestController
public class DepartmentController {
    @Autowired
    DepartmentMapper departmentMapper;
    @GetMapping("/dept/{id}")
    public Department getDepartment(@PathVariable("id") Integer id) {
        return departmentMapper.getDeptById(id);
        // 测验链接:http://localhost:8080/dept/1
        // 回来:{"id":1,"departmentName":"开发部"}
    }
    @GetMapping("/dept")
    public Department insertDepartment(Department department) {
        departmentMapper.insertDept(department);
        return department;
        // 测验链接:http://localhost:8080/dept?departmentName=开发部
        // 回来:{"id":1,"departmentName":"开发部"}
    }
}

假如此刻数据库里字段是(department_name),查询成果就展现不出来姓名了:{“id”:1,”departmentName”:null}。怎么敞开驼峰命名法装备?办法是自界说MyBatis的装备规矩,给容器中增加一个ConfigurationCustomizer:

@org.springframework.context.annotation.Configuration
public class MyBatisConfig {
    @Bean
    public ConfigurationCustomizer configurationCustomizer(){
        return new ConfigurationCustomizer() {
            @Override
            public void customize(Configuration configuration) {
                configuration.setMapUnderscoreToCamelCase(true); // 敞开驼峰命名
            }
        };
    }
}

另一个问题是,每个mapper上都需求标示@Mapper注解,主动扫描装备呢?

@MapperScan(value = "com.example.spring_demo09_mybatis.mapper") // 批量扫描一切的Mapper接口
@SpringBootApplication
public class SpringDemo09MybatisApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringDemo09MybatisApplication.class, args);
    }
}

装备文件版

@Mapper
public interface EmployeeMapper {
    public Employee getEmpById(Integer id);
    public void insertEmp(Employee employee);
}

mybatis装备文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
</configuration>
<?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.example.spring_demo09_mybatis.mapper.EmployeeMapper">
   <!--    public Employee getEmpById(Integer id);-->
    <select id="getEmpById" resultType="com.example.spring_demo09_mybatis.bean.Employee">
        SELECT * FROM employee WHERE id=#{id}
    </select>
    <!--public void insertEmp(Employee employee);-->
    <insert id="insertEmp">
        INSERT INTO employee(lastName,email,gender,d_id) VALUES (#{lastName},#{email},#{gender},#{dId})
    </insert>
</mapper>

万字SpringBoot学习笔记|菜鸟版

修正Spring装备文件增加如下内容:

#mybatis
#指定大局装备文件的方位
mybatis.config-location=classpath:mybatis/mybatis-config.xml
#指定sql映射文件的方位
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml

测验办法:

@GetMapping("/emp/{id}")
public Employee getEmp(@PathVariable("id") Integer id) {
    return employeeMapper.getEmpById(id);
    // 测验链接:http://localhost:8080/emp/1
    // 回来:{"id":1,"lastName":"Wang","gender":1,"email":"1111@qq.com","dId":1}
}

运用参阅:mybatis.org/spring-boot…

整合JPA

Spring Data 项意图意图是为了简化构建根据 Spring 结构运用的数据拜访技能,包含非联系数据库、 Map-Reduce 结构、云数据服务等等,别的也包含对联系数据库的拜访支撑。
SpringData 为咱们供给运用一致的API来对数据拜访层进行操作,这主要是Spring Data Commons项目来完成的。Spring Data Commons让咱们在运用联系型或许非联系型数据拜访技能时都根据Spring供给的一致规范,规范包含了CRUD(创立、获取、更新、删去)、查询、 排序和分页的相关操作。

万字SpringBoot学习笔记|菜鸟版

一致的Repository接口:

  • Repository<T, ID extends Serializable>:一致接口
  • RevisionRepository<T, ID extends Serializable, N extends Number & Comparable>:根据乐观锁机制
  • CrudRepository<T, ID extends Serializable>:根本CRUD操作
  • PagingAndSortingRepository<T, ID extends Serializable>:根本CRUD及分页

供给数据拜访模板类 xxxTemplate,如:MongoTemplate、RedisTemplate等

验证JPA

万字SpringBoot学习笔记|菜鸟版

1)、编写一个bean实体类和数据表进行映射,而且装备好映射联系;

package com.example.spring_demo10_jpa.entity;
import javax.persistence.*;
// 运用JPA注解装备映射联系
@Entity //告诉JPA这是一个实体类(和数据表映射的类)
@Table(name = "tbl_user") // @Table来指定和哪个数据表对应,假如省略默许表名便是user
public class User {
    @Id // 代表这是一个主键
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增主键
    private Integer id;
    @Column(name = "name", length = 50) // 这是和数据表对应的一个列
    private String name;
    @Column // 省略默许列名便是特点名
    private String email;
}

2)、编写一个Dao接口来操作实体类对应的数据表(Repository)

// 继承JpaRepository来完成对数据库的操作
public interface UserRepository extends JpaRepository<User, Integer> {
}

3)、根本的装备

#jpa
#更新或许创立数据表结构
spring.jpa.hibernate.ddl-auto=update
#操控台新鲜事SQL
spring.jpa.show-sql=true

4)、发动工程,主动生成数据表:

万字SpringBoot学习笔记|菜鸟版

5)、测验

@RestController
public class UserController {
    @Autowired
    UserRepository userRepository;
    //    @GetMapping("/user/{id}")
    //    public User getUser(@PathVariable("id") Integer id){
    //        User user = userRepository.findOne(id);
    //        return user;
    //    }
    @GetMapping("/user")
    public User insertUser(User user){
        User save = userRepository.save(user);
        return save;
    }
}

恳求http://localhost:8080/user?name=haha&email=qqqq@qq.com会进行日志输出:
Hibernate: insert into tbl_user (email, name) values (?, ?)

Spring Boot发动装备原理

发动流程

SpringApplication.run(主程序类)
1、 创立SpringApplication目标;
这一步主要是加载并保存一切的 ApplicationContextInitializer 和 ApplicationListener,并获取到主程序类
2、运转run()办法;
回调一切的SpringApplicationRunListener的starting、准备环境、创立ioc容器目标(web环境容器和一般环境容器)

事情监听机制

1、准备环境

  • 履行ApplicationContextInitializer. initialize()
  • 监听器SpringApplicationRunListener回调contextPrepared
  • 加载主装备类界说信息
  • 监听器SpringApplicationRunListener回调contextLoaded

2、改写发动IOC容器

  • 扫描加载一切容器中的组件
  • 包含从META-INF/spring.factories中获取的一切EnableAutoConfiguration组件

3、回调容器中一切的ApplicationRunner、CommandLineRunner的run办法

4、监听器SpringApplicationRunListener回调finished

Spring Boot自界说starters

编写主动装备:

@Configuration //指定这个类是一个装备类
@ConditionalOnXXX //在指定条件建立的情况下主动装备类收效
@AutoConfigureAfter //指定主动装备类的次序
@Bean //给容器中增加组件
@ConfigurationPropertie结合相关xxxProperties类来绑定相关的装备
@EnableConfigurationProperties//让xxxProperties收效加入到容器中
主动装备类要能加载,将需求发动就加载的主动装备类,装备在META‐INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\

规划形式:
发动器starter只用来做依靠导入;专门写一个主动装备模块,发动器依靠这个主动装备模块;自界说发动器名-spring-boot-starter

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。