一、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);就是给容器中导入这个场景需要的所有组件,并配置 好这些组件。

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指定的值;

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

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: 9000path: /hello 

属性和值也是大小写敏感

2、值的写法

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

k: v:字面直接来写;

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

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

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

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

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

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

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

对象还是k:v的方式

frends:lastName: zhangsanage: 20 

行内写法

friends: {lastName: zhangsan,age: 18} 

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

pets:‐ cat‐ dog‐ pig 

行内写法

pets: [cat,dog,pig] 

组合变量

多个组合到一起

3、配置文件值注入

1、@ConfigurationProperties

1、application.yml 配置文件

person:age: 18boss: falsebirth: 2017/12/12maps: {k1: v1,k2: 12}lists:- lisi- zhaoliudog:name: wangwangage: 2last-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 {@Emailprivate 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)

/*** @Configuration:指明当前类是一个配置类;就是来代替之前的Spring配置文件** 在配置文件中用<bean></bean>标签添加组件*/@Configuration
public class MyAppConfig {//将方法的返回值添加到容器中;容器这个组件id就是方法名@Beanpublic 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

优先级大于配置文件

打包 成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

还可以通过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@ConditionalOnMissingBeanpublic 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");}
}

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

2、遗留问题

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

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

核心:

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

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

3、导入slf4j的其他实现

3、SpingBoot日志框架解析

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

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>

总结:

  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的方式;

<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包的方式引入静态资源

WebJars - Web Libraries in Jars

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

2、本地资源

private String staticPathPattern = "/**";

访问任何资源

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

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

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

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;}@Beanpublic SimpleUrlHandlerMapping faviconHandlerMapping() {SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);//把任何favicon的图标都在静态文件夹下找mapping.setUrlMap(Collections.singletonMap("**/favicon.ico",faviconRequestHandler()));return mapping;}@Beanpublic 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>

直接访问HTML页面

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 theywould 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 , falseNull literal: nullLiteral 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 , orBoolean 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).

  • 自动注册 了ConverterGenericConverterFormatter beans.

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

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

      @Bean
      @ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")//在文件配置入职格式化的规则
      public Formatter<Date> 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 {@Beanpublic 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 RequestMappingHandlerMappingRequestMappingHandlerAdapter 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 {@Overridepublic 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);@Overrideprotected 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() {@Overridepublic 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、编写国际化配置文件,抽取页面需要的显示的国际化消息

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

@ConfigurationProperties(prefix = "spring.messages")
public class MessageSourceAutoConfiguration {//我们的配置文件可以直接放在类路径下叫messages.propertiesprivate String basename = "messages";@Beanpublic 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的编码进行设置

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国际化信息(截图就是这么犀利)

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 {//解析区域信息@Overridepublic 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;}@Overridepublic 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 {//目标方法执行之前@Overridepublic 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;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}

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

//所有的webMvcConfigurerAdapter组件会一起起作用
@Bean //註冊到容器去
public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/").setViewName("login");registry.addViewController("/index.html").setViewName("login");registry.addViewController("/main.html").setViewName("Dashboard");}//注册拦截器@Overridepublic 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>&copy; 2011 The Good Thymes Virtual Grocery</footer></div><!--th:replace--><footer>&copy; 2011 The Good Thymes Virtual Grocery</footer><!--th:include--><div>&copy; 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 {@AutowiredEmployeeDao 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.table list](E:\工作文档\SpringBoot\images\19.table list.jpg)

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配置HiddenHttpMethodFilter2.页面创建一个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、默认的错误处理机制

默认错误页面

原理参照

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<String, Object> getErrorAttributes(RequestAttributes requestAttributes,boolean includeStackTrace) {Map<String, Object> errorAttributes = new LinkedHashMap<String, Object>();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<String, Object> 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@ResponseBodypublic ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {Map<String, Object> body = getErrorAttributes(request,isIncludeStackTrace(request, MediaType.ALL));HttpStatus status = getStatus(request);return new ResponseEntity<Map<String, Object>>(body, status);}
  • ErrorPageCustomizer

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

    @Override
    public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,Map<String, Object> 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<String, Object> 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.htmlreturn 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文件

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

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());//转发到errorreturn "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 {@Overridepublic 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);//转发到errorreturn "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](E:\工作文档\SpringBoot\images\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的容器相关规则@Overridepublic 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 {@Overrideprotected 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 {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {System.out.println("MyFilter process");chain.doFilter(request, response);}@Overridepublic void destroy() {}
}

ServletListenerRegistrationBean

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

MyListener

public class MyListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {System.out.println(".........web应用启动..........");}@Overridepublic 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 {@Beanpublic TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {return new TomcatEmbeddedServletContainerFactory();}

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

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

继承关系

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

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 {//如果当前初始化的是一个ConfigurableEmbeddedServletContainerif (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 contextthis.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程序

2、选择版本

3、添加tomcat

4、选择tomcat

5、选择本地的Tomcat

6、配置tomcat路径

7、添加服务器

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

二、配置webapp文件夹

1、点击配置

2、添加webapp目录

3、默认配置就可以

4、配置web.xml文件

5、文档目录结构

2、运行一个示例

1、项目目录

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、运行结果

步骤

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 {@Overrideprotected 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模块里有这个文件

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网站查找

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: rootpassword: Welcome_1url: jdbc:mysql://192.168.179.131:3306/jdbcdriver-class-name: com.mysql.jdbc.Driver

3、编写测试类测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBoot06DataJdbcApplicationTests {@AutowiredDataSource dataSource;@Testpublic 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 {@Beanpublic 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 TransferSource Server         : 192.168.179.131
Source Server Version : 50719
Source Host           : 192.168.179.131:3306
Source Database       : jdbcTarget Server Type    : MYSQL
Target Server Version : 50719
File Encoding         : 65001Date: 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

3、运行测试类

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

schema:- classpath:department.sql

5、操作JdbcTemplate

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

1、新建一个Controller

@Controller
public class HelloController {@AutowiredJdbcTemplate jdbcTemplate;@ResponseBody@GetMapping("/hello")public Map<String ,Object> hello(){List<Map<String, Object>> list = jdbcTemplate.queryForList("select * from department");return list.get(0);}
}

2、表中添加数据

3、访问请求查询数据

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: rootpassword: Welcome_1url: jdbc:mysql://192.168.179.131:3306/jdbcdriver-class-name: com.mysql.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSource
#    schema:
#      - classpath:department.sql
server:port: 9000

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

3、配置Druid数据源配置

spring:datasource:username: rootpassword: Welcome_1url: jdbc:mysql://192.168.179.131:3306/jdbcdriver-class-name: com.mysql.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSource# 初始化大小,最小,最大  initialSize: 5minIdle: 5maxActive: 20# 配置获取连接等待超时的时间  maxWait: 60000# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 timeBetweenEvictionRunsMillis: 60000# 配置一个连接在池中最小生存的时间,单位是毫秒 minEvictableIdleTimeMillis: 300000validationQuery: SELECT 1 FROM DUALtestWhileIdle: truetestOnBorrow: falsetestOnReturn: falsepoolPreparedStatements: true# 配置监控统计拦截的filters,去掉监控界面sql无法统计,‘wall’用于防火墙filters: stat,wall,log4jmaxPoolPreparedStatementPerConnectionSize: 20userGlobalDataSourceStat: 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")@Beanpublic DataSource druid(){return  new DruidDataSource();}//配置Druid的监控//1、配置一个管理后台@Beanpublic 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@Beanpublic 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

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

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 {@Beanpublic ConfigurationCustomizer configurationCustomizer(){return new ConfigurationCustomizer() {@Overridepublic 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 {@AutowiredDepartmentMapper 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、新建文件

2、新建mybatis的配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//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 mapperPUBLIC "-//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.xmlmapper-locations: classpath:mybatis/mapper/*.xml

6、新建一个Controller访问方法

@RestController
public class EmployeeController {@AutowiredEmployeeMapper 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

目录结构

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;@Columnpublic 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/jpausername: rootpassword: Welcome_1driver-class-name: com.mysql.jdbc.Driverjpa:hibernate:#更新或创建ddl-auto: updateshow-sql: true

4、编写Controller测试

@RestController
public class UserController {@AutowiredUserRepository 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

ApplicationListener

2、运行Run方法

public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;FailureAnalyzers analyzers = null;configureHeadlessProperty();//获取SpringApplicationRunListeners;从类路径下META-INF/spring.factorySpringApplicationRunListeners 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容器还是普通的ioccontext = createApplicationContext();//异常分析analyzers = new FailureAnalyzers(context);//重点:将environment保存的ioc中,applyInitializers初始化器上面那6个的获取,并且回调ApplicationContextInitializer.initialize方法//回调所有的SpringApplicationRunListener的contextPrepare()//告诉prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoadedprepareContext(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监听

文件目录

1、HelloApplicationContextInitializer

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

2、HelloSpringApplicationRunListener

加构造器

public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {public HelloSpringApplicationRunListener(SpringApplication application, String[] args){}@Overridepublic void starting() {System.out.println("监听容器开始......");}@Overridepublic void environmentPrepared(ConfigurableEnvironment environment) {System.out.println("环境准备好了......"+environment.getSystemProperties().get("os.name"));}@Overridepublic void contextPrepared(ConfigurableApplicationContext context) {System.out.println("ioc容器准备好了......");}@Overridepublic void contextLoaded(ConfigurableApplicationContext context) {System.out.println("容器环境已经加载完成......");}@Overridepublic void finished(ConfigurableApplicationContext context, Throwable exception) {System.out.println("全部加载完成......");}
}

3、HelloApplicationRunner

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

4、HelloCommandLineRunner

@Component
public class HelloCommandLineRunner implements CommandLineRunner {@Overridepublic 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.HelloApplicationContextInitializerorg.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、新建一个空项目工程

2、项目命名

3、导入module

4、新建一个Maven工程

5、项目命名

6、在新建一个autoconfiguration类的spring

7、项目命名

8、无需导入依赖

9、next

最后配置完成

2、编写starter

autoconfigurer

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

目录

2、pom文件修改

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

3、编写相关的类

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 {@AutowiredHelloProperties helloProperties;@Beanpublic 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生成

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 {@AutowiredHelloService helloService;@GetMappingpublic  String hello(){return helloService.sayHello("test");}
}

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

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

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

成功爽啊

Spring Boot保姆级入门,还不会过来胖我相关推荐

  1. Spring boot 保姆级教程,包学包会,授之以渔

     相信很多初学java的朋友们,第一次接触spring boot肯定是或多或少有些懵的,尤其是对于 那些跳过spring直接学习spring boot的朋友们更是如此,不知道要踩多少坑,今天给朋友们带 ...

  2. 动态规划27k字超详细保姆级入门讲解——附DP经典线性、区间、二维图、四维8个模型题解

    动态规划27k字超详细保姆级入门讲解 写在前面: 这篇文章是目前为止我写过最长也是最久的文章,前面关于DP的讲解我查阅了大量的博客资料,学习其他博主对DP的理解,也翻阅了很多经典的纸质书籍,同时做了近 ...

  3. Spring Boot Vue Element入门实战(完结)

    最近给朋友做一个大学运动会管理系统,用作教学案例,正好自己也在自学VUE,决定用spring boot vue做一个简单的系统.vue这个前端框架很火,他和传统的Jquery 编程思路完全不一样,Jq ...

  4. 【C++保姆级入门】分支结构拓展与优化

    哈喽大家好,我是iecne,本期为大家带来的是CPP/C++保姆级入门教程的第⑧期-- 分支拓展 ,包教包会,快来看看吧! 此教程适合新手小白,因为语言会十分的通俗易懂,不会有很多的专业词汇出现,可以 ...

  5. Spring Boot的快速入门

    Spring Boot的快速入门 1.导入第一个maven项目 自动生成maven项目 2.了解maven项目结构 3.启动服务 和普通maven项目结构是一样的,值得注意的是,启动服务的入口是 ![ ...

  6. Spring Boot 热部署入门

    转载自  Spring Boot 热部署入门 1. 概述 在日常开发中,我们需要经常修改 Java 代码,手动重启项目,查看修改后的效果.如果在项目小时,重启速度比较快,等待的时间是较短的.但是随着项 ...

  7. 几个超好的Spring boot实战项目 (还不赶紧收藏起来)

    Spring boot实战项目 (还不赶紧收藏起来) 学了Spring boot有一段时间了,但是实战的经验还是比较缺乏.所以自己也是在GItHub和Gitee上找了一些超好的Spring boot项 ...

  8. Spring Boot 2.x 入门前的准备-IntelliJ IDEA 开发工具的安装与使用

    ntelliJ IDEA 开发工具的安装与使用 1 下载 IntelliJ IDEA 2 破解版本安装 2.1 window 下的破解 2018.3.1最新版破解 2.2 window 下的破解 20 ...

  9. Midjourney 绘画保姆级入门 图片教程

    对于想了解Midjourney 绘画的同学们,现在有福利了.Midjourney 绘画保姆级入门及图片教程现分享给大家参考. 一.账号申请( 5个步骤 ) 二.初步登录(3个流程 ) 三.指令教程( ...

最新文章

  1. PluckerNet:一种基于3D线匹配的配准网络(CVPR2021)
  2. 全球首条瓷绝缘子自动化生产线:黏土巧造“瓷器活”
  3. 通过Visual Studio为Linux编写C++代码
  4. php格式化金额函数分享
  5. Linux提权:常用三种方法
  6. 【专升本计算机】最新甘肃省专升本考试C语言部分复习题带答案
  7. [css] 为什么说css中能用子代选择器的时候不要用后代选择器?
  8. 第 3-3 课:泛型和迭代器 + 面试题
  9. 最常见的Web服务器市场份额
  10. 基于LD3320的嵌入式语音识别系统设计
  11. C# Readonly和Const的区别
  12. [数分提高]2014-2015-2第2教学周第1次课
  13. 利用jsoup进行模拟登录
  14. Linux花生壳使用篇
  15. Vivado中各个文件的含义
  16. 鱼塘钓鱼(贪心算法)--算法设计
  17. 通过任意数量点拟合曲线
  18. 小猫爪:嵌入式小知识01-存储器
  19. 《编程之美》 查找最大(小)的k个元素
  20. ALEXA站长全攻略(转)

热门文章

  1. 我们怎样处理垃圾网站举报?
  2. 【Python案例】python批量采集下载dy视频
  3. Linux服务器:Linux中VMware虚拟机硬盘空间扩大方法
  4. 山石岩读丨一文读懂区块链安全:区块链到底是什么?
  5. Sentence-BERT 论文阅读
  6. c语言怎么用scanf输入字母,c语言输入字符串(c语言怎么用scanf输入字符串)
  7. vue零基础——vue2基础知识
  8. Linux查看安装包
  9. 设计简约的游戏耳机,还支持主动降噪,双飞燕血手幽灵M90体验
  10. 陈松松:经验告诉我们,做视频营销这11条标准一条也不能缺