SSM
Springboot
分布式微服务
回顾: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配置文件,同时也大大简化了项目的开发。
那么,问题来了,究竟是应该使用xml还是注解呢?
1.3 注解还是XML
在spring早期版本中,由于当时的JDK并不支持注解,因此只能使用XML的配置,很快,随着JDK升级到JDK5之后,它加入了注解的新特性,这样注解就被广泛地使用起来, 于是spring内部也分为两派,一派是使用XML的,一派是使用注解的,为了简化开发,在spring2.X之后的版本也引入了注解,不过是少量的注解,如@Component @Service等,但是功能还是不强大,因此对于srping的开发,大部分情况下还是使用XML为主,随着注解的增加,尤其是Servlet3.0
(
@WebServlet
@WebFilter
@WebListener
)
之后,WEB容器可以脱离web.xml的部署,使用得WEB容器完全可以基于注解开发,对于spring3和spring4的版本注解功能越来越强大。对于XML的依赖起来越少,到了4.0完全可以脱离XML, 所以在spring中使用注解开发占据了主流地位,近年来。微服务的流行,越来越多的企业要求快速开发,所以spring Boot更加兴旺了。
应用的基本配置用xml,比如:数据源、资源文件等;
业务开发用注解,比如:Service中注入bean等;
1.4 Spring3.x到Spring4.x
从Spring3.x开始提供了Java配置方式,使用Java配置方式可以更好的理解你配置的Bean,现在我们就处于这个时代,并且Spring4.x和Springboot都推荐使用java配置的方式。
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的优点
约定大于配置
创建独立的spring应用程序。
嵌入的tomcat jetty 或者undertow 不用部署WAR文件。
允许通过Maven来根据需要获取starter
尽可能的使用自动配置spring
提供生产就绪功能,如指标,健康检查和外部配置properties yaml yml
完全没有代码生成,对XML没有要求配置[不用]
2.2.1 特性理解
为基于 Spring 的开发提供更快的入门体验
开箱即用,没有代码生成,也无需 XML 配置。同时也可以修改默认值来满足特定的需求。
提供了一些大型项目中常见的非功能特性,如嵌入式服务器、安全、指标,健康检测、外部配置等。
Spring Boot 并不是对 Spring 功能上的增强,而是提供了一种快速使用 Spring 的方式。
2.2.2 传统开发模式
所有的功能打包在一个 WAR包里,基本没有外部依赖(除了容器),部署在一个JEE容器(Tomcat,JBoss,WebLogic)里,包含了 DO/DAO,Service,UI等所有逻辑。
2.2.2.1 优点:
l 开发简单,集中式管理
l 基本不会重复开发
l 功能都在本地,没有分布式的管理和调用消耗
2.2.2.2 缺点:
l 效率低:开发都在同一个项目改代码,相互等待,冲突不断
l 维护难:代码功功能耦合在一起,新人不知道何从下手
l 不灵活:构建时间长,任何小修改都要重构整个项目,耗时
l 稳定性差:一个微小的问题,都可能导致整个应用挂掉
l 扩展性不够:无法满足高并发下的业务需求
l 对服务器的性能要求要统一,要高
2.2.3 微服务开发
微服务:架构风格(服务微化)
微服务是指开发一个单个小型的但有业务功能的服务,每个服务都有自己的处理和轻量通信机制,可以部署在单个或多个服务器上,微服务也指一种松耦合的,有一定有界上下文的面向服务的架构
目的:有效的拆分应用,实现敏捷开发和部署
2.2.3.1 优点
l 每个微服务都很小,这样能聚焦一个指定的业务功能或业务需求
l 微服务能够被小团队开发,这个小团队2-5人就可以完成了
l 微服务是松耦合的,是有功能,有意义的服务,开发阶段或部署阶段都是独立的
l 微服务可以使用不同的语言开发
l 微服务能部署在中低端配置的服务器上
l 很容易和第三方集成
l 每个服务都有自己的存储能力,单独的库,也可以有统一的库
2.2.3.2 缺点
l 微服务会带来过多的操作
l 可能有双倍的努力
l 分布式系统可能复杂难管理
l 分布跟踪部署难
l 当服务数量增加时,管理复杂度增加
3.Spring Boot 入门程序3.1 环境准备
①JDK1.8
②Maven3.6+
③IDEA2020.1
④SpringBoot2.x
⑤Spring5.x
3.2 新建项目
3.3 在项目里面创建模块
这里我们选择创建Module来完成第一个Spring Boot项目
3.4 项目目录说明
3.5 Pom.xml文件分析
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.9 打jar包启动测试
在target目录下找到jar包并且在文件夹中打开
测试访问
http://localhost:8080/hello
3.10 彩蛋
启动时查看控制台的输出会发现有一个banner图案,这个banner图案是可以自定义的
3.10.1 修改为自己的banner
在src/main/resources目录下面创建一个banner.txt文件,并且输入想要的内容即可
这里提供一个字体生产网站 https://www.bootschool.net/ascii 可以随意创建
3.10.2 启动查看
4.1 回顾spring和java的注解
4.1.1 类上的注解
@Controller
控制器
@RestController=@Controller+@ResponseBody
返回json的控制器
@Service
标记服务接口
@Respority
标记仓库接口
@Component
标记组件
@RequestMapping
请求映射 (也可在方法上)
作用在类上的常用注解
4.1.2 方法上的注解
@RequestMapping
请求映射
@GetMapping
GET请求
@PostMapping
POST请求
@DeleteMapping
DELETE请求
@PutMapping
PUT请求
@PatchMapping
PATCH请求
@ResponseBody
返回JSON对象
4.1.3 参数上的注解
@RequestBody
入参是JSON对象
@PathVariable
将路径上面的参数映射到入参里面
@RequestParam
将请求参数绑定到你控制器的方法参数上
4.1.4 属性上的注解
@Autowried
自动注入(首选按照类型) byType byName
@Resource
自动注入(首选按照名字)byName byType
4.2 相关注解说明
4.2.1 @Configuration
作用于类上,相当于一个xml配置文件;
|–application-dao.xml
4.2.2 @Bean
作用于方法上,相当于xml配置中的
4.2.2.1 创建实体类测试注解用法
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”)配置扫描
|–
5.1 什么是热部署
spring为开发者提供了一个名为spring-boot-devtools的模块来使springboot应用支持热部署,提高开发的效率,修改代码后无需重启应用
5.2 添加依赖
org.springframework.boot spring-boot-devtools runtime true5.3 配置idea的启动面板
如果不配置面板,那么可直接使用ctrl+F9去刷新,配置 以后,当我们修改代码,光标失去idea的焦点以后,就出触发自动部署
6.1 创建maven项目
6.2 修改pom.xml
6.3 创建启动类
6.4 创建测试类
6.5 创建static,templates
6.6 创建application.properties
1,默认的包扫描
启动类所在的包及其子包
2,依赖的分析
7.1 pom的依赖分析
点进去spring-boot-starter-parent以后这依赖管理里面写和很从版本
这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心;以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了
7.2 启动器 spring-boot-starter
springboot-boot-starter-xxx:就是spring-boot的场景启动器
SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来 , 我们要用什么功能就导入什么样的场景启动器即可 ,也可以自己自定义 starter;
7.3 原理分析
7.3.1 注解功能划分
7.3.2 如何自动扫描(默认扫描启动类所在的包以下面的及子包)
@Import(AutoConfigurationPackages.Registrar.class)
在Registrar里面,找到register()方法
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容器中
7.4.2 再SpringBoot中只要我们加了Web的starter,就默认做好了
7.4.2.1 查看DispatcherServletAutoConfiguration这个自动配置类
现在深刻理解了SpringBoot的 约定大于配置 这一句话了吧
8.Spring Boot的run方法到底执行了什么https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.build-systems.starters
如果你需要使用,可以直接添加对应的starter的依赖,那么只需要自定一些参数即可,因为已经有默认配置了,但是如果你需要的组件没有starter,那么久需要自己写配置类了
10.Spring Boot的配置文件语法【重中之重】10.1 首先引入依赖
10.2 创建Hero 类
@Data // lombok的注解,生成get和set方法
@Component // 标记为spring的一个组件
@ConfigurationProperties(prefix = “hero”) // 使用hero前缀去ioc容器里面读取配置文件
public class Hero {
}
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,两种配置文件是互补的存在
10.9 练习作业
在Hero中添加一个属性
private List user;
如何通过配置文件注入
11.@Value读取配置文件11.1 创建Hero2类
@Data // lombok的注解,生成get和set方法
@Component // 标记为spring的一个组件
public class Hero2 {
// @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
private 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.1 引入依赖
Springboot2.3.x以后,需要单独引入依赖,之前在web-starter里面包含
12.2 修改Hero类
12.3 修改application.yml文件
12.4 测试
12.5 常用的验证注解
@NotNull --不能为null,但可以空着不写 (name:)
@NotEmpty --不能为空,也不能为null,但可以是空格 “ ”
@NotBlank --不能为空,也不能为null,也不能为空格
@Min 最大值
@Max 最小值
@Size(min = 1, max = 6) 长度限制
@Range(min = 1,max = 2) 范围限制
@Pattern(regexp"[0,1]{1}") 正则限制
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 {
}
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
13.9 启动类添加@ImportResource注解
13.10 测试
14.1 为什么要使用profiles
在开发中,一般有两种环境
有时候开发环境和生产环境的配置方法是不一样的,那么如何快速的切换呢,这里就要使用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.1 项目内部配置文件
spring boot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件
其中同一目标下的properties文件的优先级大于yml文件
15.1.1 配置文件可以放的位置和优先级
classpath:/ --优先级4
classpath:/config/ --优先级3
file:./ --优先级2
file:./config/ --优先级1
15.1.2 查看ConfigDataEnvironment
15.1.3 测试注意
在测试的时候,需要修改pom的编译路径,确保把所有的配置文件都编译以后再测试
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 {
}
16.4 总结
SpringBoot启动会加载大量的自动配置类
看我们需要的功能有没有SpringBoot默认写好的自动配置类;
我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件有,我们就不需要再来配置了)
给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们就可
以在配置文件中指定这些属性的值;
- xxxxAutoConfigurartion:自动配置类;给容器中添加组件 - xxxxProperties:封装配置文件中的默认配置16.5 @Conditional派生注解
作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置类里面的所有内容才生效;(条件之间是并且的关系)
16.6 如何查看自动配置类生效
我们可以通过启用debug=true属性(在配置文件配置);来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效;
17.1 概述
在搭建新的系统时候必不可少的是需要日志的,日志的作用就不用多说了吧,可以用来调试程序,记录程序运行的状态,最重要的是可以用来排查线上的问题。
那我们该如何在项目中使用日志呢?
SpringBoot内部集成了LogBack日志依赖,SpringBoot默认使用LogBack记录日志信息,默认根据base.xml配置内容来输出到控制台和文件之中,不过这些默认的配置不能达到企业级项目的要求
17.2 创建模块
17.3 创建logback-spring.xml
17.4 修改启动类测试
17.5 查看本地日志
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 {
/**
吃饭的方法@param foodsName@return*/
public String eat(String foodsName) {
System.out.println(“吃” + foodsName);
return foodsName + “很好吃”;
}
}
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.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 {
/**
重写静态资源配置的方法@param registry*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(“/static/**”)
.addResourceLocations(“classpath:/mystatic/”);
}
}
19.3 webjars的访问规则
19.3.1 什么是webjars
WebJars是打包到JAR(Java Archive)文件中的客户端Web库(例如jQuery和Bootstrap)。
在基于JVM的Web应用程序中显式轻松地管理客户端依赖项
使用基于JVM的构建工具(例如Maven,Gradle,sbt,…)来下载客户端依赖项
了解您正在使用的客户端依赖项
传递依赖关系会自动解析,并可选择通过RequireJS加载
官网:https://www.webjars.org/
19.3.2 引入依赖
19.3.3 启动访问
http://localhost:8080/webjars/jquery/3.6.0/jquery.js
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的依赖
20.3 Spring Boot项目Thymeleaf模板页面存放位置
查看Thymeleaf的自动配置类
当然我们也可以自己修改他的位置,只需要在yml文件中修改即可,一般不做修改
20.4 通过Controller跳转到Thymeleaf的页面
20.4.1 在指定位置下创建hello.html
hello Thymeleaf
我是classpath:/templates/hello.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 th:each 遍历
表达式基本对象
l #locale 本地环境
l #httpServletRequest HttpServletRquest对象
l #httpSession HttpSession对象
l #servletContext servletContext对象
常用工具对象
l #numbers 格式化数字对象的实用方法
l #strings 字符串对象的实用方法:包含startsWith,将/附加等
l #dates java.util的实用方法。对象:日期格式、组件提取等
l #objects:实用方法的对象。
l #bools:布尔评价的实用方法。
l #arrays:数组的实用方法。
l #lists:list集合。
l #sets:set集合。
l #maps:map集合。
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页面
英雄面板
英雄id英雄名字
英雄性别
英雄年龄
英雄年龄
英雄国家
英雄电话
英雄生日
英雄生日
英雄存款
20.6.4 测试访问
http://localhost:8080/helloHero
20.7 Thymeleaf读取Model里面的集合
20.7.1 在RouteController增加一个方法跳转
20.7.2 创建showHeroList页面
英雄名字
英雄性别
英雄年龄
英雄年龄
英雄国家
英雄电话
英雄生日
英雄生日
英雄存款
20.7.3 测试访问
http://localhost:8080/helloHeroList
20.8 ThymeleafObjects的使用
20.8.1 在RouteController增加一个方法跳转
20.8.2 创建showObj页面
request:
session:
servletContext:
20.8.3 测试访问
http://localhost:8080/thymeleafObject
20.9 Thymeleaf在js中取值
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”;
}
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
3.消息转换 --MessageConverter
4.视图解析 --(ContentNegotiatingViewResolver)
5.格式化 --addFormatters
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 ----java.utils.Date
format.date-time —java.time.LocalDateTime
21.5 欢迎页面自动配置
创建一个配置类实现WebMvcConfigurer重写之前的方法即可实现自定义拓展
22.1 自定义视图解析器【熟悉】
这样我们就不需要写controller
22.2 自定义拦截器【掌握】
22.2.1 创建自己的拦截器
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 ServletRegistrationBean23.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 FilterRegistrationBean23.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 ServletListenerRegistrationBean24.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的依赖
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 ServletRegistrationBean24.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】
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 创建新模块并添加依赖
25.3 根据数据库创建User对象(逆向工程)
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
25.8.2 创建mybatis-cfg.xml文件
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
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.1 创建项目,选择打包方式为war包
26.2 选择依赖
26.3 配置pom.xml
26.4 添加IDEA配置
26.5 配置外部tomcat
26.6 创建controller
@Controller
public class TestController {
}
26.7 创建页面
26.8 修改配置文件
spring:
mvc:
view:
prefix: /WEB-INF/view/
suffix: .jsp
26.9 启动访问
http://localhost:8080/test
27.1 问题描述
随着互联网技术的发展,现在的网站架构基本都由原来的后端渲染,变成了:前端渲染、前后端分离的形态,而且前端技术和后端技术在各自的道路上越走越远。 前端和后端的唯一联系,变成了API接口;API文档变成了前后端开发人员联系的纽带,变得越来越重要,swagger就是一款让你更好的书写API文档的框架,而且swagger可以完全模拟http请求,入参出参和实际情况差别几乎为零。
没有API文档工具之前,大家都是手写API文档的(维护起来相当困难),在什么地方书写的都有,有在confluence上写的,有在对应的项目目录下readme.md上写的,每个公司都有每个公司的玩法,无所谓好坏。但是能称之为“框架”的,估计也只有swagger了
27.2 使用步骤
27.2.1 创建项目加入依赖
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:ss27.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 List27.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) { HashMap27.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
同步:
异步:
java里面这病异步就是多线程
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 Map28.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 Map28.2.8 再次测试
需求:领导说,每天晚上12把前一天的销售统计报表发给我
写一个任务
1,从数据库里面取数据进行处理
2,写一个定时任务 每天12点之前执行
3,从数据库里面查询数据处理完成之后以邮件的形式发送给领导
29.1 概述
sprignBoot定时任务是与quartz整合,不需要添加任何的依赖
在springBoot的启动类上添加@EnableScheduling注解开启定时调度
在需要定时调度的方法上添加@Scheduled这个注解即可,其中可以指定cron表达式和其他的定时方式
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)
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-830.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("springboot 测试邮件发送复杂格式o
"); sb.append("哈哈哈
"); sb.append("居中
"); sb.append(""); //如果要插入图片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); } }