网站抓取qq,哈尔滨网站关键词优化,湖南网站建设的公司排名,东莞展厅设计公司1、热部署 热部署可以替我们节省大把花在重启项目本身上的时间。热部署原理上#xff0c;一个springboot项目在运行时实际上是分两个过程进行的#xff0c;根据加载的东西不同#xff0c;划分成base类加载器与restart类加载器。 base类加载器#xff1a;用来加载jar包中的类…1、热部署 热部署可以替我们节省大把花在重启项目本身上的时间。热部署原理上一个springboot项目在运行时实际上是分两个过程进行的根据加载的东西不同划分成base类加载器与restart类加载器。 base类加载器用来加载jar包中的类jar包中的类和配置文件由于不会发生变化因此不管加载多少次加载的内容不会发生变化 restart类加载器用来加载开发者自己开发的类、配置文件、页面等信息这一类文件受开发者影响 当springboot项目启动时base类加载器执行加载jar包中的信息后restart类加载器执行加载开发者制作的内容。当执行构建项目后由于jar中的信息不会变化因此base类加载器无需再次执行所以仅仅运行restart类加载即可也就是将开发者自己制作的内容重新加载就行了这就完成了一次热部署的过程也可以说热部署的过程实际上是重新加载restart类加载器中的信息。
1.1、手动启动热部署
导入开发者工具对应的坐标
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-devtools/artifactIdoptionaltrue/optional
/dependency
构建项目
ctrlF9
1.2、自动启动热部署
步骤①设置自动构建项目 步骤②允许在程序运行时进行自动构建 1.3、参与热部署监控的文件范围配置 通过修改项目中的文件你可以发现其实并不是所有的文件修改都会激活热部署的原因在于在开发者工具中有一组配置当满足了配置中的条件后才会启动热部署配置中默认不参与热部署的目录信息如下 /META-INF/maven /META-INF/resources /resources /static /public /templates 以上目录中的文件如果发生变化是不参与热部署的。如果想修改配置可以通过application.yml文件进行设定哪些文件不参与热部署操作
spring:devtools:restart:# 设置不参与热部署的文件或文件夹exclude: static/**,public/**,config/application.yml
1.4、关闭热部署 线上环境运行时是不可能使用热部署功能的所以需要强制关闭此功能通过配置可以关闭此功能。
spring:devtools:restart:enabled: false 如果当心配置文件层级过多导致相符覆盖最终引起配置失效可以提高配置的层级在更高层级中配置关闭热部署。例如在启动容器前通过系统属性设置关闭热部署功能。
SpringBootApplication
public class SSMPApplication {public static void main(String[] args) {System.setProperty(spring.devtools.restart.enabled,false);SpringApplication.run(SSMPApplication.class);}
}
2、配置高级
2.1、ConfigurationProperties 在基础篇学习了ConfigurationProperties注解此注解的作用是用来为bean绑定属性的。开发者可以在yml配置文件中以对象的格式添加若干属性
servers:ip-address: 192.168.0.1 port: 2345timeout: -1
然后再开发一个用来封装数据的实体类注意要提供属性对应的setter方法
Component
Data
public class ServerConfig {private String ipAddress;private int port;private long timeout;
}
使用ConfigurationProperties注解就可以将配置中的属性值关联到开发的模型类上
Component
Data
ConfigurationProperties(prefix servers)
public class ServerConfig {private String ipAddress;private int port;private long timeout;
} 这样加载对应bean的时候就可以直接加载配置属性值了。但是目前我们学的都是给自定义的bean使用这种形式加载属性值如果是第三方的bean呢能不能用这种形式加载属性值呢为什么会提出这个疑问原因就在于当前ConfigurationProperties注解是写在类定义的上方而第三方开发的bean源代码不是你自己书写的你也不可能到源代码中去添加ConfigurationProperties注解这种问题该怎么解决呢下面就来说说这个问题。
使用ConfigurationProperties注解其实可以为第三方bean加载属性格式特殊一点而已。
步骤①使用Bean注解定义第三方bean
Bean
public DruidDataSource datasource(){DruidDataSource ds new DruidDataSource();return ds;
}
步骤②在yml中定义要绑定的属性注意datasource此时全小写
datasource:driverClassName: com.mysql.jdbc.Driver
步骤③使用ConfigurationProperties注解为第三方bean进行属性绑定注意前缀是全小写的datasource
Bean
ConfigurationProperties(prefix datasource)
public DruidDataSource datasource(){DruidDataSource ds new DruidDataSource();return ds;
} 操作方式完全一样只不过ConfigurationProperties注解不仅能添加到类上还可以添加到方法上添加到类上是为spring容器管理的当前类的对象绑定属性添加到方法上是为spring容器管理的当前方法的返回值对象绑定属性其实本质上都一样。 做到这其实就出现了一个新的问题目前我们定义bean不是通过类注解定义就是通过Bean定义使用ConfigurationProperties注解可以为bean进行属性绑定那在一个业务系统中哪些bean通过注解ConfigurationProperties去绑定属性了呢因为这个注解不仅可以写在类上还可以写在方法上所以找起来就比较麻烦了。为了解决这个问题spring给我们提供了一个全新的注解专门标注使用ConfigurationProperties注解绑定属性的bean是哪些。这个注解叫做EnableConfigurationProperties。具体如何使用呢
步骤①在配置类上开启EnableConfigurationProperties注解并标注要使用ConfigurationProperties注解绑定属性的类
SpringBootApplication
EnableConfigurationProperties(ServerConfig.class)
public class Springboot13ConfigurationApplication {
}
步骤②在对应的类上直接使用ConfigurationProperties进行属性绑定
Data
ConfigurationProperties(prefix servers)
public class ServerConfig {private String ipAddress;private int port;private long timeout;
} 有人感觉这没区别啊注意观察现在绑定属性的ServerConfig类并没有声明Component注解。当使用EnableConfigurationProperties注解时spring会默认将其标注的类定义为bean因此无需再次声明Component注解了。
总结 使用ConfigurationProperties可以为使用Bean声明的第三方bean绑定属性 当使用EnableConfigurationProperties声明进行属性绑定的bean后无需使用Component注解再次进行bean声明 2.2、松散绑定 在进行属性绑定时可能会遇到如下情况为了进行标准命名开发者会将属性名严格按照驼峰命名法书写在yml配置文件中将datasource修改为dataSource如下
dataSource:driverClassName: com.mysql.jdbc.Driver
此时程序可以正常运行然后又将代码中的前缀datasource修改为dataSource如下
Bean
ConfigurationProperties(prefix dataSource)
public DruidDataSource datasource(){DruidDataSource ds new DruidDataSource();return ds;
} 此时就发生了编译错误而且并不是idea工具导致的运行后依然会出现问题配置属性名dataSource是无效的
Configuration property name dataSource is not valid:
Invalid characters: SBean: datasourceReason: Canonical names should be kebab-case (- separated), lowercase alpha-numeric characters and must start with a letter
Action:
Modify dataSource so that it conforms to the canonical names requirements. 为什么会出现这种问题这就要来说一说springboot进行属性绑定时的一个重要知识点了有关属性名称的宽松绑定也可以称为宽松绑定。 什么是宽松绑定实际上是springboot进行编程时人性化设计的一种体现即配置文件中的命名格式与变量名的命名格式可以进行格式上的最大化兼容。兼容到什么程度呢几乎主流的命名格式都支持例如
在ServerConfig中的ipAddress属性名
Component
Data
ConfigurationProperties(prefix servers)
public class ServerConfig {private String ipAddress;
}
可以与下面的配置属性名规则全兼容
servers:ipAddress: 192.168.0.2 # 驼峰模式ip_address: 192.168.0.2 # 下划线模式ip-address: 192.168.0.2 # 烤肉串模式IP_ADDRESS: 192.168.0.2 # 常量模式 也可以说以上4种模式最终都可以匹配到ipAddress这个属性名。为什么这样呢原因就是在进行匹配时配置中的名称要去掉中划线和下划线后忽略大小写的情况下去与java代码中的属性名进行忽略大小写的等值匹配以上4种命名去掉下划线中划线忽略大小写后都是一个词ipaddressjava代码中的属性名忽略大小写后也是ipaddress这样就可以进行等值匹配了这就是为什么这4种格式都能匹配成功的原因。不过springboot官方推荐使用烤肉串模式也就是中划线模式。
到这里我们掌握了一个知识点就是命名的规范问题。再来看开始出现的编程错误信息
Configuration property name dataSource is not valid:
Invalid characters: SBean: datasourceReason: Canonical names should be kebab-case (- separated), lowercase alpha-numeric characters and must start with a letter
Action:
Modify dataSource so that it conforms to the canonical names requirements. 其中Reason描述了报错的原因规范的名称应该是烤肉串(kebab)模式(case)即使用-分隔使用小写字母数字作为标准字符且必须以字母开头。然后再看我们写的名称dataSource就不满足上述要求。闹了半天在书写前缀时这个词不是随意支持的必须使用上述标准。编程写了这么久基本上编程习惯都养成了到这里又被springboot教育了没辙谁让人家东西好用呢按照人家的要求写吧。
总结 ConfigurationProperties绑定属性时支持属性名宽松绑定这个宽松体现在属性名的命名规则上 Value注解不支持松散绑定规则 绑定前缀名推荐采用烤肉串命名规则即使用中划线做分隔符 2.3、常用计量单位绑定 在前面的配置中我们书写了如下配置值其中第三项超时时间timeout描述了服务器操作超时时间当前值是-1表示永不超时。
servers:ip-address: 192.168.0.1 port: 2345timeout: -1 但是每个人都这个值的理解会产生不同比如线上服务器完成一次主从备份配置超时时间240这个240如果单位是秒就是超时时间4分钟如果单位是分钟就是超时时间4小时。面对一次线上服务器的主从备份设置4分钟简直是开玩笑别说拷贝过程备份之前的压缩过程4分钟也搞不定这个时候问题就来了怎么解决这个误会 除了加强约定之外springboot充分利用了JDK8中提供的全新的用来表示计量单位的新数据类型从根本上解决这个问题。以下模型类中添加了两个JDK8中新增的类分别是Duration和DataSize
Component
Data
ConfigurationProperties(prefix servers)
public class ServerConfig {//时间范围DurationUnit(ChronoUnit.HOURS)private Duration serverTimeOut;//文件单位DataSizeUnit(DataUnit.MEGABYTES)private DataSize dataSize;
}
Duration表示时间间隔可以通过DurationUnit注解描述时间单位例如上例中描述的单位为小时ChronoUnit.HOURS
DataSize表示存储空间可以通过DataSizeUnit注解描述存储空间单位例如上例中描述的单位为MBDataUnit.MEGABYTES 使用上述两个单位就可以有效避免因沟通不同步或文档不健全导致的信息不对称问题从根本上解决了问题避免产生误读。
2.4、格式校验 目前我们在进行属性绑定时可以通过松散绑定规则在书写时放飞自我了但是在书写时由于无法感知模型类中的数据类型就会出现类型不匹配的问题比如代码中需要int类型配置中给了非法的数值例如写一个“a这种数据肯定无法有效的绑定还会引发错误。 SpringBoot给出了强大的数据校验功能可以有效的避免此类问题的发生。在JAVAEE的JSR303规范中给出了具体的数据校验标准开发者可以根据自己的需要选择对应的校验框架此处使用Hibernate提供的校验框架来作为实现进行数据校验。书写应用格式非常固定话不多说直接上步骤
步骤①开启校验框架
!--1.导入JSR303规范--
dependencygroupIdjavax.validation/groupIdartifactIdvalidation-api/artifactId
/dependency
!--使用hibernate框架提供的校验器做实现--
dependencygroupIdorg.hibernate.validator/groupIdartifactIdhibernate-validator/artifactId
/dependency
步骤②在需要开启校验功能的类上使用注解Validated开启校验功能
Component
Data
ConfigurationProperties(prefix servers)
//开启对当前bean的属性注入校验
Validated
public class ServerConfig {
}
步骤③对具体的字段设置校验规则
Component
Data
ConfigurationProperties(prefix servers)
//开启对当前bean的属性注入校验
Validated
public class ServerConfig {//设置具体的规则Max(value 8888,message 最大值不能超过8888)Min(value 202,message 最小值不能低于202)private int port;
} 通过设置数据格式校验就可以有效避免非法数据加载其实使用起来还是挺轻松的基本上就是一个格式。
总结 开启Bean属性校验功能一共3步导入JSR303与Hibernate校验框架坐标、使用Validated注解启用校验功能、使用具体校验规则规范数据校验格式
3、测试
3.1、加载测试专用属性
临时属性 springboot已经为我们开发者早就想好了这种问题该如何解决并且提供了对应的功能入口。在测试用例程序中可以通过对注解SpringBootTest添加属性来模拟临时属性具体如下
//properties属性可以为当前测试用例添加临时的属性配置
SpringBootTest(properties {test.proptestValue1})
public class PropertiesAndArgsTest {
Value(${test.prop})private String msg;Testvoid testProperties(){System.out.println(msg);}
} 使用注解SpringBootTest的properties属性就可以为当前测试用例添加临时的属性覆盖源码配置文件中对应的属性值进行测试。
临时参数 除了上述这种情况在前面讲解使用命令行启动springboot程序时讲过通过命令行参数也可以设置属性值。而且线上启动程序时通常都会添加一些专用的配置信息。作为运维人员他们才不懂java更不懂这些配置的信息具体格式该怎么写那如果我们作为开发者提供了对应的书写内容后能否提前测试一下这些配置信息是否有效呢当时是可以的还是通过注解SpringBootTest的另一个属性来进行设定。
//args属性可以为当前测试用例添加临时的命令行参数
SpringBootTest(args{--test.proptestValue2})
public class PropertiesAndArgsTest {Value(${test.prop})private String msg;Testvoid testProperties(){System.out.println(msg);}
} 使用注解SpringBootTest的args属性就可以为当前测试用例模拟命令行参数并进行测试。
临时参数临时配置配置文件
3.2、加载测试专用配置 学习过Spring的知识我们都知道其实一个spring环境中可以设置若干个配置文件或配置类若干个配置信息可以同时生效。现在我们的需求就是在测试环境中再添加一个配置类然后启动测试环境时生效此配置就行了。其实做法和spring环境中加载多个配置信息的方式完全一样。具体操作步骤如下
步骤①在测试包test中创建专用的测试环境配置类
Configuration
public class MsgConfig {Beanpublic String msg(){return bean msg;}
}
上述配置仅用于演示当前实验效果实际开发可不能这么注入String类型的数据
步骤②在启动测试环境时导入测试环境专用的配置类使用Import注解即可实现
SpringBootTest
Import({MsgConfig.class})
public class ConfigurationTest {
Autowiredprivate String msg;
Testvoid testConfiguration(){System.out.println(msg);}
} 到这里就通过Import属性实现了基于开发环境的配置基础上对配置进行测试环境的追加操作实现了11的配置环境效果。这样我们就可以实现每一个不同的测试用例加载不同的bean的效果丰富测试用例的编写同时不影响开发环境的配置。
总结 定义测试环境专用的配置类然后通过Import注解在具体的测试中导入临时的配置例如测试用例方便测试过程且上述配置不影响其他的测试类环境
3.3、Web环境模拟测试 在测试中对表现层功能进行测试需要一个基础和一个功能。所谓的一个基础是运行测试程序时必须启动web环境不然没法测试web功能。一个功能是必须在测试程序中具备发送web请求的能力不然无法实现web功能的测试。所以在测试用例中测试表现层接口这项工作就转换成了两件事一如何在测试类中启动web测试二如何在测试类中发送web请求。下面一件事一件事进行先说第一个
3.3.1、测试类中启动web环境 每一个springboot的测试类上方都会标准SpringBootTest注解而注解带有一个属性叫做webEnvironment。通过该属性就可以设置在测试用例中启动web环境具体如下
SpringBootTest(webEnvironment SpringBootTest.WebEnvironment.RANDOM_PORT)
public class WebTest {
} 测试类中启动web环境时可以指定启动的Web环境对应的端口springboot提供了4种设置值分别如下 MOCK根据当前设置确认是否启动web环境例如使用了Servlet的API就启动web环境属于适配性的配置 DEFINED_PORT使用自定义的端口作为web服务器端口 RANDOM_PORT使用随机端口作为web服务器端口 NONE不启动web环境 通过上述配置现在启动测试程序时就可以正常启用web环境了建议大家测试时使用RANDOM_PORT避免代码中因为写死设定引发线上功能打包测试时由于端口冲突导致意外现象的出现。就是说你程序中写了用8080端口结果线上环境8080端口被占用了结果你代码中所有写的东西都要改这就是写死代码的代价。现在你用随机端口就可以测试出来你有没有这种问题的隐患了。 测试环境中的web环境已经搭建好了下面就可以来解决第二个问题了如何在程序代码中发送web请求。
3.3.2、测试类中发送请求 对于测试类中发送请求其实java的API就提供对应的功能只不过平时各位小伙伴接触的比较少所以较为陌生。springboot为了便于开发者进行对应的功能开发对其又进行了包装简化了开发步骤具体操作如下
步骤①在测试类中开启web虚拟调用功能通过注解AutoConfigureMockMvc实现此功能的开启
SpringBootTest(webEnvironment SpringBootTest.WebEnvironment.RANDOM_PORT)
//开启虚拟MVC调用
AutoConfigureMockMvc
public class WebTest {
}
步骤②定义发起虚拟调用的对象MockMVC通过自动装配的形式初始化对象
SpringBootTest(webEnvironment SpringBootTest.WebEnvironment.RANDOM_PORT)
//开启虚拟MVC调用
AutoConfigureMockMvc
public class WebTest {
Testvoid testWeb(Autowired MockMvc mvc) {}
}
步骤③创建一个虚拟请求对象封装请求的路径并使用MockMVC对象发送对应请求
SpringBootTest(webEnvironment SpringBootTest.WebEnvironment.RANDOM_PORT)
//开启虚拟MVC调用
AutoConfigureMockMvc
public class WebTest {
Testvoid testWeb(Autowired MockMvc mvc) throws Exception {//http://localhost:8080/books//创建虚拟请求当前访问/booksMockHttpServletRequestBuilder builder MockMvcRequestBuilders.get(/books);//执行对应的请求mvc.perform(builder);}
} 执行测试程序现在就可以正常的发送/books对应的请求了注意访问路径不要写http://localhost:8080/books因为前面的服务器IP地址和端口使用的是当前虚拟的web环境无需指定仅指定请求的具体路径即可。
总结 在测试类中测试web层接口要保障测试类启动时启动web容器使用SpringBootTest注解的webEnvironment属性可以虚拟web环境用于测试 为测试方法注入MockMvc对象通过MockMvc对象可以发送虚拟请求模拟web请求调用过程
3.3.3、web环境请求结果比对 上一节已经在测试用例中成功的模拟出了web环境并成功的发送了web请求本节就来解决发送请求后如何比对发送结果的问题。其实发完请求得到的信息只有一种就是响应对象。至于响应对象中包含什么就可以比对什么。常见的比对内容如下 响应状态匹配 Test
void testStatus(Autowired MockMvc mvc) throws Exception {MockHttpServletRequestBuilder builder MockMvcRequestBuilders.get(/books);ResultActions action mvc.perform(builder);//设定预期值 与真实值进行比较成功测试通过失败测试失败//定义本次调用的预期值StatusResultMatchers status MockMvcResultMatchers.status();//预计本次调用时成功的状态200ResultMatcher ok status.isOk();//添加预计值到本次调用过程中进行匹配action.andExpect(ok);
} 响应体匹配非json数据格式 Test
void testBody(Autowired MockMvc mvc) throws Exception {MockHttpServletRequestBuilder builder MockMvcRequestBuilders.get(/books);ResultActions action mvc.perform(builder);//设定预期值 与真实值进行比较成功测试通过失败测试失败//定义本次调用的预期值ContentResultMatchers content MockMvcResultMatchers.content();ResultMatcher result content.string(springboot2);//添加预计值到本次调用过程中进行匹配action.andExpect(result);
} 响应体匹配json数据格式开发中的主流使用方式 Test
void testJson(Autowired MockMvc mvc) throws Exception {MockHttpServletRequestBuilder builder MockMvcRequestBuilders.get(/books);ResultActions action mvc.perform(builder);//设定预期值 与真实值进行比较成功测试通过失败测试失败//定义本次调用的预期值ContentResultMatchers content MockMvcResultMatchers.content();ResultMatcher result content.json({\id\:1,\name\:\springboot2\,\type\:\springboot\});//添加预计值到本次调用过程中进行匹配action.andExpect(result);
} 响应头信息匹配 Test
void testContentType(Autowired MockMvc mvc) throws Exception {MockHttpServletRequestBuilder builder MockMvcRequestBuilders.get(/books);ResultActions action mvc.perform(builder);//设定预期值 与真实值进行比较成功测试通过失败测试失败//定义本次调用的预期值HeaderResultMatchers header MockMvcResultMatchers.header();ResultMatcher contentType header.string(Content-Type, application/json);//添加预计值到本次调用过程中进行匹配action.andExpect(contentType);
} 基本上齐了头信息正文信息状态信息都有了就可以组合出一个完美的响应结果比对结果了。以下范例就是三种信息同时进行匹配校验也是一个完整的信息匹配过程。
Test
void testGetById(Autowired MockMvc mvc) throws Exception {MockHttpServletRequestBuilder builder MockMvcRequestBuilders.get(/books);ResultActions action mvc.perform(builder);
StatusResultMatchers status MockMvcResultMatchers.status();ResultMatcher ok status.isOk();action.andExpect(ok);
HeaderResultMatchers header MockMvcResultMatchers.header();ResultMatcher contentType header.string(Content-Type, application/json);action.andExpect(contentType);
ContentResultMatchers content MockMvcResultMatchers.content();ResultMatcher result content.json({\id\:1,\name\:\springboot\,\type\:\springboot\});action.andExpect(result);
}
总结 web虚拟调用可以对本地虚拟请求的返回响应信息进行比对分为响应头信息比对、响应体信息比对、响应状态信息比对
3.3.4、数据层测试回滚 当前我们的测试程序可以完美的进行表现层、业务层、数据层接口对应的功能测试了但是测试用例开发完成后在打包的阶段由于test生命周期属于必须被运行的生命周期如果跳过会给系统带来极高的安全隐患所以测试用例必须执行。但是新的问题就呈现了测试用例如果测试时产生了事务提交就会在测试过程中对数据库数据产生影响进而产生垃圾数据。这个过程不是我们希望发生的作为开发者测试用例该运行运行但是过程中产生的数据不要在我的系统中留痕这样该如何处理呢 springboot早就为开发者想到了这个问题并且针对此问题给出了最简解决方案在原始测试用例中添加注解Transactional即可实现当前测试用例的事务不提交。当程序运行后只要注解Transactional出现的位置存在注解SpringBootTestspringboot就会认为这是一个测试程序无需提交事务所以也就可以避免事务的提交。
SpringBootTest
Transactional
Rollback(true)
public class DaoTest {Autowiredprivate BookService bookService;
Testvoid testSave(){Book book new Book();book.setName(springboot3);book.setType(springboot3);book.setDescription(springboot3);
bookService.save(book);}
} 如果开发者想提交事务也可以再添加一个RollBack的注解设置回滚状态为false即可正常提交事务是不是很方便springboot在辅助开发者日常工作这一块展现出了惊人的能力实在太贴心了。
总结 在springboot的测试类中通过添加注解Transactional来阻止测试用例提交事务 通过注解Rollback控制springboot测试类执行结果是否提交事务需要配合注解Transactional使用
3.3.5、测试用例数据设定 对于测试用例的数据固定书写肯定是不合理的springboot提供了在配置中使用随机值的机制确保每次运行程序加载的数据都是随机的。具体如下
testcase:book:id: ${random.int}id2: ${random.int(10)}type: ${random.int!5,10!}name: ${random.value}uuid: ${random.uuid}publishTime: ${random.long} 当前配置就可以在每次运行程序时创建一组随机数据避免每次运行时数据都是固定值的尴尬现象发生有助于测试功能的进行。数据的加载按照之前加载数据的形式使用ConfigurationProperties注解即可
Component
Data
ConfigurationProperties(prefix testcase.book)
public class BookCase {private int id;private int id2;private int type;private String name;private String uuid;private long publishTime;
}
对于随机值的产生还有一些小的限定规则比如产生的数值性数据可以设置范围等具体如下 ${random.int}表示随机整数 ${random.int(10)}表示10以内的随机数 ${random.int(10,20)}表示10到20的随机数 其中()可以是任意字符例如[]!!均可
总结 使用随机数据可以替换测试用例中书写的固定数据提高测试用例中的测试数据有效性