目录
- Web
- 定制SpringMVC
- 定制某些mvc配置
- 定制mvc核心组件
- 完全自定义mvc
- 静态资源
- 静态资源映射
- 静态资源缓存
- 示例
- 自定义静态资源规则
- 配置方式
- 代码方式
- 欢迎页
- Favicon(网站图标)
- 路径匹配策略
- 内容协商
- 制定返回json类型数据
- 制定返回xml类型数据
- 制定返回自定义类型数据
- 新增其他内容协商
- 系统提供的MessageConverter
- 返回yaml类型数据
- 效果
- 模板引擎
- Thymeleaf整合
- 拦截器
- 国际化
- 错误处理
- 实战
- 数据访问
- ssm整合
- springBoot基础特性
- profile环境隔离
- 环境包含
- 环境分组
- 组件环境隔离
- 配置文件环境隔离
- 外部化配置
- 配置生效优先级
- 导入外配置文件
- 属性占位符
- 单元测试
- springBoot核心原理
- 事件和监听器
- 生命周期监听
Web
定制SpringMVC
定制某些mvc配置
-
如果您想保持Spring Boot MVC的默认配置,并在此基础上添加一些自定义配置,比如拦截器、格式化器、视图控制器等,可以创建一个使用
@Configuration
注解的配置类,并实现WebMvcConfigurer
接口。同时不需要标注@EnableWebMvc
注解。@Configuration public class CustomWebMvcConfigurer implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { // 添加拦截器逻辑 } @Override public void addFormatters(FormatterRegistry registry) { // 添加格式化器逻辑 } @Override public void addViewControllers(ViewControllerRegistry registry) { // 添加视图控制器逻辑 } }
例子
- 自定义拦截器: 假设您希望在每个请求前后记录请求日志。首先,创建一个拦截器类来处理请求日志逻辑
public class LoggingInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 在请求之前记录日志
System.out.println("请求开始:" + request.getRequestURI());
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
// 请求处理完成后记录日志
System.out.println("请求结束:" + request.getRequestURI());
}
}
- 自定义格式化器: 假设您需要在URL参数中接收日期,并在处理请求时将其转换为
java.util.Date
类型。首先,创建一个格式化器类来完成日期的格式化/解析:
public class DateFormatter implements Formatter<Date> {
@Override
public Date parse(String text, Locale locale) throws ParseException {
// 将文本解析为日期对象
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", locale);
return dateFormat.parse(text);
}
@Override
public String print(Date date, Locale locale) {
// 将日期对象格式化为文本
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", locale);
return dateFormat.format(date);
}
}
- 自定义视图控制器: 假设您希望处理一些简单的URL映射,而无需创建完整的Controller。首先,创建一个视图控制器类来处理URL映射:
@Controller
public class ViewController {
@GetMapping("/hello")
public String hello() {
return "hello"; // 返回视图模板名称
}
}
- 在相关配置类中注册
@Configuration
public class CustomWebMvcConfigurer implements WebMvcConfigurer {
//拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggingInterceptor());
}
//格式化器
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addFormatter(new DateFormatter());
}
//视图控制器
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/hello").setViewName("hello");
}
}
定制mvc核心组件
-
如果您只需要自定义一些核心组件实例,比如
RequestMappingHandlerMapping
、RequestMappingHandlerAdapter
或ExceptionHandlerExceptionResolver
,可以通过创建一个WebMvcRegistrations
组件并将其注册到Spring容器中实现。这样您可以自行创建和配置这些核心组件的实例,而不会影响其他默认配置。@Configuration public class Testconfig implements WebMvcRegistrations { @Override public RequestMappingHandlerMapping getRequestMappingHandlerMapping(){ //重写自己的逻辑 return null; } @Override public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter(){ //重写自己的逻辑 return null; } @Override public ExceptionHandlerExceptionResolver getExceptionHandlerExceptionResolver() { //重写自己的逻辑 return null; } }
完全自定义mvc
需要注意的是,完全自定义Spring MVC可能需要更多的工作和理解框架的内部机制。在大多数情况下,使用Spring MVC的默认配置和特性已经足够满足绝大多数Web应用程序的需要。只有在特殊情况下才需要进行完全自定义。
用于启用Spring MVC的完整功能,但是使用它可能会覆盖Spring Boot的自动配置,因此请谨慎使用。如果您只需要部分自定义配置,通常不需要使用@EnableWebMvc
注解,而是仅实现WebMvcConfigurer
接口即可。
@Configuration
@EnableWebMvc//这个注解加上的话,会将springBoot的默认配置覆盖掉,全部使用你定义的规则
public class StaticConfig {
@Bean
//将WebMvcConfigurer放在容器中
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
WebMvcConfigurer.super.addResourceHandlers(registry);
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/a/")
//缓存最大时间 单位秒
.setCacheControl(CacheControl.maxAge(7200, TimeUnit.SECONDS));
}
};
}
}
静态资源
静态资源映射
- 静态资源映射规则在 WebMvcAutoConfiguration 中进行了定义:
/webjars/**
的所有路径 资源都在classpath:/META-INF/resources/webjars/
/**
的所有路径 资源都在classpath:/META-INF/resources/
、classpath:/resources/
、classpath:/static/
、classpath:/public/
静态资源缓存
- 所有静态资源都定义了缓存规则。【浏览器访问过一次,就会缓存一段时间】,但此功能参数无默认值
-
- period: 缓存间隔。 默认 0S;
- cacheControl:缓存控制。 默认无;
- useLastModified:是否使用lastModified头。 默认 false;
示例
-
#1、spring.web: # 1.配置国际化的区域信息 # 2.静态资源策略(开启、处理链、缓存) # 开启缓存映射 spring.web.resources.add-mappings=true # 设置缓存间隔时间 spring.web.resources.cache.period=3600 # 缓存详细合并项控制,会覆盖.period这一项 spring.web.resources.cache.cachecontrol.max-age=7200 # 使用use-last-modified 服务器在接收到请求的时候,会给客户端返回一个时间 # 这个时间就是静态资源最后一次修改的时间,如果客户端当前时间和这个返回的时间相同的话 # 就说明这个资源没有被修改,客户端直接从缓存中获取,否则,将在服务器中重新获取这个静态资源 # 不设置,默认为开启,true spring.web.resources.cache.use-last-modified=true
自定义静态资源规则
配置方式
# 自定义静态资源文件夹的位置
spring.web.resources.static-locations=classpath:/a/
# 自定义静态资源规则
# 自定义webjars访问路径
spring.mvc.webjars-path-pattern=/wjs/**
# 自定静态资源访问路径
spring.mvc.static-path-pattern=/static/**
配置了访问路径.static-path-pattern
以后,在浏览器请求时,请求路径就要写http://localhost:8080/static/hyp.jpg
在配置了资源文件夹位置static-locations
后 静态资源的存放位置就变了
代码方式
**注意:**只要将WebMvcConfigurer加入到容器中,配置的底层行为就会生效
加上@EnableMvc
注解就会禁用springBoot的默认配置
/**
* 使用代码方式配置静态资源规则,代替使用properties配置
* @author Han
* @data 2023/7/17
* @apiNode
*/
@Configuration
public class StaticConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//保留默认配置
WebMvcConfigurer.super.addResourceHandlers(registry);
//添加自己的配置【访问路径为/static/**】【修改静态资源所在包 为类路径下的a文件夹】
registry.addResourceHandler("/static/**")
//添加自己的配置【访问路径为/static/**】【修改静态资源所在包 为类路径下的a文件夹】
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/a/")
//缓存最大时间 单位秒
.setCacheControl(CacheControl.maxAge(7200, TimeUnit.SECONDS));
}
}
另一种方式,不继承WebMvcConfigurer类,将WebMvcConfigurer类作为一个Bean注册到容器中
@Configuration
@EnableWebMvc
public class StaticConfig {
@Bean
//将WebMvcConfigurer放在容器中
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
WebMvcConfigurer.super.addResourceHandlers(registry);
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/a/")
//缓存最大时间 单位秒
.setCacheControl(CacheControl.maxAge(7200, TimeUnit.SECONDS));
}
};
}
}
欢迎页
欢迎页规则在 WebMvcAutoConfiguration 中进行了定义:
- 在静态资源目录下找 index.html
- 没有就在 templates下找index模板页
Favicon(网站图标)
- 在静态资源目录下找
favicon.ico
- 将网站的图片放在静态资源目录下,并命名为
favicon.ico
,就可以改变网页的图标
路径匹配策略
- springBoot支持Ant风格的路径
- 新版springBoot默认支持pathPatternParser风格路径
@GetMapping("/testPath/a*/b?/{pp}/**")
public String testPath(@PathVariable("pp") String pp, HttpServletRequest request){
var requestURL = request.getRequestURL();
System.out.println(requestURL);
return requestURL.toString()+"参数pp="+pp;
}
- 但是如果将
**
放在路径的中间位置,@GetMapping("/testPath/**/a*/b?/{pp}/**")
@GetMapping("/testPath/**/a*/b?/{pp}/**")
public String testPath(@PathVariable("pp") String pp, HttpServletRequest request){
var requestURL = request.getRequestURL();
System.out.println(requestURL);
return requestURL.toString()+"参数pp="+pp;
}
- 就会报错【检测到无效的映射模式】
-
解决方法就是将路径匹配规则在配置文件中修改为
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
-
这是因为新版springBoot默认支持的路径匹配风格是
spring.mvc.pathmatch.matching-strategy=path_pattern_parser
这个效率非常高但是不支持将**写在路径的中间位置
修改配置文件后:
内容协商
一套系统支持返回多种数据类型
分为:
- 基于请求头的内容协商
- 基于请求参数的内容协商
控制器方法
/**
* 测试内容协商
* 1、返回结果为json
* 这里遇到了问题,网页无法显示json数据
* 是因为当时没有给pojo类中提供get方法,提供以后正常了
* @return
*/
@GetMapping("/person")
public Person person(){
return new Person("小韩", 21);
}
制定返回json类型数据
因为springBoot化境下默认导入了json类型的内容协商,所以返回一个对象在浏览器默认显示的是Json
制定返回xml类型数据
-
导入支持转换成xml格式的依赖
<dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency>
-
给要返回的数据的类上标注
@
注解@JacksonXmlRootElement//可以写出为xml文档 public class Person { private String name; private Integer age; public Person(String name, Integer age) { this.name = name; this.age = age; } …………
-
配置配置文件
注意:基于请求头的内容协商是默认开启的,但是基于请求参数的是需要手动开启的
# 开启基于请求参数的内容协商 spring.mvc.contentnegotiation.favor-parameter=true # 修改请求参数指定内容协商时的使用的参数名 默认为format spring.mvc.contentnegotiation.parameter-name=type
制定返回自定义类型数据
新增其他内容协商
1、增加媒体类型支持的依赖
2、编写properties媒体类型配置
3、编写自己的MessageConverter
4、编写HttpMessageConverter。也就是在配置类中配置MessageConverter,将编写的MessageConverter添加到底层
系统提供的MessageConverter
八个:
-
ByteArrayHttpMessageConverter
: 支持字节数据读写StringHttpMessageConverter
: 支持字符串读写ResourceHttpMessageConverter
:支持资源读写ResourceRegionHttpMessageConverter
: 支持分区资源写出AllEncompassingFormHttpMessageConverter
:支持表单xml/json读写MappingJackson2HttpMessageConverter
: 支持请求响应体Json读写
-
系统提供默认的MessageConverter 功能有限,仅用于json或者普通返回数据。额外增加新的内容协商功能,必须增加新的
HttpMessageConverter
返回yaml类型数据
1、添加依赖
添加支持返回yaml格式的依赖
<!--增加处理yaml的内容协商依赖-->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
</dependency>
2、编写配置
# 增加yaml类型 spring.mvc.contentnegotiation.media-types.yaml=text/yaml
3、编写组件 自己的MessageConverter
作用是将对象转换成yaml格式写出【这一步可以将对象自己定义返回类型】
/**
* 编写组件
* 将对象可以返回为yaml格式
* @author Han
* @data 2023/7/22
* @apiNode
*/
//编写自己的类,继承AbstractHttpMessageConverter类
public class MyYamlHttpConverters extends AbstractHttpMessageConverter<Object> {
private ObjectMapper objectMapper;
public MyYamlHttpConverters() {
//定义媒体类型,这里的类型要和配置文件中一致
super(new MediaType("text", "yaml", Charset.forName("utf-8")));
YAMLFactory disableYaml = new YAMLFactory().disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER);
//转xml或者Yaml都是ObjectMapper来起作用的
this.objectMapper = new ObjectMapper(disableYaml);
}
@Override
protected boolean supports(Class<?> clazz) {
//这里可以添加逻辑判断
// 判断传入的否是一个对象
//在这里先不判断
return true;
}
@Override
protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return null;
}
@Override//将对象写出去
protected void writeInternal(Object methodReturnValue, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
//获取对应的输出流
OutputStream body = outputMessage.getBody();
try {
this.objectMapper.writeValue(body, methodReturnValue);
} finally {
body.close();
}
}
}
4、在配置类中配置,增加HttpMessageConverter
在配置类中添加组件
@Configuration
public class StaticConfig {
@Bean
//将WebMvcConfigurer放在容器中
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
WebMvcConfigurer.super.addResourceHandlers(registry);
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/a/")
//缓存最大时间 单位秒
.setCacheControl(CacheControl.maxAge(7200, TimeUnit.SECONDS));
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MyYamlHttpConverters());
}
};
}
}
效果
模板引擎
Thymeleaf整合
引入thymeleaf依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
自动配置原理
- 开启了
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration
自动配置 - 属性绑定在
ThymeleafProperties
中,对应配置文件spring.thymeleaf
内容 - 所有的模板页面默认在
classpath:/templates
文件夹下 - 默认效果
-
- 所有的模板页面在
classpath:/templates/
下面找 - 找后缀名为
.html
的页面
- 所有的模板页面在
- 这部分看语雀文档
拦截器
以用户登录为例子
使用步骤
- 编写一个拦截器实现 HandlerInterceptor 接口
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
/**
* 目标方法执行之前
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
log.info("preHandle拦截的请求路径是{}", requestURI);
//登录检查逻辑
HttpSession session = request.getSession();
Object loginUser = session.getAttribute("loginUser");
if (loginUser != null) {
//放行
return true;
}
//拦截住。未登录。跳转到登录页
request.setAttribute("msg", "请先登录");
// re.sendRedirect("/");
request.getRequestDispatcher("/").forward(request, response);
return false;
}
/**
* 目标方法执行完成以后
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse
response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandle执行{}", modelAndView);
}
/**
* 页面渲染以后
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse
response, Object handler, Exception ex) throws Exception {
log.info("afterCompletion执行异常{}", ex);
}
}
2.拦截器注册到容器中(实现 WebMvcConfigurer 的 addInterceptors() )
3.指定拦截规则(注意,如果是拦截所有,静态资源也会被拦截】
@Configuration
public class AdminWebConfig implements WebMvcConfigurer{
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())//拦截器注册到容器中
.addPathPatterns("/**") //所有请求都被拦截包括静态资源
//放行的请求
.excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**", "/js/**","/aa/**");
}
国际化
在springBoot核心配置文件中进行配置时使用的是 spring.messages
编写国际化配置文件
默认:
配置文件名:message.properties
无区域代码
login=login
sign_in=sign_in
英文版:使用区域代码_en_US
配置文件名:message_en_US.properties
_en_US前的名称随意取
login=login
sign_in=sign_in
中文版:使用区域代码_zh_CN
配置文件名:message_zh_CN.properties
_zh_CN前的名称随意取
login=登录
sign_in=注册
使用thymeleaf语法进行国际化内容显示
使用#{}
<button style="color: aqua" th:text="#{login}"/>
<button style="color: rosybrown" th:text="#{sign_in}"/>
错误处理
[原理][https://www.yuque.com/leifengyang/springboot3/wp5l9qbu1k64frz1#mMAt4]可查看语雀文档的错误处理章节
实战
1.前后端分离项目
前后端分离项目可使用@ControllerAdvice
+@ExceptionHandler
进行统一异常处理
2.服务端页面渲染【前后端不分离】
HTTP状态错误
给classpath:/templetes/error
目录下放精确码html
页面【404.html/500.html……】
给classpath:/templetes/error
目录下放模糊码html
页面【4xx.html/5xx.html……】
发生业务错误
核心业务**,每一种错误,都应该代码控制,跳转到自己定制的错误页。
通用业务**,classpath:/templates/error.html
页面,显示错误信息
注意: springBoot自动将错误信息封装到Modal中,如果要打印错误信息只需要在页面中直接使用${}
获取
前后端不分离方式
最后出现什么错误就会到对应的错误页面去显示错误信息
400.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
请求参数错误
<br>
错误信息:[[${message}]]
</body>
</html>
控制器方法
@Controller
@Slf4j
public class HtmlController {
@GetMapping("/hello")
public String hello(@RequestParam("name") String name , Model model){
//log.info(name);
//使用model向前端页面展示数据
model.addAttribute("name", name);
return "hello";
}
}
前后端分离方式
只需要集中处理错误,页面由前端负责
返回json数据,在页面的错误信息中显示json数据
/**
* @ControllerAdvice 这个注解表明这个类集中处理所有controller发生的错误
* @author Han
* @data 2023/7/26
* @apiNode
*/
@RestControllerAdvice
public class AllExceptionHandler {
@ExceptionHandler(Exception.class)//传入发生异常的类型
@ResponseBody
/**
* 写错误信息处理方法 可返回对象
* 发生什么错误返回什么对象
*/
public String handlerException(Exception e) {
return "错误!! 原因:"+e.getMessage();
}
}
数据访问
ssm整合
- 首先导入要使用的
starter
<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>3.0.2</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</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>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>3.0.2</version>
<scope>test</scope>
</dependency>
</dependencies>
- 配置基础的连接数据源
# 配置数据源
# 驱动
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# url
spring.datasource.url=jdbc:mysql://localhost:3306/p_springBoot
# 数据库用户名及密码
spring.datasource.username=root
spring.datasource.password=hyp
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
- 以查询User对象为例
- 首先要创建数据库表映射对象User
@Data
public class User {
private Long id;
private String loginName;
private String nickName;
private String passwd;
}
编写相关Mapper接口和Mapper.xml映射文件
@Mapper//标记@Mapper注解表明这个接口是一个Mapper
public interface UserMapper {
User getUserByID(@Param("id") Long id);
}
编写SQL映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hyp.ssm.mapper.UserMapper">
<select id="getUserByID" resultType="user">
select * from t_user where id = #{id}
</select>
</mapper>
编写controller控制器方法
@RestController
@Slf4j
public class UserController {
// 将Mapper接口注入
@Autowired
UserMapper userMapper;
@GetMapping("/getUserById/{id}")
public User getUserById(@PathVariable("id") Long id) {
log.info("查询id为【{}】的用户",id);
return userMapper.getUserByID(id);
}
}
mybatis相关配置
# 配置mybatis
# 别名机制 在Sql映射文件UserMapper.xml文件中的resultType属性使用别名
mybatis.type-aliases-package=com.hyp.ssm.bean
# 告诉mybatis SQL映射文件在哪里
mybatis.mapper-locations=classpath:/mapper/*.xml
# 开启驼峰命名映射
mybatis.configuration.map-underscore-to-camel-case=true
测试:
注意:
在标注Mapper接口时有两种方法
- 在Mapper接口上添加
@Mapper
注解,加了这个注解后,明示了这个接口是一个Mapper接口,并且还将它作为一个bean交给ioc容器进行管理 - 还可以在主启动类上添加注解
@MapperScan(basePackages = "填写Mapper接口所在包")
/**
*@MapperScan 这个注解是用来扫描Mapper接口的
* 如果在Mapper接口上添加了@Mapper注解,就不用再加这个注解了
*/
@SpringBootApplication
@MapperScan(basePackages = "com.hyp.ssm.mapper")
public class SsmApplication {
public static void main(String[] args) {
SpringApplication.run(SsmApplication.class, args);
}
}
- 他们只要存在一个就可以,但也可以同时存在
springBoot基础特性
profile环境隔离
- Spring Profiles 提供一种隔离配置的方式,使其仅在特定环境生效;
- 任何@Component, @Configuration 或 @ConfigurationProperties 可以使用 @Profile 标记,来指定何时被加载。【容器中的组件都可以被
@Profile
标记】
@Profile({"test"})
@Component
public class Cat {
private Integer id;
private String name;
}
@Profile({"prod"})
@Component
public class Person {
private String name;
private Integer age;
private User user;
private Cat cat;
private Map<String, Dog> dogMap;
private List<Dog> dogs;
在配置文件中选择激活的环境进行激活
- 这时候,ioc容器中只有Person组件,没有Cat组件
# 激活prod环境
spring.profiles.active=prod
环境包含
-
生效的环境 = 激活的环境/默认环境 + 包含的环境
-
项目里面这么用
-
- 基础的配置
mybatis
、log
、xxx
:写到包含环境中 - 需要动态切换变化的
db
、redis
:写到激活的环境中
- 基础的配置
# 包含环境
# 不管是否激活了Dev 和 test 环境 总是要生效的环境
spring.profiles.include=prod,test
环境分组
# 环境分组
spring.profiles.group.must=dev,test
spring.profiles.group.A = prod,test
# 可以选择一个组来激活
spring.profiles.active=must
组件环境隔离
- 一:组件环境隔离
- 1、标识环境
- 1、给定区分几个环境 :
dev(开发) text(测试) prod(生产)
- 2、指定每个组件在那个环境下生效
【在组件类上添加@profile注解】
- 通过:
@Profile({"prod"})
……
- 通过:
- 1、给定区分几个环境 :
- 1、标识环境
- 如果组件没有标注
@Profile
那么就是所有环境下都能使用这个组件 - 如果标注了
@Profile({"default"})
在没有激活指定环境时,默认只有这一个组件
-
3、只有激活这些指定的环境,这些组件才能生效
-
2、激活环境
- 1、通过配置文件激活
spring.profiles.active=prod
- 1、通过配置文件激活
配置文件环境隔离
二:配置文件环境隔离
- 1、application.properties 主配置文件,在任何环境下都生效
-
2、其他profile环境下配置文件命名规范:
-
`application-dev.properties`
-
application-test.properties
…… …… ……
-
格式:`application-{profile标识}.properties`
-
-
3、激活指定环境即可 会自动选择对应环境的配置文件进行配置
-
4、**项目的所有生效配置=激活的配置文件的所有项+主配置文件和激活文件`不冲突`的所有项** * 如果发生了配置冲突 以激活的配置文件的配置为准 * 如: * 在`application.properties`中配置的端口号为`8080` * 在`application-dev.properties`中配置的端口号为`9090` * 我们激活了`dev环境`,那么会以dev环境中的端口号`9090为准`
-
注意:要激活某个环境,只能在主配置文件中设置
外部化配置
-
application-{profile}.properties
可以作为指定环境的配置文件。 -
激活这个环境,配置就会生效。最终生效的所有配置是
-
application.properties
:主配置文件,任意时候都生效application-{profile}.properties
:指定环境配置文件,激活指定环境生效
profile优先级 > application
配置生效优先级
常用的有:
- 默认属性【SpringApplication.setDefaultProperties指定,在启动类中设置】
- 配置文件 【application.properties/yml等】
- 命令行参数
注意:
配置文件优先级如下**(返序)**
- jar 包内的
application.properties/yml
- jar 包内的
application-{profile}.properties/yml
- jar 包外的
application.properties/yml
- jar 包外的
application-{profile}.properties/yml
导入外配置文件
# 导入一个配置文件,这配置文件中配置了server.port=9999
spring.config.import=classpath:aaa.properties
# 然后这里也设置了端口,但是因为是导入的配置,
# 所以优先级大于本身所有的,则最终端口是aaa.properties所定义的
server.port=8080
属性占位符
# 属性占位符,可以获取配置文件中的配置项的值
# 在controller中使用@value注解拿出来
portValue = 端口号是:${server.port}
@RestController
public class HelloController {
@Value("${portValue}")
String portValue;
@GetMapping("/getPort")
public String getPortValue(){
return portValue;
}
}
最终页面上显示:端口号是9999
单元测试
springBoot核心原理
事件和监听器
生命周期监听
package com.example.texing.CoreTeXing.listener;
import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import java.time.Duration;
/**
*
*
* Listener先要从 META-INF/spring.factories 读到
*
* 1、引导: 利用 BootstrapContext 引导整个项目启动
* starting: 应用开始,SpringApplication的run方法一调用,只要有了 BootstrapContext 就执行
* environmentPrepared: 环境准备好(把启动参数等绑定到环境变量中),但是ioc还没有创建;【调一次】
* 2、启动:
* contextPrepared: ioc容器创建并准备好,但是sources(主配置类)没加载。并关闭引导上下文;组件都没创建 【调一次】
* contextLoaded: ioc容器加载。主配置类加载进去了。但是ioc容器还没刷新(我们的bean没创建)。
* =======截止以前,ioc容器里面还没造bean呢=======
* started: ioc容器刷新了(所有bean造好了),但是 runner 没调用。
* ready: ioc容器刷新了(所有bean造好了),所有 runner 调用完了。
* 3、运行
* 以前步骤都正确执行,代表容器running。
* 4、运行失败
* failed 以上任意一个步骤如果发生了异常,就会启动失败
*
*/
public class MyListener implements SpringApplicationRunListener {
@Override
public void starting(ConfigurableBootstrapContext bootstrapContext) {
System.out.println("===starting=== 正在启动 ");
}
@Override
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
System.out.println("===environmentPrepared=== 环境准备完成 ");
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("===contextPrepared=== ioc容器准备完成 ");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("===contextLoaded=== ioc容器加载完成 ");
}
@Override
public void started(ConfigurableApplicationContext context, Duration timeTaken) {
System.out.println("===started=== 启动完成 ");
}
@Override
public void ready(ConfigurableApplicationContext context, Duration timeTaken) {
System.out.println("===ready=== 准备就绪 ");
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("===failed=== 应用启动失败 ");
}
}