Spring Boot入门简介

内容纲要

一、Spring Boot入门

1、Spring Boot简介

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。

2、微服务

微服务:架构风格(服务微化)

一个应用应该是一组小型服务,可以通过HTTP的方式进行互通

单体应用:ALL IN ONE

微服务:每个功能元素最终都是一个可以独立替换和升级的软件单元

3、环境准备

环境约束

  • jdk1.8
  • maven 3.x :maven3.3以上
  • IDEA2017
  • SpringBoot 1.5.9RELEASE

1、MAVEN设置

<!-- 配置JDK版本 -->
<profile>    
    <id>jdk18</id>    
    <activation>    
        <activeByDefault>true</activeByDefault>    
        <jdk>1.8</jdk>    
    </activation>    
    <properties>    
        <maven.compiler.source>1.8</maven.compiler.source>    
        <maven.compiler.target>1.8</maven.compiler.target>    
        <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>    
    </properties>     
</profile>
   <!-- 当 nexus-aliyun 下不了的包,或许这个镜像能下,
        才开放它,这个实在太慢,而且要把它放在首位,即 nexus-aliyun 之前,做过测试。
        所以它的用途只有那么一瞬间,就是或许它能下载,可以通过 url 去查找确定一下
    -->
    <!-- <mirror>
        <id>spring-libs-milestone</id>
        <mirrorOf>central</mirrorOf>
        <name>Spring Milestones</name>
        <url>http://repo.spring.io/libs-milestone</url>
    </mirror> -->

    <!-- nexus-aliyun 首选,放第一位,有不能下载的包,再去做其他镜像的选择  -->
    <mirror>
        <id>nexus-aliyun</id>
        <mirrorOf>central</mirrorOf>
        <name>Nexus aliyun</name>
        <url>http://maven.aliyun.com/nexus/content/groups/public</url>
    </mirror>

    <!-- 备选镜像,也是可以通过 url 去查找确定一下,
        该镜像是否含有你想要的包,它比 spring-libs-milestone 快  -->
    <mirror>
        <id>central-repository</id>
        <mirrorOf>*</mirrorOf>
        typor<name>Central Repository</name>
        <url>http://central.maven.org/maven2/</url>
    </mirror>  

2、IDEA设置

​ 配置IDEA的Maven,指定Setting的Maven目录和MAVEN的setting.xml文件

​ 快捷键:

​ Ctrl+D 复制一行

​ Ctrl+Y 删除一行

​ Ctrl+P 参数提示

​ Ctrl+Alt+V 自动补齐方法

​ Ctrl+N 查找类方法

​ Alt+Ins 构造器、getter/setter toString

​ Ctrl+O 重载方法提示

​ Alt+Enter 提示导入类etc

​ Shift+F6 :文件重命名

4、Spring Boot的Hello World

1、创建一个Maven工程

2、导入Spring Boot的相关依赖

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

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
</properties>

<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>

3、编写个主程序

@SpringBootApplication
public class SpringBoot01HelloQuickApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBoot01HelloQuickApplication.class, args);
    }
}

4、编写相应的Controller和Service

@Controller
public class HelloController {

    @ResponseBody
    @RequestMapping("/hello")
    public  String  hello(){
        return "hello world";
    }
}

5、运行主程序测试

访问 localhost:8080/hello

6、简化部署

在pom.xml文件中,导入build插件

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

5、HelloWorld深度理解

1.POM.xml文件

1、父项目

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

这个父项目spring-boot-starter-parent又依赖一个父项目

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.0.1.RELEASE</version>
    <relativePath>../../spring-boot-dependencies</relativePath>
</parent>

下面有个属性,定义了对应的版本号

<properties>
    <activemq.version>5.15.3</activemq.version>
    <antlr2.version>2.7.7</antlr2.version>
    <appengine-sdk.version>1.9.63</appengine-sdk.version>
    <artemis.version>2.4.0</artemis.version>
    <aspectj.version>1.8.13</aspectj.version>
    <assertj.version>3.9.1</assertj.version>
    <atomikos.version>4.0.6</atomikos.version>
    <bitronix.version>2.1.4</bitronix.version>
    <build-helper-maven-plugin.version>3.0.0</build-helper-maven-plugin.version>
    。。。。。。。

Spring Boot的版本仲裁中心 会自动导入对应的版本,不需要我们自己导入依赖,没有dependencies里面管理的依赖自己声明

2、启动器

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

spring-boot-starter-web:帮我们导入web模块正常运行所依赖的组件

spring boot将所有的功能场景都抽取出来,做成一个个的starter(启动器),只需要在项目里引入这些starter相关场景的所有依赖都会被导入进来,要用什么功能就导入什么场景的启动器。

2、主程序入口

@SpringBootApplication
public class SpringBoot01HelloQuickApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBoot01HelloQuickApplication.class, args);
    }
}

@SpringBootApplication: 说明这个类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动应用

进入SpringBootApplication注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {

@SpringBootConfiguration:SpringBoot的配置类: 标准在某个类上,表示这是一个SpringBoot的配置类

@Configuration:配置类上,来标注这个注解;
配置类 ---- 配置文件,也是容器中的一个组件(@Component)
@EnableAutoConfiguration:开启自动配置功能
以前需要自动配置的东西,Spring Boot帮我们自动配置;@EnableAutoConfiguration告诉SpringBoot开启自动
配置功能;这样自动配置才能生效。

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

@AutoConfigurationPackage:自动配置包
@Import({Registrar.class}):底层注解,给容器导入组件;
将主配置类(@SpringBootApplication标注的类)的所在包及下面所有的子包里面的所有组件扫描到Spring容器;

@Import({AutoConfigurationImportSelector.class}):
给容器导入组件?

AutoConfigurationImportSelector:导入组件选择器

将所有需要导入的组件以及全类名的方式返回;这些组件将以字符串数组 String[] 添加到容器中;

会给容器非常多的自动配置类,(xxxAutoConfiguration);就是给容器中导入这个场景需要的所有组件,并配置
好这些组件。

1.configuration

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
    List<String> configurations =
SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(),
this.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;
} 

SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(),
this.getBeanClassLoader());

Spring Boot在启动的时候从类路径下的META-INF/spring.factorys中获取的EnableAutoConfiguration指定的值;

将这些值作为自动配置类导入到容器中,自动配置就生效了。 2.factories

J2EE的整体解决方案

org\springframework\boot\spring-boot-autoconfigure\2.0.1.RELEASE\spring-boot-autoconfigure-2.0.1.RELEASE.jar

6、使用Spring Initializer创建一个快速向导

1.IDE支持使用Spring Initializer

自己选择需要的组件:例如web

默认生成的SpringBoot项目

  • 主程序已经生成好了,我们只需要完成我们的逻辑

  • resources文件夹目录结构

    • static:保存所有的静态文件;js css images

    • templates:保存所有的模板页面;(Spring Boot默认jar包使用嵌入式的Tomcat,默认不支持JSP);可

    以使用模板引擎(freemarker.thymeleaf);

    • application.properties:Spring Boot的默认配置,例如 server.port=9000

二、配置文件

1、配置文件

Spring Boot使用全局配置文件,配置文件名是固定的;

  • application.properties
  • application.yml

配置文件作用:修改Spring Boot在底层封装好的默认值;

YAML(YAML AIN'T Markup Language)

是一个标记语言

又不是一个标记语言

标记语言:

以前的配置文件;大多数使用的是 xxx.xml文件;

以数据为中心,比json、xml等更适合做配置文件

YAML:配置例子

server:
    port: 9000 

XML:

<server>
    <port>9000</port>
</server> 

2、YAML语法

1、基本语法

k:(空格)v:表示一堆键值对(空格必须有);

以空格的缩进来控制层级关系;只要是左对齐的一列数据,都是同一层级的

server:
    port: 9000
    path: /hello 

属性和值也是大小写敏感

2、值的写法

字面量:普通的值(数字,字符串,布尔)

k: v:字面直接来写;

字符串默认不用加上单引号或者双引号

"":双引号 不会转义字符串里的特殊字符;特殊字符会作为本身想要表示的意思

name:"zhangsan\n lisi" 输出:zhangsan换行 lisi

'':单引号 会转义特殊字符,特殊字符最终只是一个普通的字符串数据

name:'zhangsan\n lisi' 输出:zhangsan\n lisi

对象、Map(属性和值)键值对

k :v :在下一行来写对象的属性和值的关系;注意空格控制缩进

对象还是k:v的方式

frends:
    lastName: zhangsan
    age: 20 

行内写法

friends: {lastName: zhangsan,age: 18} 

数组(List、Set):
用-表示数组中的一个元素

pets:
 ‐ cat
 ‐ dog
 ‐ pig 

行内写法

pets: [cat,dog,pig] 

组合变量

多个组合到一起

3、配置文件值注入

1、@ConfigurationProperties

1、application.yml 配置文件

person:
  age: 18
  boss: false
  birth: 2017/12/12
  maps: {k1: v1,k2: 12}
  lists:
   - lisi
   - zhaoliu
  dog:
    name: wangwang
    age: 2
  last-name: wanghuahua

application.properties 配置文件(二选一)

idea配置文件utf-8
properties 默认GBK
person.age=12
person.boss=false
person.last-name=张三
person.maps.k1=v1
person.maps.k2=v2
person.lists=a,b,c
person.dog.name=wanghuahu
person.dog.age=15

所以中文输出乱码,改进settings-->file encoding -->[property-->utf-8 ,勾选转成ascii]

javaBean

/**
* 将配置文件的配置每个属性的值,映射到组件中
* @ConfigurationProperties:告诉SpringBoot将文本的所有属性和配置文件中的相关配置进行绑定;
* prefix = "person" 配置文件爱你的那个属性进行一一映射
* *
只有这个组件是容器中的组件,才能提供到容器中
*/
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
    private String lastName;
    private Integer age;
    private Boolean boss;
    private Map<String,Object> maps;
    private List<Object> lists;
    private Dog dog;

导入配置文件处理器,以后编写配置就有提示了

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

2、@Value注解

更改javaBean中的注解

@Component
public class Person {
    /**
     * <bean class="Person">
     *     <property name="lastName" value="字面量/${key}从环境变量/#{spEL}"></property>
     * </bean>
     */
    @Value("${person.last-name}")
    private String lastName;
    @Value("#{11*2}")
    private Integer age;
    @Value("true")
    private Boolean boss;
@ConfigurationProperties @Value
功能 批量注入配置文件属性 单个指定
松散绑定(语法) 支持 不支持
spEL 不支持 支持
JSR303校验 支持 不支持
复杂类型 支持 不支持

松散语法:javaBean中last-name(或者lastName) -->application.properties中的last-name;

spEL语法:#{11*2}

JSR303:@Value会直接忽略,校验规则

JSR303校验:

@Component
@ConfigurationProperties(prefix = "person")
@Validated
public class Person {
    @Email
    private String lastName;

复杂类型栗子:

@Component
public class Person {
    /**
     * <bean class="Person">
     *     <property name="lastName" value="字面量/${key}从环境变量/#{spEL}"></property>
     * </bean>
     */
    private String lastName;
    private Integer age;
    private Boolean boss;
   // @Value("${person.maps}")
    private Map<String,Object> maps;

以上会报错,不支持复杂类型

使用场景分析

​ 如果说,我们只是在某个业务逻辑中获取一下配置文件的某一项值,使用@Value;

如果专门编写了一个javaBean和配置文件进行映射,我们直接使用@ConfigurationProperties

举栗子:

1、编写新的Controller文件

@RestController
public class HelloController {

    @Value("${person.last-name}")
    private String name;
    @RequestMapping("/hello")
    public  String sayHello(){
        return "Hello"+ name;
    }
}

2、配置文件

person.age=12
person.boss=false
person.last-name=李四
person.maps.k1=v1
person.maps.k2=v2
person.lists=a,b,c
person.dog.name=wanghuahu
person.dog.age=15

3、测试运行

访问 localhost:9000/hello

结果为Hello 李四

3、其他注解

@PropertySource

作用:加载指定的properties配置文件

1、新建一个person.properties文件

person.age=12
person.boss=false
person.last-name=李四
person.maps.k1=v1
person.maps.k2=v2
person.lists=a,b,c
person.dog.name=wanghuahu
person.dog.age=15

2、在javaBean中加入@PropertySource注解

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

@ImportResource

作用:导入Spring配置文件,并且让这个配置文件生效

1、新建一个Spring的配置文件,bean.xml

<?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.wdjr.springboot.service.HelloService"></bean>
</beans>

2、编写测试类,检查容器是否加载Spring配置文件写的bean

@Autowired
ApplicationContext ioc;

@Test
public void testHelloService(){
    boolean b = ioc.containsBean("HelloService");
    System.out.println(b);
}

import org.springframework.context.ApplicationContext;

3、运行检测

结果为false,没有加载配置的内容

4、使用@ImportResource注解

将@ImportResource标注在主配置类上

@ImportResource(locations={"classpath:beans.xml"})
@SpringBootApplication
public class SpringBoot02ConfigApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBoot02ConfigApplication.class, args);
    }
}

5、再次运行检测

结果为true

缺点:每次指定xml文件太麻烦

SpringBoot推荐给容器添加组件的方式:

1、配置类=====Spring的xml配置文件(old)

2、全注解方式@Configuration+@Bean(new)

4.MyAppConfig

/**
 * @Configuration:指明当前类是一个配置类;就是来代替之前的Spring配置文件
 *
 * 在配置文件中用<bean></bean>标签添加组件
 */

@Configuration
public class MyAppConfig {

    //将方法的返回值添加到容器中;容器这个组件id就是方法名
    @Bean
    public HelloService helloService01(){
        System.out.println("配置类给容器添加了HelloService组件");
        return new HelloService();
    }
}
@Autowired
ApplicationContext ioc;

@Test
public void testHelloService(){
    boolean b = ioc.containsBean("helloService01");
    System.out.println(b);
}

容器这个组件id就是方法名

4、配置文件占位符

1、随机数

${random.value} 、${random.int}、${random.long}
${random.int(10)}、${random.int[100,200]}

2、获取配置值

person.age=${random.int}
person.boss=false
person.last-name=张三${random.uuid}
person.maps.k1=v1
person.maps.k2=v2
person.lists=a,b,c
person.dog.name=${person.last-name}'s wanghuahu
person.dog.age=15

存在以下两种情况

没有声明person.last-name会报错,新声明的需要加默认值

person.age=${random.int}
person.boss=false
person.last-name=张三${random.uuid}
person.maps.k1=v1
person.maps.k2=v2
person.lists=a,b,c
person.dog.name=${person.hello:hello}'s wanghuahu
person.dog.age=15

结果:输出hello's wanghuahua

5、Profile

1、多Profile文件

我们在主配置文件编写的时候,文件名可以是 application-{profile}.properties/yml

  • application.properties
  • application-dev.properties
  • application-prod.properties

默认使用application.properties

application.properties配置文件指定

spring.profiles.active=dev

2、YAML文档块

server:
  port: 8081
spring:
  profiles:
    active: dev

---

server:
  port: 9000
spring:
  profiles: dev

---
server:
  port: 80
spring:
  profiles: prod

3、激活指定profile

1、在配置文件中激活

2、命令行:

--spring.profiles.active=dev

5.comandLine

优先级大于配置文件

打包 成jar后

java -jar spring-boot-02-config-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev

虚拟机参数

-Dspring.profiles.active=dev

6、加载配置文件位置

SpringBoot启动扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件

  • file:./config/
  • file./
  • classpath:/config/
  • classpath:/

优先级从高到低顺序,高优先级会覆盖低优先级的相同配置;互补配置

也可以通过spring.config.location来改变默认配置

server.servlet.context-path=/boot03

注:spring boot1x 是server.context.path=/boot02

7.priority

还可以通过spring.config.location来改变配置文件的位置

项目打包好了以后,可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;指定配置文件和默认的配置文件会共同起作用,互补配置

java -jar spring-boot-config-02-0.0.1-SNAPSHOT.jar --spring.config.location=E:/work/application.properties

运维比较有用,从外部加载,不用修改别的文件

7.引入外部配置

SpringBoot也可以从以下位置加载配置;优先级从高到低;高优先级覆盖低优先级,可以互补

  1. 命令行参数

    java -jar spring-boot-config-02-0.0.1-SNAPSHOT.jar --server.port=9005 --server.context-path=/abc

    中间一个空格

  2. 来自java:comp/env的JNDI属性

  3. java系统属性(System.getProperties())

  4. 操作系统环境变量

  5. RandomValuePropertySource配置的random.*属性值

    优先加载profile, 由jar包外到jar包内

  6. jar包外部的application-{profile}.properties或application.yml(带Spring.profile)配置文件

  7. jar包内部的application-{profile}.properties或application.yml(带Spring.profile)配置文件

  8. jar包外部的application.properties或application.yml(带Spring.profile)配置文件

  9. jar包内部的application.properties或application.yml(不带spring.profile)配置文件

  10. @Configuration注解类的@PropertySource

  11. 通过SpringApplication.setDefaultProperties指定的默认属性

官方文档

8、自动配置

配置文件到底怎么写?

Spring的所有配置参数

自动配置原理很关键

1、自动配置原理

1)、SpringBoot启动的时候加载主配置类,开启自动配置功能,@EnableAutoConfiguration

2)、@EnableAutoConfiguration 作用:

  • 利用AutoConfigurationImportSelector给容器中导入一些组件?
  • 可以查看selectImports()方法的内容
  • 获取候选的配置
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
  • 扫描类路径下的
  SpringFactoriesLoader.loadFactoryNames()
  扫描所有jar包类路径下的 MATA-INF/spring.factories
  把扫描到的这些文件的内容包装成properties对象
  从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加到容器中

将类路径下 MATE-INF/spring.factories里面配置的所有的EnableAutoConfiguration的值加入到了容器中;

3)、每一个自动配置类进行自动配置功能;

4)、以HttpEncodingAutoConfiguration 为例

@Configuration //表示是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件
@EnableConfigurationProperties({HttpEncodingProperties.class})//启动指定类的Configurationproperties功能;将配置文件中的值和HttpEncodingProperties绑定起来了;并把HttpEncodingProperties加入ioc容器中
@ConditionalOnWebApplication//根据不同的条件,进行判断,如果满足条件,整个配置类里面的配置就会失效,判断是否为web应用;
(
    type = Type.SERVLET
)
@ConditionalOnClass({CharacterEncodingFilter.class})//判断当前项目有没有这个类,解决乱码的过滤器
@ConditionalOnProperty(
    prefix = "spring.http.encoding",
    value = {"enabled"},
    matchIfMissing = true
)//判断配置文件是否存在某个配置 spring.http.encoding,matchIfMissing = true如果不存在也是成立,即使不配置也生效
public class HttpEncodingAutoConfiguration {
   //给容器添加组件,这个组件的值需要从properties属性中获取
    private final HttpEncodingProperties properties;
    //只有一个有参数构造器情况下,参数的值就会从容器中拿
    public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
        this.properties = 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.HttpEncodingProperties.Type.REQUEST));
        filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpEncodingProperties.Type.RESPONSE));
        return filter;
    }

5)、所有在配置文件中能配置的属性都是在xxxProperties类中封装着;配置文件能配置什么就可以参照某个功能对应的这个属性类

@ConfigurationProperties(prefix = "spring.http.encoding")//从配置文件中的值进行绑定和bean属性进行绑定
public class HttpEncodingProperties {

根据当前不同条件判断,决定这个配置类是否生效?

一旦这个配置类生效;这个配置类会给容器添加各种组件;这些组件的属性是从对应的properties中获取的,这些类里面的每个属性又是和配置文件绑定的

2、所有的自动配置组件

每一个xxxAutoConfiguration这样的类都是容器中的一个组件,都加入到容器中;

作用:用他们做自动配置

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration

3、精髓:

1)、SpringBoot启动会加载大量的自动配置类

2)、我们看我们需要的功能有没有SpringBoot默认写好的默认配置类;

3)、如果有在看这个自动配置类中配置了哪些组件;(只要我们要用的组件有,我们需要再来配置)

4)、给容器中自动配置添加组件的时候,会从properties类中获取属性。我们就可以在配置文件中指定这些属性的值

xxxAutoConfiguration:自动配置类;

给容器中添加组件

xxxProperties:封装配置文件中的属性;

跟之前的Person类一样,配置文件中值加入bean中

4、细节

1、@Conditional派生注解

利用Spring注解版原生的@Conditional作用

作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效;

@Conditional派生注解 作用(判断是否满足当前指定条件)
@ConditionalOnJava 系统的java版本是否符合要求
@ConditionalOnBean 容器中存在指定Bean
@ConditionalOnMissBean 容器中不存在指定Bean
@ConditionalOnExpression 满足spEL表达式
@ConditionalOnClass 系统中有指定的类
@ConditionalOnMissClass 系统中没有指定的类
@ConditionalOnSingleCandidate 容器中只有一个指定的Bean,或者这个Bean是首选Bean
@ConditionalOnProperty 系统中指定的属性是否有指定的值
@ConditionalOnResource 类路径下是否存在指定的资源文件
@ConditionalOnWebApplication 当前是web环境
@ConditionalOnNotWebApplication 当前不是web环境
@ConditionalOnJndi JNDI存在指定项

2、自动配置报告

自动配置类必须在一定条件下生效

我们可以通过启用debug=true属性,配置文件,打印自动配合报告,这样就可以知道自动配置类生效

debug=true

自动配置报告

============================

CONDITIONS EVALUATION REPORT
============================

Positive matches:(启动的,匹配成功的)
-----------------

   CodecsAutoConfiguration matched:
      - @ConditionalOnClass found required class 'org.springframework.http.codec.CodecConfigurer'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)
        ......

 Negative matches:(没有启动的,没有匹配成功的)
-----------------

   ActiveMQAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required classes 'javax.jms.ConnectionFactory', 'org.apache.activemq.ActiveMQConnectionFactory' (OnClassCondition)
.....

三、日志

Spring Boot2对日志有更改

1、日志框架

小张:开发一个大型系统;

1、System.out.println("");将关键数据打印在控制台;去掉?卸载文件中

2、框架记录系统的一些运行信息;日志框架zhanglog.jar

3、高大上功能,异步模式?自动归档?xxx?zhanglog-good.jar?

4、将以前的框架卸下来?换上新的框架,重新修改之前的相关API;zhanglog-perfect.jar;

5、JDBC--数据库驱动;

​ 写了一个统一的接口层;日志门面(日志的一个抽象层);logging-abstract.jar;

​ 给项目中导入具体的日志实现就行;我们之前的日志框架都是实现的抽象层;

市面上的日志框架

日志抽象层 日志实现
JCL(Jakarta Commons Logging) SLF4j(Simple Logging Facade for Java) jboss-logging Log4j JUL(java.util.logging) Log4j2 Logback

左边的抽象,右边的实现

SLF4J -- Logback

Spring Boot:底层是Spring框架,Spring默认框架是JCL;

​ SpringBoot选用SLF4J和logback

2、SLF4J使用

1、如何在系统中使用SLF4j

以后开发的时候,日志记录方法的调用,不应该来直接调用日志的实现类,而是调用日志抽象层里面的方法;

应该给系统里面导入slf4j的jar包和logback的实现jar

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

8.slf4j

每个日志框架的实现框架都有自己的配置文件。使用slf4j以后,配置文件还是做成日志实现框架本身的配置文件

2、遗留问题

a系统(slf4j+logback):Spring(commons-logging)、Hibernate(jboss-logging)、Mybatis

统一日志框架,即使是别的框架和我一起统一使用slf4j进行输出;

核心:

1、将系统中其他日志框架排除出去;

2、用中间包来替换原有的日志框架/

3、导入slf4j的其他实现

3、SpingBoot日志框架解析

打开IDEA ,打开pom文件的依赖图形化显示

9.IDEAdependencies

SpringBoot的基础框架

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

SpringBoot的日志功能

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-logging</artifactId>
    <version>2.0.1.RELEASE</version>
    <scope>compile</scope>
</dependency>

10.slf4jandlogback

总结:

  1. SpringBoot底层也是使用SLF4J+log4jback

  2. SpringBoot也把其他日志替换成了slf4j

  3. 起着commons.loggings的名字其实new的SLF4J替换中间包

    SpringBoot2中改成了bridge

  4. 如果要引入其他框架?一定要把这个框架的日志依赖移除掉,而且底层

4、日志的使用

1、默认配置

trace-debug-info-warn-error

可以调整需要的日志级别进行输出,不用注释语句。

//记录器
Logger logger = LoggerFactory.getLogger(getClass());
@Test
public void contextLoads() {

    //日志的级别
    //从低到高
    //可以调整输出的日志级别;日志就只会在这个级别以后的高级别生效
    logger.trace("这是trace日志");
    logger.debug("这是debug信息");
    //SpringBoot默认给的是info级别,如果没指定就是默认的root级别
    logger.info("这是info日志");
    logger.warn("这是warn信息");
    logger.error("这是Error信息");
}

调整指定包的日志级别在配置文件中进行配置

logging.level.com.wdjr=trace

日志输出格式

#控制台输出的日志格式 
#%d:日期
#%thread:线程号 
#%-5level:靠左 级别 
#%logger{50}:全类名50字符限制,否则按照句号分割
#%msg:消息+换行
#%n:换行
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n

SpringBoot修改日志的默认配置

logging.level.com.wdjr=trace
#不指定path就是当前目录下生成springboot.log
#logging.file=springboot.log
#当前磁盘下根路径创建spring文件中log文件夹,使用spring.log作为默认
logging.path=/spring/log
#控制台输出的日志格式 日期 + 线程号 + 靠左 级别 +全类名50字符限制+消息+换行
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
#指定文件中日志输出的格式
logging.pattern.file=xxx

2、指定配置

给类路径下放上每个日志框架自己的配置框架;SpringBoot就不会使用自己默认的配置

logging System Customization
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加载

<springProfile name="dev">
    <!-- 可以指定某段配置只在某个环境下生效 -->
</springProfile>
<springProfile name!="dev">
    <!-- 可以指定某段配置只在某个环境下生效 -->
</springProfile>

如何调试开发环境,输入命令行参数

--spring.profiles.active=dev

如果不带后面的xx-spring.xml就会报错

3、切换日志框架

可以根据slf4j的日志适配图,进行相关切换;

1、log4j

slf4j+log4j的方式;

11.log4j

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <artifactId>logback-classic</artifactId>
            <groupId>ch.qos.logback</groupId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
</dependency>

不推荐使用仅作为演示

2、log4j2

切换为log4j2

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <artifactId>spring-boot-starter-logging</artifactId>
            <groupId>org.springframework.boot</groupId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

四、web开发

1、简介

使用SpringBoot;

1)、创建SpringBoot应用,选中我们需要的模块;

2)、SpringBoot已经默认将这些场景配置好了,只需要在配置文件中指定少量配置就可以运行起来

3)、自己编写业务代码

自动配置原理?

这个场景的SpringBoot帮我们配置了什么?能不能修改?能修改那些配置?能不能扩展?xxx

xxxAutoConfiguration:帮我们给容器中自动配置组件
xxxProperties:配置类来封装配置文件的内容

2、静态资源文件映射规则

@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties implements ResourceLoaderAware, InitializingBean {
    //可以设置和静态资源相关的参数,缓存时间等
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
   if (!this.resourceProperties.isAddMappings()) {
      logger.debug("Default resource handling disabled");
      return;
   }
   Integer cachePeriod = this.resourceProperties.getCachePeriod();
   if (!registry.hasMappingForPattern("/webjars/**")) {
      customizeResourceHandlerRegistration(registry
            .addResourceHandler("/webjars/**")
            .addResourceLocations("classpath:/META-INF/resources/webjars/")
            .setCachePeriod(cachePeriod));
   }
   String staticPathPattern = this.mvcProperties.getStaticPathPattern();
   if (!registry.hasMappingForPattern(staticPathPattern)) {
      customizeResourceHandlerRegistration(
            registry.addResourceHandler(staticPathPattern)
                  .addResourceLocations(
                        this.resourceProperties.getStaticLocations())
                  .setCachePeriod(cachePeriod));
   }
}

1、webjar

1)、所有的/webjars/**,都去classpath:/META-INF/resources/webjars/找资源;

​ webjars:以jar包的方式引入静态资源

http://www.webjars.org/

12.jquery

localhost:8080/webjars/jquery/3.3.1/jquery.js

2、本地资源

private String staticPathPattern = "/**";

访问任何资源

2、会在这几文件夹下去找静态路径(静态资源文件夹)

"classpath:/META-INF/resources/", 
"classpath:/resources/",
"classpath:/static/", 
"classpath:/public/",
"/";当前项目的根路径

13.static

localhost:8080/abc ==>去静态资源文件夹中找abc

14.static-css

3、index页面欢迎页,静态资源文件夹下所有的index.html页面;被“/**”映射;

localhost:8080/ -->index页面

@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(
      ResourceProperties resourceProperties) {
   return new WelcomePageHandlerMapping(resourceProperties.getWelcomePage(),
         this.mvcProperties.getStaticPathPattern());
}

4、喜欢的图标,即网站title的图标favicon

@Configuration
@ConditionalOnProperty(value = "spring.mvc.favicon.enabled", matchIfMissing = true)
public static class FaviconConfiguration {

   private final ResourceProperties resourceProperties;

   public FaviconConfiguration(ResourceProperties resourceProperties) {
      this.resourceProperties = resourceProperties;
   }

   @Bean
   public SimpleUrlHandlerMapping faviconHandlerMapping() {
      SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
      mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
       //把任何favicon的图标都在静态文件夹下找
      mapping.setUrlMap(Collections.singletonMap("**/favicon.ico",
            faviconRequestHandler()));
      return mapping;
   }

   @Bean
   public ResourceHttpRequestHandler faviconRequestHandler() {
      ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
      requestHandler
            .setLocations(this.resourceProperties.getFaviconLocations());
      return requestHandler;
   }

}

可以在配置文件配置静态资源文件夹

spring.resources.static-locations=classpath:xxxx

3、模板引擎

将html和数据 结合到一起 输出组装处理好的新文件

SpringBoot推荐Thymeleaf;语法简单,功能强大

1、引入thymeleaf 3

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

默认导入thymeleaf2,版本太低 所以使用thymeleaf3.

官方导入办法

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    <!--thymeleaf 3的导入-->
    <thymeleaf.version>3.0.9.RELEASE</thymeleaf.version>
    <!--布局功能支持 同时支持thymeleaf3主程序 layout2.0以上版本  -->
    <!--布局功能支持 同时支持thymeleaf2主程序 layout1.0以上版本  -->
    <thymeleaf-layout-dialect.version>2.2.2</thymeleaf-layout-dialect.version>
</properties>

2、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";
   //只要把HTML文件方法类路径下的template文件夹下,就会自动导入

只要把HTML页面放到classpath:/templates/,thymeleaf就能自动渲染;

使用:

1、导入thymeleaf的名称空间

<html xmlns:th="http://www.thymeleaf.org">    

2、使用thymeleaf语法;

<!DOCTYPE html>
<html lang="en"  xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8" />
    <title>success</title>
</head>
<body>
<h1>success</h1>
<!--th:text 将div里面的文本内容设置为-->
<div th:text="${Lion}">
前端数据
</div>
</body>
</html>

3、语法规则

1)、th:text="${hello}"可以使用任意标签 替换原生的任何属性

在SpringBoot的环境下

<div id="testid" class="testcalss" th:id="${Lion}" th:class="${Lion}" th:text="${Lion}">
    前端数据
</div>

15.thtmeleaf-th01

直接访问HTML页面

15.thtmeleaf-th02

2)、内联写法注意需要在body上加上 th:inline="text"敲黑板

不然不起作用

<body class="text-center" th:inline="text"></body>

th标签的访问优先级

Order Feature Attributes

3、语法规则

功能 标签 功能和jsp对比
1 Fragment inclusion th:insert th:replace include(片段包含)
2 Fragment iteration th:each c:forEach(遍历)
3 Conditional evaluation th:if th:unless th:switch th:case c:if(条件判断)
4 Local variable definition th:object th:with c:set(声明变量)
5 General attribute modification th:attr th:attrprepend th:attrappend 属性修改支持前面和后面追加内容
6 Specific attribute modification th:value th:href th:src ... 修改任意属性值
7 Text (tag body modification) th:text th:utext 修改标签体内容utext:不转义字符

大标题

8 Fragment specification th:fragment 声明片段
9 Fragment removal th:remove
Simple expressions:(表达式语法)
    Variable Expressions: ${...}
        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 a result of an iteration).
    Selection Variable Expressions: *{...} //选择表达式:和${}功能一样,补充功能
   # 配合th:object使用,object=${object} 以后获取就可以使用*{a}  相当于${object.a}
        <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>
    Message Expressions: #{...} //获取国际化内容
    Link URL Expressions: @{...} //定义URL链接
        #<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a>
    Fragment Expressions: ~{...}//片段文档

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): -
Boolean operations:(布尔运算)
    Binary operators: and , or
    Boolean negation (unary operator): ! , not
Comparisons and equality:(比较运算)
    Comparators: > , < , >= , <= ( gt , lt , ge , le )
    Equality operators: == , != ( eq , ne )
Conditional operators:(条件运算)
    If-then: (if) ? (then)
    If-then-else: (if) ? (then) : (else)
    Default: (value) ?: (defaultvalue)
Special tokens:(空操作)
    No-Operation: _

inline写法

[[]] -->th:text
[()] -->th:utext

4、SpringMVC自动配置

1、SpringMVC的自动导入

Spring框架

自动配置好了mvc:

以下是SpringBoot对SpringMVC的默认

Spring Boot provides auto-configuration for Spring MVC that works well with most applications.

The auto-configuration adds the following features on top of Spring’s defaults:

  • Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.

    • 自动配置了ViewResolver(视图解析器:根据方法的返回值得到视图对象(View),视图对象决定如何渲染(转发?重定向?))
    • ContentNegotiatingViewResolver组合所有视图解析器
    • 如何定制:我们可以自己给容器中添加一个视图解析器;自动将其整合进来
  • Support for serving static resources, including support for WebJars (see below).静态资源

  • Static index.html support.

  • Custom Favicon support (see below).

  • 自动注册 了Converter, GenericConverter, Formatter beans.

    • Converter:类型转换 文本转为字面量

    • Formatter :格式化器 转换后格式转换

    @Bean
    @ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")//在文件配置入职格式化的规则
    public Formatter dateFormatter() {
       return new DateFormatter(this.mvcProperties.getDateFormat());//日期格式化组件
    }

    自己添加的格式化转换器,只需要放在容器中即可

  • Support for HttpMessageConverters (see below).

    • HttpMessageConverters :转换HTTP转换和响应:User - json

    • HttpMessageConverters :是从容器中确定;获取所有的HttpMessageConverters ,将自己的组件注册在容器中@Bean

    • If you need to add or customize converters you can use Spring Boot’s HttpMessageConverters class:

    import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
    import org.springframework.context.annotation.*;
    import org.springframework.http.converter.*;
    
    @Configuration
    public class MyConfiguration {
    
        @Bean
        public HttpMessageConverters customConverters() {
            HttpMessageConverter additional = ...
            HttpMessageConverter another = ...
            return new HttpMessageConverters(additional, another);
        }
    
    }
  • Automatic registration of MessageCodesResolver (see below).

    • 定义错误代码生成规则
  • Automatic use of a ConfigurableWebBindingInitializer bean (see below).

    • @Override
      protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() {
      try {
        return this.beanFactory.getBean(ConfigurableWebBindingInitializer.class);
      }
      catch (NoSuchBeanDefinitionException ex) {
        return super.getConfigurableWebBindingInitializer();
      }
      }

    在beanFactory:中可以自己创建一个,初始化webDataBinder

    请求数据 ==》javaBean

If you want to keep Spring Boot MVC features, and you just want to add additional MVC configuration (interceptors, formatters, view controllers etc.) you can add your own @Configuration class of type WebMvcConfigurerAdapter, but without @EnableWebMvc. If you wish to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter or ExceptionHandlerExceptionResolver you can declare a WebMvcRegistrationsAdapter instance providing such components.

If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.

思想:修改默认配置

2、扩展SpringMVC

编写一个配置类,类型是WebMvcConfigurerAdapter(继承),使用WebMvcConfigurerAdapter可以扩展,不能标注@EnableWebMvc;既保留了配置,也能拓展我们自己的应用

@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
//        super.addViewControllers(registry);
        //浏览器发送wdjr请求,也来到success页面
        registry.addViewController("/wdjr").setViewName("success");
    }
}

原理:

1)、WebMvcAutoConfiguration是SpringMVC的自动配置

2)、在做其他自动配置时会导入;@Import(EnableWebMvcConfiguration.class)

@Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {
    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();

    //从容器中获取所有webMVCconfigurer
    @Autowired(required = false)
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.configurers.addWebMvcConfigurers(configurers);

                @Override
                protected void addViewControllers(ViewControllerRegistry registry) {
                    this.configurers.addViewControllers(registry);
                }
            //一个参考实现,将所有的webMVCconfigurer相关配置一起调用(包括自己的配置类)
                @Override
               // public void addViewControllers(ViewControllerRegistry registry) {
                   // for (WebMvcConfigurer delegate : this.delegates) {
                 //delegate.addViewControllers(registry);
                    //}
                }
        }
    }

3)、自己的配置被调用

效果:SpringMVC的自动配置和我们的扩展配置都会起作用

3、全面接管mvc

不需要SpringBoot对SpringMVC的自动配置。

@EnableWebMvc
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {

@Override
public void addViewControllers(ViewControllerRegistry registry) {

//        super.addViewControllers(registry);
        //浏览器发送wdjr请求,也来到success页面
        registry.addViewController("/wdjr").setViewName("success");
    }
}

例如静态资源访问,不推荐全面接管

原理:

为什么@EnableWebMvc注解,SpringBoot对SpringMVC的控制就失效了

1)、核心配置

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

2)、DelegatingWebMvcConfiguration

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

3)、WebMvcAutoConfiguration

@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
      WebMvcConfigurerAdapter.class })
//容器没有这个组件的时候,这个自动配置类才生效
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
      ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {

4)、@EnableWebMvc将WebMvcConfigurationSupport导入进来了;

5)、导入的WebMvcConfigurationSupport只是SpringMVC最基本的功能

5、修改SpringMVC默认配置

模式:

​ 1)、SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(@Bean、@Component)如果有就用用户配置的,如果没有,才自动配置;如果有些组件可以有多个(ViewResolver)将用户配置的和自己默认的组合起来;

​ 2)、在SpringBoot中会有 xxxConfigurer帮助我们扩展配置。

6、RestfulCRUD

1、默认访问首页

在config/MyConfig.java中编写配置类

//所有的webMvcConfigurerAdapter组件会一起起作用
@Bean //註冊到容器去
public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){
    WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/").setViewName("login");
            registry.addViewController("/login.html").setViewName("login");
        }
    };
    return adapter;
}

静态资源引用

<link href="#" th:href="@{/css/signin.css}" rel="stylesheet" />

2、国际化

1、编写国际化配置文件

2、使用ResourceBundleMessageSource管理国际化资源文件

3、在页面中使用fmt:message,取出国际化内容

1、浏览器切换国际化

步骤

1、编写国际化配置文件,抽取页面需要的显示的国际化消息

16.national

2、SpringBoot自动配置好了国际化配置的资源文件

@ConfigurationProperties(prefix = "spring.messages")
public class MessageSourceAutoConfiguration {
    //我们的配置文件可以直接放在类路径下叫messages.properties
    private String basename = "messages";
    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        if (StringUtils.hasText(this.basename)) {
            //设置国际化文件的基础名,去掉语言国家代码
            messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(
                    StringUtils.trimAllWhitespace(this.basename)));
        }
        if (this.encoding != null) {
            messageSource.setDefaultEncoding(this.encoding.name());
        }
        messageSource.setFallbackToSystemLocale(this.fallbackToSystemLocale);
        messageSource.setCacheSeconds(this.cacheSeconds);
        messageSource.setAlwaysUseMessageFormat(this.alwaysUseMessageFormat);
        return messageSource;
    }

3、对IDEA的编码进行设置

17.encoding

4、login进行标签插入

<!DOCTYPE html>
<!-- saved from url=(0051)https://getbootstrap.com/docs/4.1/examples/sign-in/ -->
<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="" />
    <link rel="icon" href="https://getbootstrap.com/favicon.ico" />

    <title>登录页面</title>

    <!-- Bootstrap core CSS -->
    <link href="#" th:href="@{/css/bootstrap.min.css}" rel="stylesheet" />

    <!-- Custom styles for this template -->
    <link href="./login_files/signin.css" th:href="@{/css/signin.css}" rel="stylesheet" />
  </head>

  <body class="text-center">
    <form class="form-signin">
      <img class="mb-4" src="./login_files/bootstrap-solid.svg" th:src="@{/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>
      <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 for="inputPassword" class="sr-only" th:text="#{login.password}">Password</label>
      <input type="password" name="password" id="inputPassword" 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>
    </form>

</body></html>

效果根据浏览器语言的信息切换国际化

原理:

国际化locale(区域信息对象);LocaleResolver(获取区域对象);

@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
public LocaleResolver localeResolver() {
    if (this.mvcProperties
        .getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
        return new FixedLocaleResolver(this.mvcProperties.getLocale());
    }
    AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
    localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
    return localeResolver;
}            

默认的就是根据请求头带来的区域信息获取local国际化信息(截图就是这么犀利)

18.accept-language

2、点击链接切换国际化

自己编写localResolver,加到容器中

1、更改HTML代码

<p class="mt-5 mb-3 text-muted">© 2017-2018</p>
  <a href="#" class="btn btn-sm" th:href="@{/index.html?lg=zh_CN}">中文</a>
  <a href="#" class="btn btn-sm" th:href="@{/index.html?lg=en_US}">English</a>

2、新建一个MyLocaleResolver.class

public class MyLocaleResolver implements LocaleResolver {

    //解析区域信息
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        String l = request.getParameter("lg");
        Locale locale = Locale.getDefault();
        if(!StringUtils.isEmpty(l)){
            String[] split = l.split("_");
            locale = new Locale(split[0], split[1]);
        }
        return locale;
    }

    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {

    }
}

3、将MyLocaleResolver加入到容器中

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

4、启动演示

3、登录拦截器

1、登录

开发技巧

​ 1、清除模板缓存

​ 2、Ctrl+F9刷新

1、新建一个LoginController

@Controller
public class LoginController {

    @PostMapping(value ="/user/login")
    public String login(@RequestParam("username")String username,
                        @RequestParam("password")String password,
                        Map<String,Object> map){
        if(!StringUtils.isEmpty(username) && "123456".equals(password)){
            //登录成功
            return "list";
        }else{
            map.put("msg", "用户名密码错误");
            return "login";
        }

    }
}

2、登录错误消息显示

<!--判断-->
<p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>

3、表单重复提交

表单重复提交事件 --》重定向来到成功页面--》模板引擎解析

if(!StringUtils.isEmpty(username) && "123456".equals(password)){
    //登录成功,防止重复提交
    return "redirect:/main.html";
}else{
    map.put("msg", "用户名密码错误");
    return "login";
}

模板引擎解析

@Override
public void addViewControllers(ViewControllerRegistry registry) {
    registry.addViewController("/").setViewName("login");
    registry.addViewController("/index.html").setViewName("login");
    registry.addViewController("/main.html").setViewName("Dashboard");
}

4、拦截器

作用:实现权限控制,每个页面请求前中后,都会进入到拦截器进行处理(登录权限)

1、在component下新建一个LoginHandlerInterceptor拦截器

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){
            //已经登录
            return true;
        }
        //未经过验证
        request.setAttribute("msg", "没权限请先登录");
        request.getRequestDispatcher("/index.html").forward(request, response);

        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

2、在MyMvcConfig配置中重写拦截器方法,加入到容器中

//所有的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) {
            //静态资源 css js img 已经做好了静态资源映射
            registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**").
                    excludePathPatterns("/index.html","/","/user/login");
        }
    };
    return adapter;
}

3、在LoginHandler中添加登录成功写入session

@Controller
public class LoginController {

    @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) && "123456".equals(password)){
            //登录成功,防止重复提交
            session.setAttribute("loginUser", username);
            return "redirect:/main.html";
        }else{
            map.put("msg", "用户名密码错误");
            return "login";
        }

    }
}

5、CRUD-员工列表

实验要求:

1)、RestfulCRUD:CRUD满足Rest风格

URI:/资源名称/资源标识+HTTP操作

普通CRUD RestfulCRUD
查询 getEmp emp -- GET
添加 addEmp?xxx emp --POST
修改 updateEmp?id=xxx&xxx=xx emp/{id} -- PUT
删除 deleteEmp?id=1 emp/{id} --DELETE

2、实验的请求架构

请求URI 请求方式
查询所有员工 emps GET
查询某个员工 emp/{id} GET
添加页面 emp GET
添加员工 emp POST
修改页面(回显) emp/{id} GET
修改员工 emp/{id} PUT
删除员工 emp/{id} DELETE

3、员工列表

1、公共页面抽取

使用方法

1、抽取公共片段
<!--footer.html-->
<div id="footid" th:fragment="copy">xxx</div>
2、引入公共片段
<!--test.html-->
<div th:insert=~{footer::copy}></div>
~{templatename::selector} 模板名::选择器  footer::#footid
~{templatename::fragmentname} 模板名::片段名称 footer::copy
行内写法可以加~{xx::xx} 标签体可以 xx::xx

三种引用方式

th:insert :加个外层标签 +1

th:replace :完全替换 1

th:include:就替换里面的内容 -1

公共页面

<body>
    ...
    <div th:insert="footer :: copy"></div>
    <div th:replace="footer :: copy"></div>
    <div th:include="footer :: copy"></div>
</body>

结果

<body>
...
    <!-- th:insert -->
    <div>
        <footer>
            © 2011 The Good Thymes Virtual Grocery
        </footer>
    </div>
    <!--th:replace-->
    <footer>
        © 2011 The Good Thymes Virtual Grocery
    </footer>
    <!--th:include-->
    <div>
        © 2011 The Good Thymes Virtual Grocery
    </div>
</body>

用此种方法将公共页面引入

2、列表高亮

引入片段的时候传入参数,新建一个commons文件夹存储公共页面bar.html

模板引入变量名

dashboard

<a class="nav-link active"
   th:class="${activeUri}=='main.html'?'nav-link active':'nav-link'"
   href="https://getbootstrap.com/docs/4.1/examples/dashboard/#" th:href="@{/main.html}">
    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-home"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>
    Dashboard <span class="sr-only">(current)</span>
</a>

员工管理

<li class="nav-item">
    <a class="nav-link"
       th:class="${activeUri}=='emps'?'nav-link active':'nav-link'"
       href="https://getbootstrap.com/docs/4.1/examples/dashboard/#" th:href="@{/emps}">
        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-users"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg>
        员工管理
    </a>

引入模板的时候传入参数

dashboard.html引入

<!--引入侧边栏-->
   <div th:replace="commons/bar :: sidebar(activeUri='main.html')"></div>

list.html引入

<!--引入侧边栏-->
<div th:replace="commons/bar::sidebar(activeUri='emps')"></div>

6、列表数据显示(查)

1、传入员工对象

EmployeeController类,传入员工对象

@Controller
public class EmployeeController {

    @Autowired
    EmployeeDao employeeDao;
    /**
     * 查询所有员工返回列表页面
     */
    @GetMapping(value = "/emps")
    public String list(Model model){

        Collection<Employee> employees = employeeDao.getAll();
        model.addAttribute("emps",employees);
        return "emp/list";
    }
}

2、 遍历对象

list.html中 使用模板的 th:each方法

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}">1</td>
            <td th:text="${emp.lastName}">1</td>
            <td th:text="${emp.email}">1</td>
            <td th:text="${emp.gender}">1</td>
            <td th:text="${emp.department.departmentName}">1</td>
            <td th:text="${#dates.format(emp.birth,'yyyy-MM-dd HH:mm:ss')}">1</td>
            <td>
                <button class="btn btn-sm btn-primary">编辑</button>
                <button class="btn btn-sm btn-danger">删除</button>
            </td>
        </tr>
    </tbody>
</table>

3、效果显示

19.tablelist

7、员工添加(增)

功能:点击添加按钮,出现新增页面

1、新增页面

<form>
    <!-- LastName -->
    <div class="form-group">
        <label for="LastName">LastName</label>
        <input type="text" class="form-control" id="LastName"  placeholder="LastName">
    </div>
    <!-- Email -->
    <div class="form-group">
        <label for="Email">Email</label>
        <input type="email" class="form-control" id="Email"  placeholder="zhangsan@163.com">
    </div>
    <!--gender-->
    <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">
            <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">
            <label class="form-check-label" >女</label>
        </div>
    </div>
    <!-- department -->
    <div class="form-group">
        <label for="exampleFormControlSelect1">department</label>
        <select class="form-control" id="exampleFormControlSelect1">
            <option th:each="dept:${depts}" th:value="${dept.id}" th:text="${dept.departmentName}"></option>
        </select>
    </div>
    <!--Birth-->
    <div class="form-group">
        <label for="birthDate">Birth</label>
        <input type="text" class="form-control" id="birthDate" placeholder="2012-12-12">
    </div>
    <button type="submit" class="btn btn-primary">添 加</button>
</form>

2、页面跳转

在EmployeeController中添加addEmpPage方法

/**
 * 添加员工
 */
@GetMapping(value = "/emp")
public String toAddPage(Model model){
    //来到添加页面,查出所有部门显示
    Collection<Department> depts = departmentDao.getDepartments();
    model.addAttribute("depts",depts);
    return "emp/add";
}

关键点:在添加部门页面要遍历部门信息,所以在方法中出入部门信息

3、添加功能完成

新建一个PostMapping

ThymeleafViewResolver 查看redirect和forward,原生的sendredirect方法;

1、新建一个postMapping的方法用来接受页面的添加POST请求

/**
 * 员工添加
 */
@PostMapping(value = "/emp")
public String addEmp(Employee employee){

    employeeDao.save(employee);
    //来到员工列表页面、redirect:重定向到一个地址,forward转发到一个地址
    return "redirect:/emps";
}

2、修改添加页面,添加name属性

<form th:action="@{/emp}" method="post">
    <!-- LastName -->
    <div class="form-group">
        <label for="LastName">LastName</label>
        <input type="text" class="form-control" id="LastName" name="lastName" placeholder="LastName">
    </div>
    <!-- Email -->
    <div class="form-group">
        <label for="Email">Email</label>
        <input type="email" class="form-control" id="Email"  name="email" placeholder="zhangsan@163.com">
    </div>
    <!--gender-->
    <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">
            <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">
            <label class="form-check-label" >女</label>
        </div>
    </div>
    <!-- department -->
    <div class="form-group">
        <label >department</label>
        <select class="form-control"  name="department.id">
            <option th:each="dept:${depts}" th:value="${dept.id}" th:text="${dept.departmentName}"></option>
        </select>
    </div>
    <div class="form-group">
        <label for="birthDate">Birth</label>
        <input type="text" class="form-control" id="birthDate" placeholder="2012-12-12" name="birth">
    </div>
    <button type="submit" class="btn btn-primary">添 加</button>
</form>

1、部门对象问题?

<select class="form-control"  name="department.id">

2、日期格式化?

属性中添加 date-formate 默认是 /

@Bean
@ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")
public Formatter<Date> dateFormatter() {
   return new DateFormatter(this.mvcProperties.getDateFormat());
}

@Override
public MessageCodesResolver getMessageCodesResolver() {
   if (this.mvcProperties.getMessageCodesResolverFormat() != null) {
      DefaultMessageCodesResolver resolver = new DefaultMessageCodesResolver();
      resolver.setMessageCodeFormatter(
            this.mvcProperties.getMessageCodesResolverFormat());
      return resolver;
   }
   return null;
}
spring.mvc.date-format=yyyy-MM-dd

8、员工编辑(改)

思路使用add页面,并且数据回显,然后区分添加,PUT请求

1、修改按钮

在list.html的编辑按钮加上链接

<td>
    <a  href="#" th:href="@{/emp/}+${emp.id}" class="btn btn-sm btn-primary">编辑</a>
    <button class="btn btn-sm btn-danger">删除</button>
</td>

2、编写跳转页面

跳转到员工编辑页面的Controller

/**
 * 员工编辑页面
 */
@GetMapping(value = "/emp/{id}")
public String toEditPage(@PathVariable("id") Integer id ,Model model){
    Employee emp = employeeDao.getEmpById(id);
    Collection<Department> departments = departmentDao.getDepartments();
    model.addAttribute("emp",emp);
    model.addAttribute("depts",departments);
    return "emp/add";
}

3、对页面修改

对add页面进行修改

1)、添加回显

2)、添加判断是否emp!=null(区分add or edit)

3)、添加put请求 --两个input的hidden标签

 <form th:action="@{/emp}" method="post">
        <!--发送put请求-->
        <!--1.SpringMVC配置HiddenHttpMethodFilter
            2.页面创建一个post表单
            3.创建一个 input name_method 值就是我们请求的方式-->
        <input type="hidden" name="_method" value="put" th:if="${emp!=null}">

        <input type="hidden" name="id" th:value="${emp.id}" th:if="${emp!=null}">
        <!-- LastName -->
        <div class="form-group">
            <label for="LastName">LastName</label>
            <input type="text" class="form-control" id="LastName" name="lastName" placeholder="LastName" th:value="${emp!=null}?${emp.lastName}">
        </div>
        <!-- Email -->
        <div class="form-group">
            <label for="Email">Email</label>
            <input type="email" class="form-control" id="Email"  name="email" placeholder="zhangsan@163.com" th:value="${emp!=null}?${emp.email}">
        </div>
        <!--gender-->
        <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>
        <!-- department -->
        <div class="form-group">
            <label >department</label>
            <select class="form-control"  name="department.id" >
                <option th:selected="${emp!=null}?${dept.id == emp.department.id}" th:each="dept:${depts}" th:value="${dept.id}" th:text="${dept.departmentName}"></option>
            </select>
        </div>
        <div class="form-group">
            <label for="birthDate">Birth</label>
            <input type="text" class="form-control" id="birthDate" placeholder="2012-12-12" name="birth" th:value="${emp!=null}?${#dates.format(emp.birth,'yyyy-MM-dd HH:mm:ss')}">
        </div>
        <button type="submit" class="btn btn-primary" th:text="${emp!=null}?'修改':'添加'">添 加</button>
    </form>
</main>

9、员工删除(删)

1、新建Contoller

/**
 * 员工删除
 */
@DeleteMapping(value = "/emp/{id}")
public String deleteEmp(@PathVariable("id") Integer id){
    employeeDao.deleteEmpById(id);
    return "redirect:/emps";
}

2、修改删除标签

<button th:attr="del_uri=@{/emp/}+${emp.id}"  class="btn btn-sm btn-danger deleteBtn">
    删除
</button>

3、写Form表单

form表单卸载外面,input 中 name="_method" value="delete" 模拟delete请求

                </tbody>
            </table>
        </div>
    </main>
    <form id="deleteEmpForm" method="post">
        <input type="hidden" name="_method" value="delete">
    </form>
</div>

4、写JS提交

<script>
    $(".deleteBtn").click(function () {
        $("#deleteEmpForm").attr("action",$(this).attr("del_uri")).submit();
        return false;
    })
</script>

return false;禁用btn提交效果

7、错误机制的处理

1、默认的错误处理机制

默认错误页面

20.error

原理参照

ErrorMvcAutoConfiguration:错误处理的自动配置

org\springframework\boot\spring-boot-autoconfigure\1.5.12.RELEASE\spring-boot-autoconfigure-1.5.12.RELEASE.jar!\org\springframework\boot\autoconfigure\web\ErrorMvcAutoConfiguration.class
  • DefaultErrorAttributes

    帮我们在页面共享信息

    @Override
    public Map getErrorAttributes(RequestAttributes requestAttributes,
        boolean includeStackTrace) {
     Map errorAttributes = new LinkedHashMap();
     errorAttributes.put("timestamp", new Date());
     addStatus(errorAttributes, requestAttributes);
     addErrorDetails(errorAttributes, requestAttributes, includeStackTrace);
     addPath(errorAttributes, requestAttributes);
     return errorAttributes;
    }
  • BasicErrorController

    @Controller
    @RequestMapping("${server.error.path:${error.path:/error}}")
    public class BasicErrorController extends AbstractErrorController {
      //产生HTML数据
      @RequestMapping(produces = "text/html")
    public ModelAndView errorHtml(HttpServletRequest request,
            HttpServletResponse response) {
        HttpStatus status = getStatus(request);
        Map model = Collections.unmodifiableMap(getErrorAttributes(
                request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
        response.setStatus(status.value());
        ModelAndView modelAndView = resolveErrorView(request, response, status, model);
        return (modelAndView == null ? new ModelAndView("error", model) : modelAndView);
    }
    //产生Json数据
    @RequestMapping
    @ResponseBody
    public ResponseEntity> error(HttpServletRequest request) {
        Map body = getErrorAttributes(request,
                isIncludeStackTrace(request, MediaType.ALL));
        HttpStatus status = getStatus(request);
        return new ResponseEntity>(body, status);
    }
  • ErrorPageCustomizer

    @Value("${error.path:/error}")
    private String path = "/error";//系统出现错误以后来到error请求进行处理,(web.xml)
  • DefaultErrorViewResolver

    @Override
    public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,
        Map model) {
     ModelAndView modelAndView = resolve(String.valueOf(status), model);
     if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
        modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
     }
     return modelAndView;
    }
    
    private ModelAndView resolve(String viewName, Map model) {
      //默认SpringBoot可以找到一个页面?error/状态码
     String errorViewName = "error/" + viewName;
      //如果模板引擎可以解析地址,就返回模板引擎解析
     TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
           .getProvider(errorViewName, this.applicationContext);
     if (provider != null) {
         //有模板引擎就返回到errorViewName指定的视图地址
        return new ModelAndView(errorViewName, model);
     }
      //自己的文件 就在静态文件夹下找静态文件 /静态资源文件夹/404.html
     return resolveResource(errorViewName, model);
    }

一旦系统出现4xx或者5xx错误 ErrorPageCustomizer就回来定制错误的响应规则,就会来到 /error请求,BasicErrorController处理,就是一个Controller

1.响应页面,去哪个页面是由 DefaultErrorViewResolver 拿到所有的错误视图

protected ModelAndView resolveErrorView(HttpServletRequest request,
      HttpServletResponse response, HttpStatus status, Map<String, Object> model) {
   for (ErrorViewResolver resolver : this.errorViewResolvers) {
      ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
      if (modelAndView != null) {
         return modelAndView;
      }
   }
   return null;
}

l浏览器发送请求 accpt:text/html

客户端请求:accept:/*

2、如何定制错误响应

​ 1)、如何定制错误的页面

​ 1.有模板引擎:静态资源/404.html,什么错误什么页面;所有以4开头的 4xx.html 5开头的5xx.html

​ 有精确的404和4xx优先选择404

​ 页面获得的数据

​ timestamp:时间戳

​ status:状态码

​ error:错误提示

​ exception:异常对象

​ message:异常信息

​ errors:JSR303有关

​ 2.没有放在模板引擎,放在静态文件夹,也可以显示,就是没法使用模板取值

​ 3.没有放模板引擎,没放静态,会显示默认的错误

​ 2)、如何定义错误的数据

举例子:新建4xx和5xx文件

21.error-static

<body >
    <p>status: [[${status}]]</p>
    <p>timestamp: [[${timestamp}]]</p>
    <p>error: [[${error}]]</p>
    <p>message: [[${message}]]</p>
    <p>exception: [[${exception}]]</p>
</body>

22.4xxhtml

3、如何定制Json数据

1、仅发送json数据

public class UserNotExitsException extends  RuntimeException {
    public UserNotExitsException(){
        super("用户不存在");
    }
}
/**
 * 异常处理器
 */
@ControllerAdvice
public class MyExceptionHandler {

    @ResponseBody
    @ExceptionHandler(UserNotExitsException.class)
    public Map<String ,Object> handlerException(Exception e){
        Map<String ,Object> map =new HashMap<>();
        map.put("code", "user not exist");
        map.put("message", e.getMessage());
        return map;
    }
}

无法自适应 都是返回的json数据

2、转发到error自适应处理

@ExceptionHandler(UserNotExitsException.class)
public String handlerException(Exception e, HttpServletRequest request){
    Map<String ,Object> map =new HashMap<>();
    //传入自己的状态码
    request.setAttribute("javax.servlet.error.status_code", 432);
    map.put("code", "user not exist");
    map.put("message", e.getMessage());
    //转发到error
    return "forward:/error";
}

程序默认获取状态码

protected HttpStatus getStatus(HttpServletRequest request) {
   Integer statusCode = (Integer) request
         .getAttribute("javax.servlet.error.status_code");
   if (statusCode == null) {
      return HttpStatus.INTERNAL_SERVER_ERROR;
   }
   try {
      return HttpStatus.valueOf(statusCode);
   }
   catch (Exception ex) {
      return HttpStatus.INTERNAL_SERVER_ERROR;
   }

没有自己写的自定义异常数据

3、自适应和定制数据传入

Spring 默认的原理,出现错误后回来到error请求,会被BasicErrorController处理,响应出去的数据是由BasicErrorController的父类AbstractErrorController(ErrorController)规定的方法getAttributes得到的;

1、编写一个ErrorController的实现类【或者AbstractErrorController的子类】,放在容器中;

2、页面上能用的数据,或者是json数据返回能用的数据都是通过errorAttributes.getErrorAttributes得到;

容器中的DefaultErrorAtrributes.getErrorAtrributees();默认进行数据处理

public class MyErrorAttributes extends DefaultErrorAttributes {
    @Override
    public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
        Map<String, Object> map = super.getErrorAttributes(requestAttributes, includeStackTrace);
        map.put("company", "wdjr");
        return map;
    }
}

异常处理:把map方法请求域中

    @ExceptionHandler(UserNotExitsException.class)
    public String handlerException(Exception e, HttpServletRequest request){
        Map<String ,Object> map =new HashMap<>();
        //传入自己的状态码
        request.setAttribute("javax.servlet.error.status_code", 432);
        map.put("code", "user not exist");
        map.put("message", e.getMessage());
        request.setAttribute("ext", map);
        //转发到error
        return "forward:/error";
    }
}

在上面的MyErrorAttributes类中加上

//我们的异常处理器
Map<String,Object> ext = (Map<String, Object>) requestAttributes.getAttribute("ext", 0);
map.put("ext", ext);

8、配置嵌入式servlet容器

1、定制和修改Servlet容器

SpringBoot默认使用Tomcat作为嵌入式的Servlet容器;

![23.tomcat emd](http://cdn.imodou.com.cn/wp-content/uploads/2021/04/23.tomcat emd.jpg)

问题?

1)、如何定制和修改Servlet容器;

1、 修改Server相关的配置文件 application.properties

#通用的servlet容器配置
server.xxx
#tomcat的配置
server.tomcat.xxxx

2、编写一个EmbeddedServletContainerCustomizer;嵌入式的Servlet容器的定制器;来修改Servlet的容器配置

@Bean
public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer(){
    return new EmbeddedServletContainerCustomizer() {
        //定制嵌入式Servlet的容器相关规则
        @Override
        public void customize(ConfigurableEmbeddedServletContainer container) {
            container.setPort(8999);
        }
    };
}

其实同理,都是实现EmbeddedServletContainerCustomizer

2、注册Servlet三大组件

三大组件 Servlet Filter Listener

由于SprringBoot默认是以jar包启动嵌入式的Servlet容器来启动SpringBoot的web应用,没有web.xml

注册三大组件

ServletRegistrationBean

@Bean
public ServletRegistrationBean myServlet(){
    ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new MyServlet(),"/servlet");
    return servletRegistrationBean;
}

MyServlet

public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("Hello Servlet");
    }
}

FilterRegistrationBean

@Bean
public FilterRegistrationBean myFilter(){
    FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
    filterRegistrationBean.setFilter(new MyFilter());
    filterRegistrationBean.setUrlPatterns(Arrays.asList("/hello","/myServlet"));
    return filterRegistrationBean;
}

MyFilter

public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("MyFilter process");
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {

    }
}

ServletListenerRegistrationBean

@Bean
public ServletListenerRegistrationBean myListener(){
    ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean<>(new MyListener());
    return registrationBean;
}

MyListener

public class MyListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println(".........web应用启动..........");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println(".........web应用销毁..........");
    }
}

SpringBoot帮助我们自动配置SpringMVC的时候,自动注册SpringMVC的前端控制器;DispatcherServlet;

@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
   public ServletRegistrationBean dispatcherServletRegistration(
         DispatcherServlet dispatcherServlet) {
      ServletRegistrationBean registration = new ServletRegistrationBean(
            dispatcherServlet, this.serverProperties.getServletMapping());
       //默认拦截 /所有请求 包括静态资源 不包括jsp
       //可以通过server.servletPath来修改SpringMVC前端控制器默认拦截的请求路径
      registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
      registration.setLoadOnStartup(
            this.webMvcProperties.getServlet().getLoadOnStartup());
      if (this.multipartConfig != null) {
         registration.setMultipartConfig(this.multipartConfig);
      }
      return registration;
   }

}

3、切换其他的Servlet容器

在ServerProperties中

private final Tomcat tomcat = new Tomcat();

private final Jetty jetty = new Jetty();

private final Undertow undertow = new Undertow();

tomcat(默认支持)

jetty(长连接)

undertow(多并发)

切换容器 仅仅需要修改pom文件的依赖就可以

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</artifactId>
        </dependency>
<!--        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>-->

4、嵌入式Servlet容器自动配置原理

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@Import(BeanPostProcessorsRegistrar.class)
//给容器导入组件 后置处理器 在Bean初始化前后执行前置后置的逻辑 创建完对象还没属性赋值进行初始化工作
public class EmbeddedServletContainerAutoConfiguration {
    @Configuration
    @ConditionalOnClass({ Servlet.class, Tomcat.class })//当前是否引入tomcat依赖
    //判断当前容器没有用户自定义EmbeddedServletContainerFactory,就会创建默认的嵌入式容器
    @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
    public static class EmbeddedTomcat {

        @Bean
        public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
            return new TomcatEmbeddedServletContainerFactory();
        }

1)、EmbeddedServletContainerFactory(嵌入式Servlet容器工厂)

public interface EmbeddedServletContainerFactory {
    //获取嵌入式的Servlet容器
   EmbeddedServletContainer getEmbeddedServletContainer(
         ServletContextInitializer... initializers);

}

继承关系

24.EmdServletFactory

2)、EmbeddedServletContainer:(嵌入式的Servlet容器)

25.EmdServletContainer

3)、TomcatEmbeddedServletContainerFactory为例

@Override
public EmbeddedServletContainer getEmbeddedServletContainer(
      ServletContextInitializer... initializers) {
   Tomcat tomcat = new Tomcat();
    //配置tomcat的基本环节
   File baseDir = (this.baseDirectory != null ? this.baseDirectory
         : createTempDir("tomcat"));
   tomcat.setBaseDir(baseDir.getAbsolutePath());
   Connector connector = new Connector(this.protocol);
   tomcat.getService().addConnector(connector);
   customizeConnector(connector);
   tomcat.setConnector(connector);
   tomcat.getHost().setAutoDeploy(false);
   configureEngine(tomcat.getEngine());
   for (Connector additionalConnector : this.additionalTomcatConnectors) {
      tomcat.getService().addConnector(additionalConnector);
   }
   prepareContext(tomcat.getHost(), initializers);
    //将配置好的tomcat传入进去;并且启动tomcat容器
   return getTomcatEmbeddedServletContainer(tomcat);
}

4)、嵌入式配置修改

ServerProperties、EmbeddedServletContainerCustomizer

EmbeddedServletContainerCustomizer:定制器帮我们修改了Servlet容器配置?

怎么修改?

5)、容器中导入了EmbeddedServletContainerCustomizerBeanPostProcessor

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
      BeanDefinitionRegistry registry) {
   if (this.beanFactory == null) {
      return;
   }
   registerSyntheticBeanIfMissing(registry,
         "embeddedServletContainerCustomizerBeanPostProcessor",
         EmbeddedServletContainerCustomizerBeanPostProcessor.class);
   registerSyntheticBeanIfMissing(registry,
         "errorPageRegistrarBeanPostProcessor",
         ErrorPageRegistrarBeanPostProcessor.class);
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
      throws BeansException {
    //如果当前初始化的是一个ConfigurableEmbeddedServletContainer
   if (bean instanceof ConfigurableEmbeddedServletContainer) {
      postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean);
   }
   return bean;
}

private void postProcessBeforeInitialization(
    ConfigurableEmbeddedServletContainer bean) {
    //获取所有的定制器,调用每个定制器的customer方法给Servlet容器进行赋值
    for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) {
        customizer.customize(bean);
    }
}

private Collection<EmbeddedServletContainerCustomizer> getCustomizers() {
    if (this.customizers == null) {
        // Look up does not include the parent context
        this.customizers = new ArrayList<EmbeddedServletContainerCustomizer>(
            this.beanFactory
            //从容器中获取所有的这个类型的组件:EmbeddedServletContainerCustomizer
            //定制Servlet,给容器中可以添加一个EmbeddedServletContainerCustomizer类型的组件
            .getBeansOfType(EmbeddedServletContainerCustomizer.class,
                            false, false)
            .values());
        Collections.sort(this.customizers, AnnotationAwareOrderComparator.INSTANCE);
        this.customizers = Collections.unmodifiableList(this.customizers);
    }
    return this.customizers;
}

ServerProperties也是EmbeddedServletContainerCustomizer定制器

步骤:

1)、SpringBoot根据导入的依赖情况,给容器中添加响应的容器工厂 例:tomcat

EmbeddedServletContainerFactory【TomcatEmbeddedServletContainerFactory】

2)、容器中某个组件要创建对象就要通过后置处理器;

EmbeddedServletContainerCustomizerBeanPostProcessor

只要是嵌入式的Servlet容器工厂,后置处理器就工作;

3)、后置处理器,从容器中获取的所有的EmbeddedServletContainerCustomizer,调用定制器的定制方法

5、嵌入式Servlet容器启动原理

什么时候创建嵌入式的Servlet的容器工厂?什么时候获取嵌入式的Servlet容器并启动Tomcat;

获取嵌入式的容器工厂

1)、SpringBoot应用启动Run方法

2)、刷新IOC容器对象【创建IOC容器对象,并初始化容器,创建容器的每一个组件】;如果是web环境AnnotationConfigEmbeddedWebApplicationContext,如果不是AnnotationConfigApplicationContext

if (contextClass == null) {
   try {
      contextClass = Class.forName(this.webEnvironment
            ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
   }

3)、refresh(context);刷新创建好的IOC容器

try {
   // Allows post-processing of the bean factory in context subclasses.
   postProcessBeanFactory(beanFactory);

   // Invoke factory processors registered as beans in the context.
   invokeBeanFactoryPostProcessors(beanFactory);

   // Register bean processors that intercept bean creation.
   registerBeanPostProcessors(beanFactory);

   // Initialize message source for this context.
   initMessageSource();

   // Initialize event multicaster for this context.
   initApplicationEventMulticaster();

   // Initialize other special beans in specific context subclasses.
   onRefresh();

   // Check for listener beans and register them.
   registerListeners();

   // Instantiate all remaining (non-lazy-init) singletons.
   finishBeanFactoryInitialization(beanFactory);

   // Last step: publish corresponding event.
   finishRefresh();
}

4)、 onRefresh();web的ioc容器重写了onRefresh方法

5)、webioc会创建嵌入式的Servlet容器;createEmbeddedServletContainer

6)、获取嵌入式的Servlet容器工厂;

EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();

从ioc容器中获取EmbeddedServletContainerFactory组件;

@Bean
public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
return new TomcatEmbeddedServletContainerFactory();
}

TomcatEmbeddedServletContainerFactory创建对象,后置处理器看这个对象,就来获取所有的定制器来定制Servlet容器的相关配置;

7)、使用容器工厂获取嵌入式的Servlet容器

8)、嵌入式的Servlet容器创建对象并启动Servlet容器;

先启动嵌入式的Servlet容器,在将ioc容器中剩下的没有创建出的对象获取出来

ioc启动创建Servlet容器

9、使用外置的Servlet容器

嵌入式的Servlet容器:应用达成jar包

​ 优点:简单、便携

​ 缺点:默认不支持JSP、优化定制比较复杂(使用定制器【ServerProperties、自定义定制器】,自己来编写嵌入式的容器工厂)

外置的Servlet容器:外面安装Tomcat是以war包的方式打包。

1、IDEA操作外部Servlet

1、创建程序为war程序

26.tomcat1

2、选择版本

27.tomcat2

3、添加tomcat

28.tomcat3

4、选择tomcat

30.tomcat4

5、选择本地的Tomcat

31.tomcat5

6、配置tomcat路径

32.tomcat6

7、添加服务器

33.tomcat7

8、添加exploded的war配置,应用OK tomcat配置完成

34.tomcat8

二、配置webapp文件夹

1、点击配置

35.tomcat9

2、添加webapp目录

36.tomcat10

3、默认配置就可以

37.tomcat11

4、配置web.xml文件

38.tomcat12

5、文档目录结构

39.tomcat13

2、运行一个示例

1、项目目录

40.demo1

2、配置文件写视图解析前后缀

spring.mvc.view.prefix=/WEB-INF/jsp/

spring.mvc.view.suffix=.jsp

3、HelloController

@Controller
public class HelloController {
    @GetMapping("/hello")
    public String hello(Model model){
        model.addAttribute("message","这是Controller传过来的message");
        return "success";
    }
}

4、success.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Success</title>
</head>
<body>
<h1>Success</h1>
message:${message}
</body>
</html>

5、运行结果

41.demo2

步骤

1、必须创建一个war项目;

2、将嵌入式的Tomcat指定为provided

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <scope>provided</scope>
</dependency>

3、必须编写一个SpringBootServletInitializer的子类,并调用configure方法里面的固定写法

public class ServletInitializer extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        //传入SpringBoot的主程序,
        return application.sources(SpringBoot04WebJspApplication.class);
    }

}

4、启动服务器就可以;

3、原理

jar包:执行SpringBoot主类的main方法,启动ioc容器,创建嵌入式的Servlet的容器;

war包:启动服务器,服务器启动SpringBoot应用,【SpringBootServletInitializer】启动ioc容器

servlet3.0规范

8.2.4 共享库和运行时插件

规则:

1、服务器启动(web应用启动),会创建当前的web应用里面每一个jar包里面ServletContrainerInitializer的实现类的实例

2、SpringBootServletInitializer这个类的实现需要放在jar包下的META-INF/services文件夹下,有一个命名为javax.servlet.ServletContainerInitalizer的文件,内容就是ServletContainerInitializer的实现类全类名

3、还可以使用@HandlerTypes注解,在应用启动的时候可以启动我们感兴趣的类

流程:

1、启动Tomcat服务器

2、spring web模块里有这个文件

42.servletContainerInit

org.springframework.web.SpringServletContainerInitializer

3、SpringServletContainerInitializer将handlerTypes标注的所有类型的类传入到onStartip方法的Set<Class<?>>;为这些感兴趣类创建实例

4、每个创建好的WebApplicationInitializer调用自己的onStratup

5、相当于我们的SpringBootServletInitializer的类会被创建对象,并执行onStartup方法

6、SpringBootServletInitializer执行onStartup方法会创建createRootApplicationContext

protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
    SpringApplicationBuilder builder = this.createSpringApplicationBuilder();
    //环境构建器
    StandardServletEnvironment environment = new StandardServletEnvironment();
    environment.initPropertySources(servletContext, (ServletConfig)null);
    builder.environment(environment);
    builder.main(this.getClass());
    ApplicationContext parent = this.getExistingRootWebApplicationContext(servletContext);
    if (parent != null) {
        this.logger.info("Root context already created (using as parent).");
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, (Object)null);
        builder.initializers(new ApplicationContextInitializer[]{new ParentContextApplicationContextInitializer(parent)});
    }

    builder.initializers(new ApplicationContextInitializer[]{new ServletContextApplicationContextInitializer(servletContext)});
    builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class);
    //调用Configure,子类重写了这个方法,将SpringBoot的主程序类传入进来
    builder = this.configure(builder);
    //创建一个spring应用
    SpringApplication application = builder.build();
    if (application.getSources().isEmpty() && AnnotationUtils.findAnnotation(this.getClass(), Configuration.class) != null) {
        application.getSources().add(this.getClass());
    }

    Assert.state(!application.getSources().isEmpty(), "No SpringApplication sources have been defined. Either override the configure method or add an @Configuration annotation");
    if (this.registerErrorPageFilter) {
        application.getSources().add(ErrorPageFilterConfiguration.class);
    }
    //最后启动Spring容器
    return this.run(application);
}

7、Spring的应用就启动完了并且创建IOC容器;

public ConfigurableApplicationContext run(String... args) {
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   FailureAnalyzers analyzers = null;
   configureHeadlessProperty();
   SpringApplicationRunListeners listeners = getRunListeners(args);
   listeners.starting();
   try {
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
      ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);
      Banner printedBanner = printBanner(environment);
      context = createApplicationContext();
      analyzers = new FailureAnalyzers(context);
      prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      listeners.finished(context, null);
      stopWatch.stop();
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass)
               .logStarted(getApplicationLog(), stopWatch);
      }
      return context;
   }
   catch (Throwable ex) {
      handleRunFailure(context, listeners, analyzers, ex);
      throw new IllegalStateException(ex);
   }
}

五、Docker

1、简介

Docker是一个开源的应用容器引擎

将软件编译成一个镜像;然后在镜像里各种软件做好配置,将镜像发布出去,其他的使用这就可以直接使用这个镜像。运行中的这个镜像叫做容器,容器启动速度快,类似ghost操作系统,安装好了什么都有了;

2、Docker的核心概念

docker主机(HOST):安装了Docker程序的机器(Docker直接安装在操作系统上的)

docker客户端(Client):操作docker主机

docker仓库(Registry):用来保存打包好的软件镜像

docker镜像(Image):软件打好包的镜像,放到docker的仓库中

docker容器(Container):镜像启动后的实例(5个容器启动5次镜像)

docker的步骤:

​ 1、安装Docker

​ 2、去Docker仓库找到这个软件对应的镜像;

​ 3、使用Docker运行的这个镜像,镜像就会生成一个容器

​ 4、对容器的启动停止,就是对软件的启动和停止

3、安装Docker

1、安装Linux

安装vxbox并且安装ubuntu

2、在linux上安装docker

1、查看centos版本
# uname -r
3.10.0-693.el7.x86_64
要求:大于3.10
如果小于的话升级*(选做)
# yum update
2、安装docker
# yum install docker
3、启动docker
# systemctl start docker
# docker -v
4、开机启动docker
# systemctl enable docker
5、停止docker
# systemctl stop docker

4、docker的常用操作

1、镜像操作

1、搜索

docker search mysql

默认去docker hub网站查找44.docker1

2、拉取

默认最新版本
# docekr pull mysql
安装指定版本
# docker pull mysql:5.5

3、查看

docker images

4、删除

docker rmi imageid

2、容器操作

软件的镜像(qq.exe) -- 运行镜像 -- 产生一个容器(正在运行的软件)

1、搜索镜像
# docker search tomcat
2、拉取镜像
# docker pull tomcat
3、根据镜像启动容器
[root@lion ~]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
docker.io/tomcat    latest              d3d38d61e402        35 hours ago        549 MB
[root@lion ~]# docker run --name mytomcat -d tomcat:latest
2f0348702f5f2a2777082198795d8059d83e5ee38f430d2d44199939cc63e249
4、查看那个进程正在进行
[root@lion ~]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
2f0348702f5f        tomcat:latest       "catalina.sh run"   41 seconds ago      Up 39 seconds       8080/tcp            mytomcat
5、停止运行中容器
[root@lion ~]# docker stop 2f0348702f5f
2f0348702f5f
6、查看所有容器
[root@lion ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                       PORTS               NAMES
2f0348702f5f        tomcat:latest       "catalina.sh run"   52 minutes ago      Exited (143) 2 minutes ago                       mytomcat
7、启动容器
[root@lion ~]# docker start 2f0348702f5f
8、删除docker容器
[root@lion ~]# docker rm 2f0348702f5f
2f0348702f5f
9、端口映射
[root@lion ~]# docker run --name mytomcat -d -p 8888:8080 tomcat
692c408c220128014df32ecb6324fb388427d1ecd0ec56325580135c58f63b29
虚拟机:8888
容器的:8080
-d:后台运行
-p:主机端口映射到容器端口
浏览器:192.168.179.129:8888
10、docker的日志
[root@lion ~]# docker logs 692c408c2201
11、多个启动
[root@lion ~]# docker run -d -p 9000:8080 --name mytomcat2 tomcat
浏览器:192.168.179.129:9000

更多命令参考docker镜像文档

3、安装Mysql

docker pull mysql
docker run --name mysql001 -e MYSQL_ROOT_PASSWORD -d -p 3307:3306 mysql

六、数据访问

1、整合JDBC数据源

1、新建项目 spring-boot-06-data-jdbc

  • WEB
  • Mysql
  • JDBC
  • SpringBoot1.5

2、编写配置文件appliction.yml

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

3、编写测试类测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBoot06DataJdbcApplicationTests {

    @Autowired
    DataSource dataSource;

    @Test
    public void contextLoads() throws SQLException {
        System.out.println(dataSource.getClass());

        Connection connection = dataSource.getConnection();
        System.out.println(connection);
        connection.close();
    }

}

4、测试结果

class org.apache.tomcat.jdbc.pool.DataSource
ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@c35af2a]]

数据源相关配置都在DataSourceProperties属性里

自动配置原理

E:\Develop\Maven_Repo\org\springframework\boot\spring-boot-autoconfigure\1.5.13.RELEASE\spring-boot-autoconfigure-1.5.13.RELEASE.jar!\org\springframework\boot\autoconfigure\jdbc

1、DataSource

参考DataSourceConfiguration,根据配置创建数据源,默认是使用tomcat连接池,可以使用spring.datasource.type指定自定义的数据源

2、SpringBoot默认支持

Tomcat数据源
HikariDataSource
dbcp.BasicDataSource
dbcp2.BasicDataSource

3、自定义数据源

 */
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type")
static class Generic {

   @Bean
   public DataSource dataSource(DataSourceProperties properties) {
       //使用builder创建数据源,利用反射创建相应的type数据源,并绑定数据源
      return properties.initializeDataSourceBuilder().build();
   }

}

4、运行sql建表

在DataSourceAutoConfiguration中DataSourceInitializer

监听器

作用:

1)、postConstruct -》runSchemaScript 运行建表sql文件

2)、runDataScript运行插入数据的sql语句;

默认只需要将文件命名为:

schema-*.sql data-*.sql
默认规则:schema.sql ,schema-all.sql;

举个栗子

创建department表

1、department.sql

/*
Navicat MySQL Data Transfer

Source Server         : 192.168.179.131
Source Server Version : 50719
Source Host           : 192.168.179.131:3306
Source Database       : jdbc

Target Server Type    : MYSQL
Target Server Version : 50719
File Encoding         : 65001

Date: 2018-05-14 14:28:52
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for department
-- ----------------------------
DROP TABLE IF EXISTS `department`;
CREATE TABLE `department` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `departmentName` varchar(255) DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2、将department.sql命名为schema-all.sql

45.schema-all

3、运行测试类

自定义sql的文件名,department.sql在配置文件中

schema:
  - classpath:department.sql

5、操作JdbcTemplate

FBI warning:将department.sql删除或者改名,因为运行文件会将表中数据清除

1、新建一个Controller

@Controller
public class HelloController {

    @Autowired
    JdbcTemplate jdbcTemplate;

    @ResponseBody
    @GetMapping("/hello")
    public Map<String ,Object> hello(){

        List<Map<String, Object>> list = jdbcTemplate.queryForList("select * from department");
        return list.get(0);
    }
}

2、表中添加数据

46.department

3、访问请求查询数据

47.hello

2、自定义数据源

1、导入Druid的依赖

<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.9</version>
</dependency>

2、修改配置文件

spring:
  datasource:
    username: root
    password: Welcome_1
    url: jdbc:mysql://192.168.179.131:3306/jdbc
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
#    schema:
#      - classpath:department.sql
server:
  port: 9000

已经替换了原来的tomcat数据源

3、配置Druid数据源配置

spring:
  datasource:
    username: root
    password: Welcome_1
    url: jdbc:mysql://192.168.179.131:3306/jdbc
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    # 初始化大小,最小,最大  
    initialSize: 5
    minIdle: 5
    maxActive: 20
    # 配置获取连接等待超时的时间  
    maxWait: 60000
    # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 
    timeBetweenEvictionRunsMillis: 60000
    # 配置一个连接在池中最小生存的时间,单位是毫秒 
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
    # 配置监控统计拦截的filters,去掉监控界面sql无法统计,‘wall’用于防火墙
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    userGlobalDataSourceStat: true
    # 通过connectProperties属性来打开mergeSql功能;慢SQL记录  
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
#    schema:
#      - classpath:department.sql
server:
  port: 9000

4、Druid配置监控

@Configuration
public class DruidConfig {

    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource druid(){
        return  new DruidDataSource();
    }

    //配置Druid的监控
    //1、配置一个管理后台
    @Bean
    public ServletRegistrationBean statViewServlet(){
        ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(),"/druid/*");
        Map<String,String> initParams =new HashMap<>();
        initParams.put("loginUsername", "admin");
        initParams.put("loginPassword", "123456");
        bean.setInitParameters(initParams);
        return bean;
    }
    //2、配置监控的filter
    @Bean
    public FilterRegistrationBean webstatFilter(){
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new WebStatFilter());

        Map<String,String> initParams =new HashMap<>();
        initParams.put("exclusions", "*.js,*.css,/druid/*");
        bean.setInitParameters(initParams);
        bean.setUrlPatterns(Arrays.asList("/*"));
        return bean;
    }

}

5、运行测试,访问 localhost:9000/druid

48.druid

输入刚才调好的用户名密码即可访问

3、整合Mybatis

1、新建工程,SpringBoot1.5+web+JDBC+Mysql

导入依赖

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.9</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

2、导入配置文件中关于Druid的配置

​ 2.1、导入依赖

​ 2.2、配置文件application.yml(指定用户名密码...配置Druid的配置参数,修改sql文件加载的默认名)

​ 2.3、将Druid组件加入到容器中(监控)重点

​ 具体同上

3、创建数据表department和employee表

​ 3.1、根据sql文件,新建两张表

​ 3.2、修改加载的sql名(默认为schema.sql和schema-all.sql)

spring:
  datasource:
    schema:
      - classpath:sql/department.sql
      - classpath:sql/employeee.sql

​ 3.3、运行程序检查数据库是否创建成功

4、创建数据库对应的JavaBean (驼峰命名,getter/setter toString/注释掉schema防止重复创建)

在配置文件中修改驼峰命名开启 ,不写配置文件就写配置类

mybatis:
  configuration:
    map-underscore-to-camel-case: true
//类名冲突所以全类名
@org.springframework.context.annotation.Configuration
public class MyBatisConfig {

    @Bean
    public ConfigurationCustomizer configurationCustomizer(){

        return new ConfigurationCustomizer() {
            @Override
            public void customize(Configuration configuration) {
                configuration.setMapUnderscoreToCamelCase(true);
            }
        };
    }
}

注解方式

5、新建mapper

//指定是一个mapper
@Mapper
public interface DepartmentMapper {

    @Insert("insert into department(dept_name) value(#{deptName})")
    public int insertDept(Department department);

    @Delete("delete from department where id=#{id}")
    public int deleteDeptById(Integer id);

    @Update("update department set dept_Name=#{deptName} where id=#{id}")
    public int updateDept(Department department);

    @Select("select * from department where id=#{id}")
    public Department getDeptById(Integer id);

}

6、编写controller测试

@RestController
public class DeptController {

    @Autowired
    DepartmentMapper departmentMapper;

    @RequestMapping("/getDept/{id}")
    public Department getDepartment(@PathVariable("id") Integer id){
        return departmentMapper.getDeptById(id);
    }

    @RequestMapping("/delDept/{id}")
    public int delDept(@PathVariable("id") Integer id){
        return departmentMapper.deleteDeptById(id);
    }

    @RequestMapping("/update/{id}")
    public int updateDept(@PathVariable("id") Integer id){
        return departmentMapper.updateDept(new Department(id, "开发部"));
    }

    @GetMapping("/insert")
    public int insertDept(Department department){
        return departmentMapper.insertDept(department);
    }
}

问题:

mapper文件夹下有多个mapper文件,加麻烦,可以直接扫描整个mapper文

件夹下的mapper

//主配置类或者mybatis配置类
@MapperScan(value = "com.wdjr.springboot.mapper")

配置文件方式

1、新建文件

50.mybatisxml

2、新建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>

3、新建Employee的接口方法

public interface EmployeeMapper {

    public Employee getEmpById(Integer id);

    public void insetEmp(Employee employee);
}

4、新建Employee的mapper.xml的映射文件

<?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.wdjr.springboot.mapper.EmployeeMapper">
    <select id="getEmpById" resultType="com.wdjr.springboot.bean.Employee">
      select * from employee where id=#{id}
   </select>

    <insert id="insetEmp">
        INSERT  INTO employee(last_name,email,gender,d_id) VALUES (#{lastName},#{email},#{gender},#{dId})
    </insert>
</mapper>

5、修改application.yml配置文件

mybatis:
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml

6、新建一个Controller访问方法

@RestController
public class EmployeeController {
    @Autowired
    EmployeeMapper employeeMapper;

    @RequestMapping("/getEmp/{id}")
    public Employee getEmp(@PathVariable("id") Integer id){
        return employeeMapper.getEmpById(id);
    }

    @GetMapping("/insertEmp")
    public Employee insertEmp(Employee employee){
        employeeMapper.insetEmp(employee);
        return employee;
    }
}

4、JPA数据访问

新建工程 springBoot1.5+Web+JPA+MYSQL+JDBC

目录结构

51.JPA

1、新建一个实体类User

//使用JPA注解配置映射关系
@Entity//告诉JPA这是一个实体类(和数据表映射的类)
@Table(name="tbl_user") //@Table来指定和那个数据表对应,如果省略默认表明就是user;

public class User {

    @Id //这是一个主键
    @GeneratedValue(strategy = GenerationType.IDENTITY)//自增组件
    private Integer id ;

    @Column(name="last_name",length = 50) //这是和数据表对应的一个列
    private String lastName;
    @Column//省略默认列名就是属性名
    private String email;
    @Column
    public Integer getId() {
        return id;
    }

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

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

2、新建一个UserRepository来继承jpa的绝大多数功能

//继承jpaRepository
public interface UserRepository extends JpaRepository<User,Integer> {

}

3、编写配置文件application.yml

spring:
  datasource:
    url: jdbc:mysql://192.168.179.131/jpa
    username: root
    password: Welcome_1
    driver-class-name: com.mysql.jdbc.Driver
  jpa:
    hibernate:
    #更新或创建
      ddl-auto: update
    show-sql: true

4、编写Controller测试

@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("/insert")
    public User insertUser(User user){
        User user1 = userRepository.save(user);
        return  user1;
    }
}

七、启动配置原理

几个重要的事件回调机制

加载配置文件META-INF/spring.factories

​ ApplicationContextInitializer

​ SpringApplicationRunListener

ioc容器中

​ ApplicationRunner

​ CommandLineRunner

启动流程

1、创建SpringApplicaiotn对象

private void initialize(Object[] sources) {
    //保存主配置类
   if (sources != null && sources.length > 0) {
      this.sources.addAll(Arrays.asList(sources));
   }
    //判断当前是否是个web应用
   this.webEnvironment = deduceWebEnvironment();
    //从类路径下找到META-INF/spring.factories配置中的所有ApplicationInitializer 然后保存起来
   setInitializers((Collection) getSpringFactoriesInstances(
         ApplicationContextInitializer.class));
    //从类路径下找到META-INF/spring.factories配置中的所有ApplicationListener 然后保存起来
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    //决定哪一个是主程序
   this.mainApplicationClass = deduceMainApplicationClass();
}

ApplicationInitializer

52.applicationCotextInitializer

ApplicationListener

53.Listener

2、运行Run方法

public ConfigurableApplicationContext run(String... args) {
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   FailureAnalyzers analyzers = null;
   configureHeadlessProperty();
    //获取SpringApplicationRunListeners;从类路径下META-INF/spring.factory
   SpringApplicationRunListeners listeners = getRunListeners(args);
    //回调所有的SpringApplicationRunListener.starting()方法
   listeners.starting();
   try {
       //封装命令行参数
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
       //准备环境
      ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);
       //创建环境,完成后回调SpringApplicationRunListener.environmentPrepared环境准备完成
       //打印SpringBoot图标
      Banner printedBanner = printBanner(environment);
       //创建ApplicationContext,决定创建web的ioc容器还是普通的ioc
      context = createApplicationContext();
       //异常分析
      analyzers = new FailureAnalyzers(context);
       //重点:将environment保存的ioc中,applyInitializers初始化器上面那6个的获取,并且回调ApplicationContextInitializer.initialize方法

       //回调所有的SpringApplicationRunListener的contextPrepare()
       //告诉prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded
      prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
       //重要:刷新所有组件 ioc容器初始化,如果是web应用还会创建嵌入式的tomcat
       //扫描 创建加载所有组件的地方
      refreshContext(context);
       //从ioc中获取所有的ApplicationRunner和CommandLineRunner
       //ApplicationRunner先回调
      afterRefresh(context, applicationArguments);
       //所有的SpringApplicationRunListener回调finished方法
      listeners.finished(context, null);
       //保存应用状态
      stopWatch.stop();
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass)
               .logStarted(getApplicationLog(), stopWatch);
      }
       //整个springboot启动完成以后返回启动的ioc容器
      return context;
   }
   catch (Throwable ex) {
      handleRunFailure(context, listeners, analyzers, ex);
      throw new IllegalStateException(ex);
   }
}

3、事件监听机制

新建listener监听

文件目录

54.listener2

1、HelloApplicationContextInitializer

//泛型监听ioc容器
public class HelloApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("ApplicationContextInitializer...跑起来了....."+applicationContext);
    }
}

2、HelloSpringApplicationRunListener

加构造器

public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {

    public HelloSpringApplicationRunListener(SpringApplication application, String[] args){

    }

    @Override
    public void starting() {
        System.out.println("监听容器开始......");
    }

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        System.out.println("环境准备好了......"+environment.getSystemProperties().get("os.name"));
    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        System.out.println("ioc容器准备好了......");
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        System.out.println("容器环境已经加载完成......");
    }

    @Override
    public void finished(ConfigurableApplicationContext context, Throwable exception) {
        System.out.println("全部加载完成......");
    }
}

3、HelloApplicationRunner

@Component
public class HelloApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("ApplicationRunner.....run....");
    }
}

4、HelloCommandLineRunner

@Component
public class HelloCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("CommandLineRunner......run....."+Arrays.asList(args));
    }
}

事件运行方法

HelloApplicationContextInitializer和HelloSpringApplicationRunListener文件META-INF/spring.factories中加入

# Initializers
org.springframework.context.ApplicationContextInitializer=\
com.wdjr.springboot.listener.HelloApplicationContextInitializer

org.springframework.boot.SpringApplicationRunListener=\
com.wdjr.springboot.listener.HelloSpringApplicationRunListener

HelloApplicationRunner和HelloCommandLineRunner ioc加入

@Component

八、SpringBoot的自定义starter

starter:场景启动器

1、场景需要使用什么依赖?

2、如何编写自动配置

@Configuration //指定这个类是一个配置类
@ConditionalOnXXX //在指定条件下成立的情况下自动配置类生效
@AutoConfigureAfter //指定自动配置类的顺序
@Bean //给容器中添加组件

@ConfigurationProperties //结合相关xxxProperties类来绑定相关的配置
@EnableConfigurationProperties //让xxxProperties生效加到容器中

自动配置类要能加载
将需要启动就加载的自动配置类,配置在META-INF/spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\

3、模式

启动器空的jar只需要做依赖管理导入;

专门写一个自动配置模块;

启动器依赖自动配置,别人只需要引入starter

xxx-spring-boot-starter

新建一个starter

绕的你怀疑人生

1、新建一个空项目工程

56.starter01

2、项目命名

57.starter02

3、导入module

58.starter03

4、新建一个Maven工程

59.starter04

5、项目命名

60.starter05

61.starter06

6、在新建一个autoconfiguration类的spring

62.starter07

7、项目命名

63.starter08

8、无需导入依赖

64.starter09

9、next

65.starter10

最后配置完成

2、编写starter

autoconfigurer

lxy-spring-boot-starter-autoconfigurer进行删减

目录

66.starter-build01

2、pom文件修改

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

    </dependencies>

</project>

3、编写相关的类

67.starter-build02

4、HelloProperties

package com.lxy.starter;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "lxy.hello")
public class HelloProperties {
    private String prefix;
    private String suffix;

    public String getPrefix() {
        return prefix;
    }

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

    public String getSuffix() {
        return suffix;
    }

    public void setSuffix(String suffix) {
        this.suffix = suffix;
    }
}

5、HelloService

package com.lxy.starter;

public class HelloService {

    HelloProperties helloProperties;

    public HelloProperties getHelloProperties() {
        return helloProperties;
    }

    public void setHelloProperties(HelloProperties helloProperties) {
        this.helloProperties = helloProperties;
    }

    public String sayHello(String name){
        return helloProperties.getPrefix()+name+helloProperties.getSuffix();
    }
}

6、HelloServiceAutoConfiguration

package com.lxy.starter;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnWebApplication
@EnableConfigurationProperties(HelloProperties.class)
public class HelloServiceAutoConfiguration {

    @Autowired
    HelloProperties helloProperties;
    @Bean
    public HelloService helloService(){
        HelloService service = new HelloService();
        service.setHelloProperties(helloProperties);
        return service;
    }

}

7、配置文件

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.lxy.starter.HelloServiceAutoConfiguration

8、修改lxy-spring-boot-starter 也就是之前的Maven项目,修改pom文件引入autoconfiguration依赖

<dependencies>
    <dependency>
        <groupId>com.lxy.starter</groupId>
        <artifactId>lxy-spring-boot-starter-autoconfigurer</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
</dependencies>

9、install生成

68.starter-build03

3、测试

新建一个springboot 1.5+web

1、引入starter

    <dependency>
        <groupId>com.lxy.starter</groupId>
        <artifactId>lxy-spring-boot-starter</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

</dependencies>

2、新建一个Controller用来测试

@RestController
public class HelloController {

    @Autowired
    HelloService helloService;

    @GetMapping
    public  String hello(){
        return helloService.sayHello("test");
    }
}

3、编写配置文件制定前缀和后缀名

lxy.hello.prefix=Starter-
lxy.hello.suffix=-Success

4、运行访问http://localhost:8080/hello

70.starter-build05

成功爽啊

赞(1) 打赏
未经允许不得转载:魔豆IT » Spring Boot入门简介
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!