1.Spring的发展
回顾:Spring是一个开源框架,2003年兴起的一个轻量级的Java 开发框架,作者:Rod Johnson。Spring是为了解决企业级应用开发的复杂性而创建的,简化开发。
1.1 Spring1.x时代
在Spring1.x时代,都是通过xml文件配置bean,随着项目的不断扩大,需要将xml配置分放到不同的配置文件中,需要频繁的在java类和xml配置文件中切换。
1.2 Spring2.x时代
随着JDK 1.5带来的注解支持,Spring2.x可以使用注解对Bean进行申明和注入,大大的减少了xml配置文件,同时也大大简化了项目的开发。
1.3 注解还是XML
在spring早期版本中,由于当时的JDK并不支持注解,因此只能使用XML的配置,很快,随着JDK升级到JDK5之后,它加入了注解的新特性,这样注解就被广泛地使用起来, 于是spring内部也分为两派,一派是使用XML的,一派是使用注解的,为了简化开发,在spring2.X之后的版本也引入了注解,不过是少量的注解,如@Component @Service等,但是功能还是不强大,因此对于srping的开发,大部分情况下还是使用XML为主,随着注解的增加,尤其是Servlet3.0
@WebFilter
@WebListener
)
之后,WEB容器可以脱离web.xml的部署,使用得WEB容器完全可以基于注解开发,对于spring3和spring4的版本注解功能越来越强大。对于XML的依赖起来越少,到了4.0完全可以脱离XML, 所以在spring中使用注解开发占据了主流地位,近年来。微服务的流行,越来越多的企业要求快速开发,所以spring Boot更加兴旺了。
1.4 Spring3.x到Spring4.x
从Spring3.x开始提供了Java配置方式,使用Java配置方式可以更好的理解你配置的Bean,现在我们就处于这个时代,并且Spring4.x和Springboot都推荐使用java配置的方式。
2.Spring Boot和微服务的介绍
2.1 SpringBoot的简介
Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot 致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。
https://spring.io/projects/spring-boot
https://docs.spring.io/spring-boot/docs/current/reference/html/
2.2 Spring Boot的优点
约定大于配置
嵌入的tomcat jetty 或者undertow 不用部署WAR文件。
尽可能的使用自动配置spring
提供生产就绪功能,如指标,健康检查和外部配置properties yaml yml
2.2.1 特性理解
为基于 Spring 的开发提供更快的入门体验
开箱即用,没有代码生成,也无需 XML 配置。同时也可以修改默认值来满足特定的需求。
提供了一些大型项目中常见的非功能特性,如嵌入式服务器、安全、指标,健康检测、外部配置等。
Spring Boot 并不是对 Spring 功能上的增强,而是提供了一种快速使用 Spring 的方式。
2.2.2 传统开发模式
所有的功能打包在一个 WAR包里,基本没有外部依赖(除了容器),部署在一个JEE容器(Tomcat,JBoss,WebLogic)里,包含了 DO/DAO,Service,UI等所有逻辑。
l 基本不会重复开发
2.2.2.2 缺点:
l 效率低:开发都在同一个项目改代码,相互等待,冲突不断
l 维护难:代码功功能耦合在一起,新人不知道何从下手
l 稳定性差:一个微小的问题,都可能导致整个应用挂掉
l 对服务器的性能要求要统一,要高
微服务是指开发一个单个小型的但有业务功能的服务,每个服务都有自己的处理和轻量通信机制,可以部署在单个或多个服务器上,微服务也指一种松耦合的,有一定有界上下文的面向服务的架构
2.2.3.1 优点
l 每个微服务都很小,这样能聚焦一个指定的业务功能或业务需求
l 微服务是松耦合的,是有功能,有意义的服务,开发阶段或部署阶段都是独立的
l 微服务可以使用不同的语言开发
l 微服务能部署在中低端配置的服务器上
2.2.3.2 缺点
l 微服务会带来过多的操作
l 可能有双倍的努力
l 分布跟踪部署难
3.Spring Boot 入门程序
②Maven3.6+
③IDEA2020.1
④SpringBoot2.x
⑤Spring5.x
3.2 新建项目
3.3 在项目里面创建模块
这里我们选择创建Module来完成第一个Spring Boot项目
3.4 项目目录说明
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--继承了springBoot的父项目-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!-- 我们自己的GAV坐标-->
<groupId>com.bjpowernode</groupId>
<artifactId>01-spring-boot-hello</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!--项目名称-->
<name>spring-boot-hello</name>
<!--项目描述-->
<description>Demo project for Spring Boot</description>
<!--版本控制-->
<properties>
<java.version>1.8</java.version>
</properties>
<!--依赖-->
<dependencies>
<!--web的依赖整合了Mvc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--热部署的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- 配置文件拓展依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!--lombok工具依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--springBoot测试依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!--构建-->
<build>
<!--打包插件-->
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
3.6 创建IndexController(在启动类同级或者子包下面)
package com.bjpowernode.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class IndexController {
@GetMapping("hello")
public String hello() {
return "hello Spring Boot";
}
}
3.7 启动
3.8 测试访问
http://localhost:8080/hello
3.10 彩蛋
启动时查看控制台的输出会发现有一个banner图案,这个banner图案是可以自定义的
3.10.1 修改为自己的banner
在src/main/resources目录下面创建一个banner.txt文件,并且输入想要的内容即可
这里提供一个字体生产网站 https://www.bootschool.net/ascii 可以随意创建
4.常用注解【spring的java配置】
4.1 回顾spring和java的注解
4.1.1 类上的注解
@Controller
控制器
@RestController=@Controller+@ResponseBody
@Service
@Respority
@Component
作用在类上的常用注解
@GetMapping
GET请求
POST请求
DELETE请求
@PutMapping
PUT请求
@PatchMapping
PATCH请求
入参是JSON对象
@PathVariable
4.1.4 属性上的注解
@Autowried
@Resource
自动注入(首选按照名字)byName byType
4.2 相关注解说明
4.2.1 @Configuration
作用于类上,相当于一个xml配置文件;
|–application-dao.xml
4.2.2 @Bean
作用于方法上,相当于xml配置中的
package com.bjpowernode.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String name;
private String address;
}
4.2.2.2 创建配置类
package com.bjpowernode.config;
import com.bjpowernode.domain.User;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConfig1 {
@Bean(value = "user1")
public User user1() {
return new User(1, "小明", "武汉");
}
@Bean(value = "user2")
public User user2() {
return new User(1, "小明", "武汉");
}
@Bean(value = "user3")
public User user3(@Qualifier("user1") User user) {
return new User(1, "小明", "武汉");
}
}
4.2.3 @Qualifier注解
qualifier的意思是合格者,通过这个标示,表明了哪个实现类才是我们所需要的,我们修改调用代码,添加@Qualifier注解,需要注意的是@Qualifier的参数名称必须为我们之前定义@Bean注解的名称之一
4.2.4 @Primary 主候选的
当IOC容器里面有多个同类型的对象时,就会发生冲突,标注了该注解的就作为主候选对象
4.2.5 @Import注解
在创建配置文件之后可以引入其它的配置文件
|–
4.2.6 @ComponentScan(“com.bjpowernode”)配置扫描
|–<context:component-scan base–package=“com.bjpowernode.*.mapper”/>
5.Spring Boot热部署
5.1 什么是热部署
spring为开发者提供了一个名为spring-boot-devtools的模块来使springboot应用支持热部署,提高开发的效率,修改代码后无需重启应用
5.2 添加依赖
org.springframework.boot spring-boot-devtools
runtime
true
5.3 配置idea的启动面板
如果不配置面板,那么可直接使用ctrl+F9去刷新,配置 以后,当我们修改代码,光标失去idea的焦点以后,就出触发自动部署
6.手动创建springboot项目
6.1 创建maven项目
6.2 修改pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--继承springboot的项目-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.2</version>
<relativePath/>
</parent>
<groupId>com.powernode</groupId>
<artifactId>02-springboot-hello</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--spring boot web项目的启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--热部署工具-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!--写配置配置提示的包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!--简化类的构造器和get set-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--测试用-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
6.3 创建启动类
6.4 创建测试类
6.5 创建static,templates
6.6 创建application.properties
7. Spring Boot的自动配置原理以及启动分析(难点)
1,默认的包扫描
启动类所在的包及其子包
2,依赖的分析
<!--继承了springBoot的父项目-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
点进去spring-boot-starter-parent以后这依赖管理里面写和很从版本
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.5.3</version>
</parent>
这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心;以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了
7.2 启动器 spring-boot-starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.5.3</version>
<scope>compile</scope>
</dependency>
springboot-boot-starter-xxx:就是spring-boot的场景启动器
<!--web的依赖整合了Mvc-->
<!-- 引入了spring-boot-starter-web以后,springboot就帮我们导入了web模块运行的所有组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来 , 我们要用什么功能就导入什么样的场景启动器即可 ,也可以自己自定义 starter;
7.3.2 如何自动扫描(默认扫描启动类所在的包以下面的及子包)
@Import(AutoConfigurationPackages.Registrar.class)
7.3.3 如何加载自动配置类
找到@Import(AutoConfigurationImportSelector.class)
7.3.3.1 在这个类里面的getAutoConfigurationEntry()方法
7.3.3.2 进入getCandidateConfigurations()方法查看究竟
这个方法spring的加载工厂去筛选所有引入(link)EnableAutoConfiguration的配置类
EnableAutoConfiguration是Key 返回的List是value
7.3.3.3 接着进入loadFactoryNames()方法
7.3.3.4 查看MATE-INF/spring.factories文件
最终我们从候选方法中获取到了需要自动配置的全限定类名的集合,我们再回到一开始的加载候选的方法
7.3.3.5 最后回到getAutoConfigurationEntry()方法里面往下执行
7.3.3.6 剔除掉不需要的自动配置类(pom.xml中没有加入依赖的)
至此自动加载配置类的初探就结束了
7.4 选讲如何加载前端控制器(DispatcherServlet)
7.4.1 在ssm里面,我们需要手动去创建DispatcherServlet对象,然后注入到Servlet容器中
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:spring-context.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
7.4.2 再SpringBoot中只要我们加了Web的starter,就默认做好了
7.4.2.1 查看DispatcherServletAutoConfiguration这个自动配置类
现在深刻理解了SpringBoot的 约定大于配置 这一句话了吧
8.Spring Boot的run方法到底执行了什么
9.Spring Boot给我们提供了哪些自动配置类呢?
https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.build–systems.starters
如果你需要使用,可以直接添加对应的starter的依赖,那么只需要自定一些参数即可,因为已经有默认配置了,但是如果你需要的组件没有starter,那么久需要自己写配置类了
10.Spring Boot的配置文件语法【重中之重】
10.1 首先引入依赖
<!-- 配置文件拓展依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
10.2 创建Hero 类
@Data // lombok的注解,生成get和set方法
@Component // 标记为spring的一个组件
@ConfigurationProperties(prefix = “hero”) // 使用hero前缀去ioc容器里面读取配置文件
public class Hero {
private Integer id;
private Integer age;
private String name;
private Date birth;
private String[] hobby;
private List<String> list;
private Set<String> set;
private Map<String, String> map;
}
10.3 properties方式
hero.id=2
hero.name=CXS
hero.age=18
hero.birth=2012/12/31
hero.hobby=LOL,DNF,CF
hero.list=C#,Python,PS
hero.set=football,basketball,swim
hero.map.k1=v1
hero.map.k2=v2
10.4 Yml方式
hero:
id: 2
age: 18
name: 超人
birth: 2012/12/31
hobby:
– LOL
– DNF
– CF
list:
– JAVA
– JS
– C++
set:
– 足球
– 篮球
– 排球
map:
k1: v1
k2: v2
10.5 测试
这里我们得知当properties配置和yml配置都存在是 proeprties的优先级更高
10.6 配置文件占位符
10.7 配置文件中读取IOC容器里的值
10.8 两种配置文件的用法说明
1,如果properties里面配置了就不会去yml里面去取值,如果没有配置就会去yml里面取
2,两种配置文件是互补的存在
private List user;
如何通过配置文件注入
11.@Value读取配置文件
11.1 创建Hero2类
@Data // lombok的注解,生成get和set方法
@Component // 标记为spring的一个组件
public class Hero2 {
@Value("${hero.id}")
private Integer id;
@Value("${hero.age}")
private Integer age;
@Value("${hero.name}")
private String name;
@Value("${hero.birth}")
private Date birth;
// @Value(“
h
e
r
o
.
h
o
b
b
y
”
)
p
r
i
v
a
t
e
S
t
r
i
n
g
[
]
h
o
b
b
y
;
/
/
@
V
a
l
u
e
(
”
{hero.hobby}”) private String[] hobby; // @Value(”
hero.hobby“)privateString[]hobby;//@Value(“{hero.list}”)
private List list;
// @Value(“
h
e
r
o
.
s
e
t
”
)
p
r
i
v
a
t
e
S
e
t
<
S
t
r
i
n
g
>
s
e
t
;
/
/
@
V
a
l
u
e
(
”
{hero.set}”) private Set<String> set; // @Value(”
hero.set“)privateSet<String>set;//@Value(“{hero.map}”)
private Map<String, String> map;
}
11.2 Yml文件
hero:
id: 2
age: 18
name: 小白
birth: 2012/12/31
hobby:
– LOL
– DNF
– CF
list:
– JAVA
– JS
– C++
set:
– 足球
– 篮球
– 排球
map:
k1: v1
k2: v2
11.3 总结说明
1,@Value只能注入普通的属性[也就是基本数据类型和String,Date] 其它的复杂类型是不能取到值的[如果yaml配置是array:LOL,DNF]这样的配置是可以取
2,如果属性是使用驼峰命名法则不能使用属性名注入,要使用@Value(“${hero.class-name}”)来取值
不能使用@Value(“${hero.className}”)来取值
11.4 @Value和@ConfigurationProperties取值比较
12.注解验证
12.1 引入依赖
Springboot2.3.x以后,需要单独引入依赖,之前在web-starter里面包含
<!--引入注解验证的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
12.2 修改Hero类
12.3 修改application.yml文件
12.4 测试
12.5 常用的验证注解
@NotNull –不能为null,但可以空着不写 (name:)
@NotEmpty –不能为空,也不能为null,但可以是空格 “ ”
@NotBlank –不能为空,也不能为null,也不能为空格
@Min 最大值
@Max 最小值
@Pattern(regexp“[0,1]{1}”) 正则限制
13.@PropertySource和@ImportResource的使用
13.1 为什么要用@PropertiesSource
上面的注入,所有的配置都是写在appliaction.properties或application.yml文件里,那么如果不想写在这里面怎么处理呢,使用@PropertySource可以解决
13.2 注入优先级的问题
所在的配置都是优先注入appliaction.properties或application.yml里面的数据
如果要不一样,必须修改配置文件引入的前缀
13.3 创建Hero3类
@Data
@Component // 标记为spring的一个组件
@PropertySource(value = {“classpath:hero.properties”}) // properties配置文件的指定
@ConfigurationProperties(prefix = “hero”) // 使用hero前缀去ioc容器里面读取配置文件
public class Hero3 {
private Integer id;
private String name;
private Integer age;
private Date birth;
private String[] hobby;
private List<String> list;
private Set<String> set;
private Map<String, String> map;
private String className;
}
13.4 创建Hero.properties配置文件
hero.name=“测试自己的配置文件”
hero.className=“班级名称”
13.5 测试
13.6 为什么要用@ImportResource
从上面所有的配置中可以看出我们没有使用以前的spring的xml的配置方法,如果还是要使用spring里面的xml的配置方式怎么办理,使用@ImportResource
13.7 创建Hero4类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Hero4 {
private Integer id;
private String name;
}
13.8 创建beans.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="hero" class="com.bjpowernode.domain.Hero4">
<property name="id" value="1"/>
<property name="name" value="后羿"/>
</bean>
</beans>
13.9 启动类添加@ImportResource注解
13.10 测试
14.Profile配置文件详解
14.1 为什么要使用profiles
在开发中,一般有两种环境
1,生产环境 [项目上线,客户在使用中,就是生产环境]
2,开发环境 [就是开发环境,自己开发测试的环境]
有时候开发环境和生产环境的配置方法是不一样的,那么如何快速的切换呢,这里就要使用profiles文件
14.2 创建application–dev.yml
14.3 创建application-pro.yml
14.4 修改application.yml
在application.yml的主配置文件中,激活哪个配置文件,就会使用该配置文件进行运行
14.5 启动测试
14.6 打jar包部署运行测试
打jar包运行不会去看3.9章节
java -jar 01-spring-boot-hello-0.0.1-SNAPSHOT.jar –spring.profiles.active=pro
15.配置文件加载优先级和外部配置文件
15.1 项目内部配置文件
spring boot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件
其中同一目标下的properties文件的优先级大于yml文件
15.1.1 配置文件可以放的位置和优先级
classpath:/ —优先级4
file:./config/ –优先级1
15.1.2 查看ConfigDataEnvironment
15.1.3 测试注意
在测试的时候,需要修改pom的编译路径,确保把所有的配置文件都编译以后再测试
<build>
<!-- 将所有的配置文件都编译-->
<resources>
<resource>
<directory>D:workspaceSpringBoot-Code02-spring-boot-config</directory>
<includes>
<include>**/*.yml</include>
<include>application.yml</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
15.2 外部的配置文件
在D盘放一个application.yml文件 端口指定为8009
java -jar 02-spring-boot-config-0.0.1-SNAPSHOT.jar –spring.config.location=D:/application.yml
15.2.1 使用命令行参数
也可以使用命令行参数指定(文档一行写不下,你们写的时候不要回车啊)
java -jar 02-spring-boot-config-0.0.1-SNAPSHOT.jar —server.port=8888 —server.servlet.context-path=/bjpowernode
16.自动配置原理以及 @Conditional派生注解
16.1 配置文件到底怎么写什么?怎么写?
https://docs.spring.io/spring-boot/docs/2.5.3/reference/htmlsingle/#application-properties
16.2 自动配置
自7.3.3以后,我们就了解到了Spring Boot在启动的时候:
l 去扫描加载了所有引入依赖的jar包下面的MATE-INF/spring.factories文件,
l 然后通过EnableAutoConfiguration为key,下面所有自动配置类的全限定类名作为value,(List)
l 经过筛选排除掉不符合要求的(pom中没有添加依赖的配置类)
l 最后把(List:符合条件的自配配置类的全限定类名)添加到IOC容器去管理,从而实现了自动配置原理
16.3 以HttpEncodingAutoConfiguration为例来理解自动装配
根据当前不同的条件判断,决定这个配置类是否生效?
一但这个配置类生效;这个配置类就会给容器中添加各种组件;这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的;
//表示这是一个配置类
@Configuration(proxyBeanMethods = false)
//开启自动配置属性,指定ServerProperties配置文件
@EnableConfigurationProperties(ServerProperties.class)
//spring的底层注解,根据条件判断当前类是否生效
//判断当前项目环境是否是一个web环境
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
//判断当前环境是否有CharacterEncodingFilter类
@ConditionalOnClass(CharacterEncodingFilter.class)
//判断我们自己配置的yml文件里面是否有server.servlet.encoding.enabled属性,如果没有就自动生效
@ConditionalOnProperty(prefix = “server.servlet.encoding”, value = “enabled”, matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
//这个属性和下面的有参构造器绑定在了一起,配置文件映射成功
private final Encoding properties;
public HttpEncodingAutoConfiguration(ServerProperties properties) {
this.properties = properties.getServlet().getEncoding();
}
@Bean //创建一个CharacterEncodingFilter对象
@ConditionalOnMissingBean //前提条件是容器里面没有这个对象
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
return filter;
}
}
16.4 总结
-
SpringBoot启动会加载大量的自动配置类
-
看我们需要的功能有没有SpringBoot默认写好的自动配置类;
-
我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件有,我们就不需要再来配置了)
以在配置文件中指定这些属性的值;
- xxxxAutoConfigurartion:自动配置类;给容器中添加组件
- xxxxProperties:封装配置文件中的默认配置
16.5 @Conditional派生注解
作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置类里面的所有内容才生效;(条件之间是并且的关系)
16.6 如何查看自动配置类生效
我们可以通过启用debug=true属性(在配置文件配置);来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效;
17.整合logback
17.1 概述
在搭建新的系统时候必不可少的是需要日志的,日志的作用就不用多说了吧,可以用来调试程序,记录程序运行的状态,最重要的是可以用来排查线上的问题。
那我们该如何在项目中使用日志呢?
SpringBoot内部集成了LogBack日志依赖,SpringBoot默认使用LogBack记录日志信息,默认根据base.xml配置内容来输出到控制台和文件之中,不过这些默认的配置不能达到企业级项目的要求
17.2 创建模块
17.3 创建logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="10 seconds">
<!--<include resource="org/springframework/boot/logging/logback/base.xml" />-->
<contextName>logback</contextName>
<!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
<property name="log.path" value="D:/mylogs/" />
<!-- 彩色日志 -->
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!--输出到控制台-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>info</level>
</filter>
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!--输出到文件-->
<!-- 时间滚动输出 level为 DEBUG 日志 -->
<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_debug.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志归档 -->
<fileNamePattern>${log.path}/debug/log-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文件只记录debug级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>debug</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 时间滚动输出 level为 INFO 日志 -->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_info.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天日志归档路径以及格式 -->
<fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文件只记录info级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>info</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 时间滚动输出 level为 WARN 日志 -->
<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_warn.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文件只记录warn级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>warn</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 时间滚动输出 level为 ERROR 日志 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_error.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文件只记录ERROR级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!--
<logger>用来设置某一个包或者具体的某一个类的日志打印级别、
以及指定<appender>。<logger>仅有一个name属性,
一个可选的level和一个可选的addtivity属性。
name:用来指定受此logger约束的某一个包或者具体的某一个类。
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。
如果未设置此属性,那么当前logger将会继承上级的级别。
addtivity:是否向上级logger传递打印信息。默认是true。
-->
<!--<logger name="org.springframework.web" level="info"/>-->
<!--<logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/>-->
<!--
使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作:
第一种把<root level="info">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息
第二种就是单独给dao下目录配置debug模式,代码如下,这样配置sql语句会打印,其他还是正常info级别:
-->
<!--
root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
不能设置为INHERITED或者同义词NULL。默认是DEBUG
可以包含零个或多个元素,标识这个appender将会添加到这个logger。
-->
<!--开发环境:打印控制台-->
<springProfile name="dev">
<logger name="com.nmys.view" level="debug"/>
</springProfile>
<root level="info">
<appender-ref ref="CONSOLE" />
<appender-ref ref="DEBUG_FILE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="WARN_FILE" />
<appender-ref ref="ERROR_FILE" />
</root>
<!--生产环境:输出到文件-->
<!--<springProfile name="pro">-->
<!--<root level="info">-->
<!--<appender-ref ref="CONSOLE" />-->
<!--<appender-ref ref="DEBUG_FILE" />-->
<!--<appender-ref ref="INFO_FILE" />-->
<!--<appender-ref ref="ERROR_FILE" />-->
<!--<appender-ref ref="WARN_FILE" />-->
<!--</root>-->
<!--</springProfile>-->
</configuration>
17.4 修改启动类测试
17.5 查看本地日志
18.AOP开发
18.1 概述
aop是spring的两大功能模块之一,功能非常强大,为解耦提供了非常优秀的解决方案。SpringBoot集成aop是非常方便的,下面使用aop来拦截业务组件的方法
Aop的作用:在不修改源代码的情况下,对类里面的方法进行增强
(前置,后置,环绕,异常)
18.2 使用方法
18.3 创建项目并添加maven依赖
org.springframework.boot
spring-boot-starter-aop
18.4 创建Man测试类
/**
-
将此类加入IOC管理
-
AOP作用的类一定要加入IOC容器中
*/
@Component
public class Man {/**
18.5 创建切面
/**
* 切面类加入IOC容器管理
* 添加切面的标识
*/
@Component
@Aspect
public class ManAspect {
/**
* 切入点
*/
public static final String POINT_CUT = "execution(* com.bjpowernode.test.Man.*(..))";
/**
* 前置通知
*/
@Before(value = POINT_CUT)
public void before() {
System.out.println("吃饭前洗手");
}
/**
* 后置通知
*/
@After(value = POINT_CUT)
public void after() {
System.out.println("饭后甜点");
}
/**
* 环绕通知
*
* @param joinPoint
* @return
*/
@Around(value = POINT_CUT)
public Object around(ProceedingJoinPoint joinPoint) {
Object result = null;
System.out.println("执行目标方法之前");
try {
// 执行目标方法
result = joinPoint.proceed();
System.out.println("执行目标方法之后");
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return result;
}
/**
* 异常通知
*
* @param ex
*/
@AfterThrowing(value = POINT_CUT, throwing = "ex")
public void afterThrowing(Throwable ex) {
System.out.println("异常了" + ex.getMessage());
}
}
18.6 测试
18.7 代理方式的切换
从springBoot2.x以后,切换代理方式需要在配置文件中配置,使用注解切换的方式失效了
修改application.yml文件的切换方式代理方式
spring:
aop:
proxy–target-class: true # false表示使用JDK代理 true表示使用CGLIB代理,SpringBoot2.x以后默认使用CGLIB代理
19.WEB静态资源访问规则
19.1 springboot访问静态资源的几种方式
查看WebMvcAutoConfiguration里面的静态类WebMvcAutoConfigurationAdapter
关于资源管的方法addResourceHandlers
META-INF/resources>resources>static>public
19.2 自定义静态资源访问方式
19.2.1 自定义方式1配置文件方式yml
spring:
web:
resources:
static–locations: classpath:/mystatic/ # 静态资源存放的目录
mvc:
static-path-pattern: /static/** # 访问mvc的路径映射
19.2.2 Java类方式
/**
-
自己写配置类去实现WebMvc的配置,并且重写
*/
@Configuration
public class MyWebMvcResourceHandler implements WebMvcConfigurer {/**
19.3 webjars的访问规则
19.3.1 什么是webjars
WebJars是打包到JAR(Java Archive)文件中的客户端Web库(例如jQuery和Bootstrap)。
使用基于JVM的构建工具(例如Maven,Gradle,sbt,…)来下载客户端依赖项
了解您正在使用的客户端依赖项
19.3.2 引入依赖
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.6.0</version>
</dependency>
19.3.3 启动访问
http://localhost:8080/webjars/jquery/3.6.0/jquery.js
20.Thymeleaf模板的使用
20.1 Thymeleaf概述
简单说, Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,它可以完全替代 JSP 。相较与其他的模板引擎,它有如下三个极吸引人的特点:
1、Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。
2、Thymeleaf 开箱即用的特性。它提供标准和spring标准两种方言,可以直接套用模板实现JSTL、 OGNL表达式效果,避免每天套模板、该jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。
3、Thymeleaf 提供spring标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能
20.2 创建项目添加thymeleaf的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
20.3 Spring Boot项目Thymeleaf模板页面存放位置
查看Thymeleaf的自动配置类
当然我们也可以自己修改他的位置,只需要在yml文件中修改即可,一般不做修改
20.4 通过Controller跳转到Thymeleaf的页面
20.4.1 在指定位置下创建hello.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hello</title>
</head>
<body>
<h1>hello Thymeleaf</h1>
<h1>我是classpath:/templates/hello.html</h1>
</body>
</html>
20.4.2 创建RouteController类
@Controller
public class RouteController {
@GetMapping("hello")
public String hello(){
return "hello";
}
}
20.4.3 测试访问
http://localhost:8080/hello
20.5 Thymeleaf的相关语法
官网:https://www.thymeleaf.org/
l ${…} 取作用域里面的值 request session applicationContext
l #{…} 取IOC容器中的值
l @{…} URL表达式 th:href=”@{/test(name=’abc’,pwd=’123’)}”
l th:text 标签中取字面值
l #locale 本地环境
l #httpServletRequest HttpServletRquest对象
l #httpSession HttpSession对象
l #servletContext servletContext对象
常用工具对象
l #strings 字符串对象的实用方法:包含startsWith,将/附加等
l #dates java.util的实用方法。对象:日期格式、组件提取等
l #objects:实用方法的对象。
l #bools:布尔评价的实用方法。
l #aggregates:实用程序方法用于创建聚集在数组或集合.
l #ids:实用程序方法来处理可能重复的id属性(例如,由于迭代)。
20.6 Thymeleaf读取Model里面的对象
20.6.1 创建Hero类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Hero {
private Integer id;
private String name;
private String sex;
private Integer age;
private String country;
private String phone;
private Date birth;
private Double salary;
}
20.6.2 在RouteController增加一个方法跳转
@GetMapping(“helloHero”)
public String helloHero(Model model) {
Hero hero = new Hero(1, “后羿”, “男”, 18, “中国”, “110”, new Date(), 3150D);
model.addAttribute(“hero”, hero);
return “showHero”;
}
20.6.3 创建showHero页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>英雄面板</h1>
<span>英雄id</span> <span th:text="${hero.id}"></span> <br>
<span>英雄名字</span> <span th:text="${hero.name}"></span> <br>
<span>英雄性别</span> <span th:text="${hero.sex}"></span> <br>
<span>英雄年龄</span> <span th:text="${hero.age}"></span> <br>
<span>英雄年龄</span> <span th:text="${#numbers.formatDecimal(hero.age,0,2)}"></span> <br>
<span>英雄国家</span> <span th:text="${hero.country}"></span> <br>
<span>英雄电话</span> <span th:text="${hero.phone}"></span> <br>
<span>英雄生日</span> <span th:text="${hero.birth}"></span> <br>
<span>英雄生日</span> <span th:text="${#dates.format(hero.birth,'yyyy-MM-dd')}"></span> <br>
<span>英雄存款</span> <span th:text="${hero.salary}"></span> <br>
</body>
</html>
20.6.4 测试访问
http://localhost:8080/helloHero
20.7 Thymeleaf读取Model里面的集合
20.7.1 在RouteController增加一个方法跳转
@GetMapping("helloHeroList")
public String helloHeroList(Model model) {
ArrayList<Hero> heroes = new ArrayList<>();
for (int i = 1; i <= 3; i++) {
heroes.add(new Hero(i, "后裔" + i, "男", 18 + i, "华夏", "110" + i, new Date(), 3150D + i));
}
model.addAttribute("heros", heroes);
return "showHeroList";
}
20.7.2 创建showHeroList页面
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div th:each="hero: ${heros}">
<div>
<span>英雄id</span> <span th:text="${hero.id}"></span> <br>
<span>英雄名字</span> <span th:text="${hero.name}"></span> <br>
<span>英雄性别</span> <span th:text="${hero.sex}"></span> <br>
<span>英雄年龄</span> <span th:text="${hero.age}"></span> <br>
<span>英雄年龄</span> <span th:text="${#numbers.formatDecimal(hero.age,0,2)}"></span> <br>
<span>英雄国家</span> <span th:text="${hero.country}"></span> <br>
<span>英雄电话</span> <span th:text="${hero.phone}"></span> <br>
<span>英雄生日</span> <span th:text="${hero.birth}"></span> <br>
<span>英雄生日</span> <span th:text="${#dates.format(hero.birth,'yyyy-MM-dd')}"></span> <br>
<span>英雄存款</span> <span th:text="${hero.salary}"></span> <br>
</div>
<hr>
</div>
</body>
</html>
20.7.3 测试访问
http://localhost:8080/helloHeroList
20.8 ThymeleafObjects的使用
20.8.1 在RouteController增加一个方法跳转
@GetMapping("thymeleafObject")
public String thymeleafObject(Model model, HttpServletRequest request){
model.addAttribute("name","超人");
request.setAttribute("age",22);
HttpSession session = request.getSession();
session.setAttribute("address","武汉");
request.getServletContext().setAttribute("hobby","编码");
return "showObj";
}
20.8.2 创建showObj页面
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div>
model: <span th:text="${name}"></span>
<hr>
request: <span th:text="${#httpServletRequest.getAttribute('age')}"></span>
<hr>
session: <span th:text="${#httpSession.getAttribute('address')}"></span>
<hr>
servletContext: <span th:text="${#servletContext.getAttribute('hobby')}"> </span>
<hr>
<div>
<span th:text="${#locale.getCountry()}"></span>
<span th:text="${#locale.getLanguage()}"></span>
</div>
</div>
</body>
</html>
20.8.3 测试访问
http://localhost:8080/thymeleafObject
<script>
// 字符串需要引号
let name = "[[${name}]]"
// 数字类型不需要引号
let age = [[${age}]]
console.log(name)
console.log(age)
</script>
20.10 Thymeleaf链接接传值
20.10.1 创建一个按钮
点击传值
20.10.2 在RouteController增加一个方法
@GetMapping(“testGetParam”)
@ResponseBody
public String testGetParam(String name,String pwd){
System.out.println(name);
System.out.println(pwd);
return “ok”;
}
21.SpringBoot自动管理MVC分析
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-web-applications.spring-mvc.auto-configuration
21.1 查看WebMvcAutoConfiguration及其静态内部类
Mvc帮我们做了哪些事情?
1.(请求分发 –dispatcherServlet)
2.适配器 –requestMappingHandlerAdapter
4.视图解析 –(ContentNegotiatingViewResolver)
6…
21.2 具体查看视图解析
在ContentNegotiatingViewResolver内容视图协商解析器中初始化servletContext的时候,把加载的所有视图解析器收集了起来
这里总结出:只要IOC容器里面实现了viewResolver接口的,都可以被收集起来
21.3 具体查看文件上传下载
MultipartAutoConfiguration中帮我们自动配置了
MultipartProperties配置文件中可以设定参数,可以的yml文件里面配置
21.4 具体查看格式化【接收页面参数并转化】
在WebMvcAutoConfiguration中的addFormatters
我们可以看到默认配置了很多转换规则
我们也可以配置文件中自己配置
spring:
mvc:
format:
date-time: yyyy-MM-dd HH:mm:ss
版本说明
format.date-time —java.time.LocalDateTime
21.5 欢迎页面自动配置
22.扩展MVC的组件【了解】
创建一个配置类实现WebMvcConfigurer重写之前的方法即可实现自定义拓展
22.1 自定义视图解析器【熟悉】
这样我们就不需要写controller
@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 转发的控制器注册
registry.addViewController("/test")
.setViewName("test");
// 重定向的控制器注册
registry.addRedirectViewController("/tobaidu", "https://www.baidu.com");
}
}
22.2 自定义拦截器【掌握】
22.2.1 创建自己的拦截器
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
// 返回true 就是放行 返回false就是拦截
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}
22.2.2 注册到webMvc管理
/**
* 注册自己的拦截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/**") // 拦截的路径
.excludePathPatterns("/hello"); // 放行的路径
}
23.注册Web三大组件【重点】
Servlet
|–@WebServlet
|–web.xml
Filter
|–@WebFilter
|–web.xml
Listener
|–@WebListener
|–web.xml
23.1 注册自己的Servlet
我们可以模仿DispatcherServlet的注册方式
23.1.1 创建UserServlet
@Component
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("这是我自己的servlet");
PrintWriter writer = resp.getWriter();
writer.write("hello");
writer.flush();
writer.close();
}
}
23.1.2 在配置类注册自己的servlet
@Configuration
public class MyWebConfig {
@Autowired
private UserServlet userServlet;
/**
* 注册自己的servlet
*
* @return
*/
@Bean
public ServletRegistrationBean<UserServlet> userServletServletRegistrationBean() {
ServletRegistrationBean<UserServlet> registrationBean = new ServletRegistrationBean<>();
registrationBean.setServlet(userServlet);
registrationBean.addUrlMappings("/user");
return registrationBean;
}
}
23.2 注册自己的Filter
23.2.1 创建MyFilter
@Component
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("我自己的过滤器");
chain.doFilter(request,response);
}
}
23.2.2 在配置类里注册自己的过滤器
@Autowired
private MyFilter myFilter;
@Bean
public FilterRegistrationBean<MyFilter> myFilterFilterRegistrationBean(){
FilterRegistrationBean<MyFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(myFilter);
// 所有的请求都走过滤器 /不会过滤页面,只会过滤路径 /*会过滤路径和页面
registrationBean.setUrlPatterns(Arrays.asList("/*"));
return registrationBean;
}
23.3 注册自己的Listener
23.3.1 创建MyListener
@Component
public class MyListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("MyListener init");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("MyListener Destroy");
}
}
23.3.2 在配置类里面注册自己的监听器
@Autowired
private MyListener myListener;
@Bean
public ServletListenerRegistrationBean<MyListener> myListenerServletListenerRegistrationBean(){
ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean<>();
registrationBean.setListener(myListener);
return registrationBean;
}
24.数据源配置和自动管理【重中之中】
24.1 创建项目选择依赖
24.2 创建数据库
24.3 使用DriverManagerDataSource
24.3.1 修改配置文件
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
type: org.springframework.jdbc.datasource.DriverManagerDataSource # spring自带的数据源
url: jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8
username: root
password: root
24.3.2 测试
24.4 使用Druid数据源【自己配置】
24.4.1 添加durid的依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.6</version>
</dependency>
24.4.2 添加MyDruidProperties配置文件类
@Data
@AllArgsConstructor
@NoArgsConstructor
@ConfigurationProperties(prefix = "my.druid")
public class MyDruidProperties {
private String url;
private String driverClassName;
private String username;
private String password;
/**
* 初始化链接数
*/
private Integer initialSize;
/**
* 最大链接活跃数
*/
private Integer maxActive;
/**
* 最小链接数
*/
private Integer minIdle;
/**
* 检查的sql语句
*/
private String validationQuery;
private StatView statView;
/**
* 监控配置
*/
@Data
static class StatView {
/**
* 监控登陆用户名
*/
private String loginUsername;
/**
* 监控登陆密码
*/
private String loginPassword;
/**
* 白名单
*/
private String allow;
/**
* 黑名单
*/
private String deny;
/**
* 映射路径
*/
private String[] urlMapping;
}
}
24.4.3 添加MyDruidAutoConfiguration自动配置类
@Configuration // 配置类
@EnableConfigurationProperties(MyDruidProperties.class) // 指定配置类
@ConditionalOnClass(DataSource.class) // 必须要有这个类才生效
public class MyDruidAutoConfiguration {
@Autowired
private MyDruidProperties myDruidProperties;
/**
* 创建数据源
*
* @return
*/
@Bean(initMethod = "init", destroyMethod = "close")
public DruidDataSource druidDataSource() {
if (!StringUtils.hasText(myDruidProperties.getUrl())) {
throw new IllegalArgumentException("URL 不能为空");
}
DruidDataSource druidDataSource = new DruidDataSource();
// 设置参数
druidDataSource.setUrl(myDruidProperties.getUrl());
druidDataSource.setUsername(myDruidProperties.getUsername());
druidDataSource.setPassword(myDruidProperties.getPassword());
druidDataSource.setDriverClassName(myDruidProperties.getDriverClassName());
druidDataSource.setMaxActive(myDruidProperties.getMaxActive());
druidDataSource.setInitialSize(myDruidProperties.getInitialSize());
druidDataSource.setMinIdle(myDruidProperties.getMinIdle());
druidDataSource.setValidationQuery(myDruidProperties.getValidationQuery());
return druidDataSource;
}
/**
* 注册servlet
*
* @return
*/
@Bean
public ServletRegistrationBean<StatViewServlet> statViewServletServletRegistrationBean() {
StatViewServlet statViewServlet = new StatViewServlet();
ServletRegistrationBean<StatViewServlet> registrationBean = new ServletRegistrationBean<>();
registrationBean.setServlet(statViewServlet);
// 设置参数
registrationBean.addInitParameter("loginUsername", myDruidProperties.getStatView().getLoginUsername());
registrationBean.addInitParameter("loginPassword", myDruidProperties.getStatView().getLoginPassword());
registrationBean.addInitParameter("allow", myDruidProperties.getStatView().getAllow());
registrationBean.addInitParameter("deny", myDruidProperties.getStatView().getDeny());
registrationBean.addUrlMappings(myDruidProperties.getStatView().getUrlMapping());
return registrationBean;
}
}
24.4.4 修改yml配置文件
my:
druid:
url: jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
initial-size: 2
max-active: 10
min-idle: 3
validation-query: select 'x'
stat-view:
login-username: admin
login-password: admin
allow:
deny:
url-mapping:
- /druid/*
- /druid2/*
24.4.5 测试访问
http://localhost:8080/druid
24.5 使用Druid数据源【官方starter】
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
24.5.1 修改配置文件
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&serverTimezone=GMT%2B8
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
druid:
max-active: 10
min-idle: 2
validation-query: select 'x'
stat-view-servlet:
login-username: admin
enabled: true #启用监控页
login-password: admin
allow:
deny:
url-pattern: /druid/*
25.集成mybatis【重点】
25.1 创建数据库并且新增测试数据
25.2 创建新模块并添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
25.4 修改yml配置文件
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&serverTimezone=GMT%2B8
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
druid:
max-active: 10
min-idle: 2
validation-query: select ‘x’
stat-view-servlet:
login-username: admin
enabled: true #启用监控页
login-password: admin
allow:
deny:
url-pattern: /druid/*
mybatis:
mapper–locations: classpath:mapper/*.xml # mapper.xml文件位置
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # sql日志输出
25.5 修改启动类
25.6 测试查询
25.7 使用注解的方式
/**
* 在mapper接口的方法上使用注解的方式
* @Select
* @Update
* @Insert
* @Delete
*
* @return
*/
@Select(“select * from user”)
List selectAllUser();
25.8 配置PageHelper插件分页(第一种,不推荐)
25.8.1 依赖pageHelper
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.1</version>
</dependency>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
</configuration>
25.8.3 修改application.yml文件
mybatis:
mapper–locations: classpath:mapper/*.xml # mapper.xml文件位置
config-location: classpath:mybatis-cfg.xml # 指定配置文件
25.8.4 测试分页
25.9 配置PageHelper插件分页(第二种,推荐)
25.9.1 依赖pageHelper的starter
<!-- <dependency>-->
<!-- <groupId>com.github.pagehelper</groupId>-->
<!-- <artifactId>pagehelper</artifactId>-->
<!-- <version>5.2.1</version>-->
<!-- </dependency>-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
25.9.2 删除mybatis-cfg.xml文件后修改yml文件
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # sql日志输出
mapper-locations: classpath:mapper/*.xml # mapper.xml文件位置
# config-location: classpath:mybatis-cfg.xml # 指定配置文件
25.9.3 测试查询分页
25.10 事务管理
和spring里面一样的,但是在boot里面只要在XXXXServiceImpl上Transactionl
@Override
@Transactional(rollbackFor = RuntimeException.class, propagation = Propagation.REQUIRED)
public int addUser(User user) {
int i = userMapper.insert(user);
int a = 10 / 0;
return i;
}
25.11 boot和mybatis的集成的总结
1,创建项目导依赖
2,配置数据源[修改yml]
3,配置mybatis[修改yml]
4,配置mapper接口扫描
5,使用
26.使用外部tomcat【了解】
Springboot项目里面默认不支持jsp 它是使用模板引擎[thymeleaf]
Springboot不支持jsp的原因是因为内置的tomcat不支持jsp
26.2 选择依赖
26.3 配置pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<!--加入内嵌入tomcat对jsp的支持-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<!-- servlet 依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<!--jstl的依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
26.4 添加IDEA配置
26.5 配置外部tomcat
26.6 创建controller
@Controller
public class TestController {
@GetMapping("test")
public String test(){
return "test";
}
}
26.7 创建页面
26.8 修改配置文件
spring:
mvc:
view:
prefix: /WEB-INF/view/
suffix: .jsp
26.9 启动访问
http://localhost:8080/test
27.集成swagger【熟悉】
27.1 问题描述
随着互联网技术的发展,现在的网站架构基本都由原来的后端渲染,变成了:前端渲染、前后端分离的形态,而且前端技术和后端技术在各自的道路上越走越远。 前端和后端的唯一联系,变成了API接口;API文档变成了前后端开发人员联系的纽带,变得越来越重要,swagger就是一款让你更好的书写API文档的框架,而且swagger可以完全模拟http请求,入参出参和实际情况差别几乎为零。
没有API文档工具之前,大家都是手写API文档的(维护起来相当困难),在什么地方书写的都有,有在confluence上写的,有在对应的项目目录下readme.md上写的,每个公司都有每个公司的玩法,无所谓好坏。但是能称之为“框架”的,估计也只有swagger了
27.2 使用步骤
27.2.1 创建项目加入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--swagger starter-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
27.2.2 创建SwaggerProperties信息配置类
@Data
@AllArgsConstructor
@NoArgsConstructor
@ConfigurationProperties(prefix = "swagger3")
public class SwaggerProperties {
/**
* 扫描的包
* 给这个包下面的接口创建文档
*/
private String basePackage;
/**
* 作者姓名
*/
private String name;
/**
* 作者主页链接
*/
private String url;
/**
* 作者邮箱
*/
private String email;
/**
* 版本号
*/
private String version;
/**
* 分组名称
*/
private String groupName;
/**
* 文档标题
*/
private String title;
/**
* 文档描述
*/
private String description;
/**
* 组织地址
*/
private String termsOfServiceUrl;
/**
* 许可证
*/
private String license;
/**
* 许可链接
*/
private String licenseUrl;
}
27.2.3 创建SwaggerAutoConfiguration自动配置类
@Configuration
@EnableOpenApi // 开启swagger的功能 旧版本是EnableSwagger2
@EnableConfigurationProperties(SwaggerProperties.class)
public class SwaggerAutoConfiguration {
@Autowired
private SwaggerProperties swaggerProperties;
@Bean
public Docket docket() {
return new Docket(DocumentationType.OAS_30)
.apiInfo(getApiInfo())
.groupName(swaggerProperties.getGroupName())
.select()
.apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage()))
.build();
}
private ApiInfo getApiInfo() {
Contact contact = new Contact(swaggerProperties.getName(), swaggerProperties.getUrl(), swaggerProperties.getEmail());
return new ApiInfo(swaggerProperties.getTitle(),
swaggerProperties.getDescription(),
swaggerProperties.getVersion(),
swaggerProperties.getTermsOfServiceUrl(),
contact,
swaggerProperties.getLicense(),
swaggerProperties.getLicenseUrl(),
new ArrayList<>()
);
}
}
27.2.4 修改yml文件
swagger3:
base-package: com.bjpowernode.controller
name: cxs
url: https://gitee.com/smiledouble
email: 775610843@qq.com
version: 1.0
group-name: cxs
title: "测试"
description: "测试swagger文档"
terms-of-service-url: https://gitee.com/smiledouble
license: cxs
license-url: https://gitee.com/smiledouble
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
mvc:
format:
date-time: yyyy-MM-dd HH:mm:ss
27.2.5 创建Hero类
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("英雄对象") // 描述实体类
public class Hero {
@ApiModelProperty(value = "英雄的id")
private Integer id;
@ApiModelProperty(value = "英雄的名称")
private String name;
@ApiModelProperty(value = "英雄的地址")
private String address;
@ApiModelProperty(value = "英雄的生日")
private Date birth;
@ApiModelProperty(value = "英雄的爱好")
private List<String> hobby;
@ApiModelProperty(value = "英雄的map")
private Map<String, String> map;
}
27.2.6 创建Controller
@RestController
@Api(tags = "英雄的管理接口")
public class HeroController {
@GetMapping("getHero/{id}")
@ApiOperation("根据id获取英雄")
@ApiImplicitParam(name = "id", value = "英雄编号(必填)", required = true, dataType = "Integer", paramType = "path")
public Hero getHeroById(@PathVariable("id") Integer id) {
HashMap<String, String> map = new HashMap<>();
map.put("技能", "射箭");
return new Hero(id, "后裔", "峡谷", new Date(), Arrays.asList("打猎"), map);
}
@PostMapping("addHero")
@ApiOperation("添加英雄")
public Map<String, Object> addHero(@RequestBody Hero hero) {
System.out.println(hero);
HashMap<String, Object> map = new HashMap<>();
map.put("code", 200);
map.put("msg", "OK");
return map;
}
@DeleteMapping("delHero")
@ApiOperation("根据id删除一个英雄")
@ApiImplicitParam(name = "id", value = "英雄编号", required = true, paramType = "query", dataType = "Integer")
public Map<String, Object> delHero(@RequestParam Integer id) {
System.out.println(id);
HashMap<String, Object> map = new HashMap<>();
map.put("code", 200);
map.put("msg", "OK");
return map;
}
}
27.2.7 测试访问文档页面
http://localhost:8080/swagger-ui/index.html
27.2.8 测试接口
27.2.9 补充注解说明
https://gumutianqi1.gitbooks.io/specification-doc/content/tools-doc/spring-boot-swagger2-guide.html
28.Spring Boot异步
同步:
异步:
springboot里面的异步,就是为开发者提供了种快速使用多线程的方式
New Thread().start();
28.1 概述
28.1.1 什么是异步调用?
异步调用是相对于同步调用而言的,同步调用是指程序按预定顺序一步步执行,每一步必须等到上一步执行完后才能执行,异步调用则无需等待上一步程序执行完即可执行。
28.1.2 如何实现异步调用?
多线程,这是很多人第一眼想到的关键词,没错,多线程就是一种实现异步调用的方式。
在非spring目项目中我们要实现异步调用的就是使用多线程方式,可以自己实现Runable接口或者集成Thread类,或者使用jdk1.5以上提供了的Executors线程池。
StrngBoot中则提供了很方便的方式执行异步调用。
28.1.3 异步接口的使用场景
耗时比较长,任务比较多的接口。比方说,文件下载,大文件下载比较耗时,这个时候就可以使用异步接口。还有就是对用户接下来操作没有影响的写库之类
28.2 最佳实践
28.2.1 创建项目选择依赖
28.2.2 创建Controller接口测试
@RestController
public class AsyncController {
@GetMapping("doAsync")
public Map<String,Object> doAsync(){
Map<String,Object> map=new HashMap<>();
Long startMills=System.currentTimeMillis();
task1();
task2();
task3();
Long endMills=System.currentTimeMillis();
map.put("code",200);
map.put("msg","异步调用成功,耗时"+(endMills-startMills));
return map;
}
@Async
public void task1(){
try {
long start = System.currentTimeMillis();
Thread.sleep(1000);
long end = System.currentTimeMillis();
System.out.println("task1耗时:" + (end - start));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Async
public void task2(){
try {
long start = System.currentTimeMillis();
Thread.sleep(2000);
long end = System.currentTimeMillis();
System.out.println("task2耗时:" + (end - start));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Async
public void task3(){
try {
long start = System.currentTimeMillis();
Thread.sleep(3000);
long end = System.currentTimeMillis();
System.out.println("task3耗时:" + (end - start));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
28.2.3 修改启动类开启异步功能
28.2.4 访问测试
http://localhost:8080/doAsync
28.2.5 异步并没有执行?
难道是代码写错了?反复检查了好几遍,并没有发现什么明显错误,想起spring对@Transactional注解时也有类似问题,spring扫描时具有@Transactional注解方法的类时,是生成一个代理类,由代理类去开启关闭事务,而在同一个类中,方法调用是在类体内执行的,spring无法截获这个方法调用。
28.2.6 添加异步任务类
@Component
public class AsyncTask {
@Async
public void task1(){
try {
long start = System.currentTimeMillis();
Thread.sleep(1000);
long end = System.currentTimeMillis();
System.out.println("task1耗时:" + (end - start));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Async
public void task2(){
try {
long start = System.currentTimeMillis();
Thread.sleep(2000);
long end = System.currentTimeMillis();
System.out.println("task2耗时:" + (end - start));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Async
public void task3(){
try {
long start = System.currentTimeMillis();
Thread.sleep(3000);
long end = System.currentTimeMillis();
System.out.println("task3耗时:" + (end - start));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
28.2.7 修改Controller接口测试
@RestController
public class AsyncController {
@Autowired
private AsyncTask asyncTask;
@GetMapping("doAsync")
public Map<String,Object> doAsync(){
Map<String,Object> map=new HashMap<>();
Long startMills=System.currentTimeMillis();
asyncTask.task1();
asyncTask.task2();
asyncTask.task3();
Long endMills=System.currentTimeMillis();
map.put("code",200);
map.put("msg","异步调用成功,耗时"+(endMills-startMills));
return map;
}
}
28.2.8 再次测试
29.Spring Boot定时任务
写一个任务
29.1 概述
-
在springBoot的启动类上添加
@EnableScheduling
注解开启定时调度
29.2 最佳实践
29.2.1 开启定时任务
29.2.2 执行任务
@Component
public class MyTask {
/**
* 这个方法必须是无参无返回值的
* @Scheduled里面有两种用法
* 一种是固定速率
* fixedDelay = 2000 规定延迟两秒执行一次
* fixedRate = 2000 固定过多少秒执行一次
* initialDelay = 2000,fixedRate = 5000 第一次延迟两秒执行,后面按照fixedRate的规则执行
* 一种是cron表达式 https://www.matools.com/cron/
*/
@Scheduled(cron = "0/3 * * * * ?") // 每三秒执行一次
public void myTask(){
System.out.println(new Date());
}
}
30.Spring Boot邮件发送
30.1 概述
SpringBoot实现邮件功能是非常的方便快捷的,因为SpringBoot默认有starter实现了Mail。
发送邮件应该是网站的必备功能之一,什么注册验证,忘记密码或者是给用户发送营销信息。
最早期的时候我们会使用JavaMail相关api来写发送邮件的相关代码,后来spring退出了JavaMailSender更加简化了邮件发送的过程,在之后springboot对此进行了封装就有了现在的spring-boot-starter-mail。
30.2 最佳实践
先去qq邮箱设置smtp开启,并获得授权码
邮箱->设置->账户->POP3/SMTP服务:开启服务后会获得授权码
30.2.1 创建项目引入依赖(mail)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
30.2.2 修改yml配置文件
spring:
mail:
host: smtp.qq.com #配置服务器qq:smtp.qq.com,网易163:smtp.163.com
password: dxyjutiafqktbdgd #授权码,邮箱->设置->账户->POP3/SMTP服务:开启服务后会获得权码
username: 775610843@qq.com
default-encoding: UTF-8
30.2.3 编写测试发送邮件
@SpringBootTest
class SpringBootEmailApplicationTests {
@Autowired
private JavaMailSender javaMailSender;
@Test
void contextLoads() {
System.out.println(javaMailSender);
}
/**
* 发送基本内容
*/
@Test
void testSend() {
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
// 发件人邮箱
simpleMailMessage.setFrom("775610843@qq.com");
// 收件人邮箱
simpleMailMessage.setTo("2270415677@qq.com");
// 邮件主题
simpleMailMessage.setSubject("这是一个测试邮件");
// 邮件内容
simpleMailMessage.setText("测试内容");
javaMailSender.send(simpleMailMessage);
}
/**
* 测试发送复杂内容,例如图片和附件等
*/
@Test
void testSend2() throws MessagingException {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
// 创建一个邮件工具,可以发送附件
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage,true,"utf-8");
mimeMessageHelper.setFrom("775610843@qq.com");
mimeMessageHelper.setTo("2270415677@qq.com");
mimeMessage.setSubject("这是一个携带了图片和附件的邮件");
//拼接内容参数
StringBuilder sb = new StringBuilder();
sb.append("<html> <body> <h1 style='color:red'>springboot 测试邮件发送复杂格式o</h1>");
sb.append("<p style='color:blue,font-size:16px'>哈哈哈</p>");
sb.append("<p style='text-align:center'>居中</p>");
sb.append("<img src='cid:picture'/> </body></html>"); //如果要插入图片src='cid:picture'
//设置内容,可以被html解析
mimeMessageHelper.setText(sb.toString(), true);
// 从本地磁盘中读取到图片 站位到内容中去
mimeMessageHelper.addInline("picture",new File("C:\Users\cxsxjw\Pictures\Saved Pictures\abc.jpg"));
// 添加附件
mimeMessageHelper.addAttachment("测试文件.xls",new File("D:\测试文件.xls"));
javaMailSender.send(mimeMessage);
}
}
原文地址:https://blog.csdn.net/m0_47946173/article/details/134659362
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_7255.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!