SpringBoot
底层注解
@SpringBootApplication
用于主程序类
相当于三个注解@SpringBootConfiguration,@EnableAutoConfiguration,@CompontentScan("com.boot")
@Configuration
用于构造配置类
/**
* 1.配置类中使用@Bean标注在方法上给容器注册组件,默认为单实例
* 2.配置类本身也是组件
*/
@Configuration //告诉SpringBoot这是一个配置类,即配置文件
public class MyConfig{
/* 给容器中添加组件
* 方法名作为组件的id
* 返回类型为组件类型
* 返回值为组件在容器中的实例
*/
// @Component,@Service,@Controller,@Reposity也可以使用
@Bean
public User user01(){
return new User();
}
}
proxyBeanMethods
代理bean的方法,默认为true
- Full模式,如果
@Configuration(proxyBeanMethods=true),则由代理对象调用方法,保持组件单实例,可以保持组件之间的依赖关系 - Lite模式,
@Configuration(proxyBeanMethods=false),则每次调用方法都会new一个新的对象,而不是从容器中取出,不能保持组件之间的依赖关系
@ComponentScan
组件扫描,默认扫描该类所在包下所有配置类,也可指定要扫描的包使用value属性
@Import
写在组件类上,给容器中导入组件,组件默认名为全类名
// 括号内的值为数组
@Import({User.class})
@Conditional
写在创建对象的注解或类上,条件装配:满足Conditional指定的条件,则进行组件注入
@ImportResource
写在配置类上,导入spring的配置文件
@ImportResource("classpath:beans.xml")
@ConfigurationProperties
写在类上,用于配置属性
// 将需要注入的属性写在application.properties中
mycar.price = 100000
mycar.brand = byd
//1.直接在组件类中注入属性
@Component
@ConfigurationProperties(prefix="mycar")
public class Car{
private String brand;
private Integer price;
}
//2.在配置类中注入属性
//不需要写@Component
@ConfigurationProperties(prefix="mycar")
public class Car{
private String brand;
private Integer price;
}
@EnableConfigurationProperties(Car.class)
// 写在配置类上,开启Car配置绑定功能,并注册这个组件
自动配置
- SpringBoot先加载所有的自动配置类(xxxAutoConfiguration)
- 配置类按照条件生效,默认绑定配置文件指定的值(xxxProperties)
- 用户配置的内容,以用户优先(ConditionalOnMissingBean)
自动包规则原理
@SpringBootConfiguration:代表当前是一个配置类
@EnableAutoConfiguration:
- @AutoConfigurationPackage:利用Register给容器中导入一系列组件,将MainApplication所在包下的所有组件导入
- @Import(AutoConfigurationImportSelector.class):利用getAutoConfigurationEntry(annotationMetadata)给容器中批量导入组件,调用List configurations = getCandidateConfigurations(annotationMetadata,attributes)获取所有需要导入到容器中的自动配置类
@ComponentScan:指定扫描Spring注解
按需开启自动配置项
按照条件装配规则(@Conditional),最终按需配置
定制化配置
- 使用@Bean替换底层组件
- 查看组件获取的配置文件的值,修改
application.properties配置文件
开发工具
Lombok
引入依赖
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>安装lombok插件
使用注解 注解添加在类上
@Data:
为属性生成set和get方法
@ToString:
生成toString方法
@AllArgsConstructor:
生成一个全参构造器
@NoArgsConstructor:
生成一个无参构造器
@Slf4j:
注入一个log属性(日志)
Dev-tools
引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>快速重启 ctrl + F9
Spring Initializer
项目初始化工具
yaml
基本语法
- key: value,有空格
- 大小写敏感
- 使用缩进表示层级关系
- 缩进不适用tab,只用空格
- 缩进的空格数不重要,对齐即可
- ‘#‘表示注释
- 字符串无需引号
- 双引号不转义,单引号转义
数据类型
字面量:单个的,不可再分的值(date,boolean,string,number,null)
- k:v
对象:键值对的集合(map,hash,set,object)
行内写法
k: {k1:v1,k2:v2}
分行写法
k: k1: v1 k2: v2
数组:一组按次序排列的值(array,list,queue)
行内写法
k: [v1,v2]
分行写法
k:
\- v1 \- v2
配置提示
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
关闭对配置处理器的打包(新版自带)
配置提示利于开发,但对项目没有影响,应该避免打包冗余
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configurationprocessor</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
<configuration>
<excludes>
<exclude>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configurationprocessor</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
Web开发
静态资源
静态资源目录
类路径下:/static or /public or /resources or /META-INF/resources
访问:当前项目根路径/静态资源名
静态资源访问前缀
“static-path-pattern” 用于定义触发服务器为静态资源提供服务的URL路径模式
spring:
mvc:
static-path-pattern: /res/**
#yaml格式
#访问:当前项目+static-path-pattern+静态资源名
改变默认静态资源路径
“static-locations” 用于指定存储静态资源的服务器文件系统上的目录
web:
resources:
static-locations: [classpath:/folder/]
welcome page
- 静态资源目录下的
index.html文件 - controller能处理/index
Favicon - 网站图标
将图标命名为favicon.ico,放在静态资源路径下
请求处理
请求映射
Rest风格
使用Http请求方式动词来表示对资源的操作
/user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户
核心Filter
HiddenHttpMethodFilter
用法
表单method=post,隐藏域_method=xxx
代码实现
@RestController
public class HelloController{
//@RequestMapping(value="/user",method="RequestMethod.Get")
@GetMapping("/user")
public String getUser(){
}
...
}
<form action="/user" method="get">
<input value="REST-GET t提交" type="submit"/>
</form>
<form action="/user" method="post">
<input value="REST-POST t提交" type="submit"/>
</form>
<form action="/user" method="post">
<!--隐式实现真正的请求方法-->
<input name="_method" type="hidden" value="DELETE">
<input value="REST-DELETE t提交" type="submit"/>
</form>
<form action="/user" method="post">
<input name="_method" type="hidden" value="PUT">
<input value="REST-PUT t提交" type="submit"/>
</form>
// 配置文件中开启页面表单的Rest功能
mvc:
hiddenmethod:
filter:
enabled: true
Rest原理
REST风格表单提交
- 表单提交中带上参数
_method - 请求被HiddenHttpMethodFilter拦截
- 请求正常并且是POST请求
- 获取
_mthod的值 - requestWrapper重写了getMethod方法,返回传入的值
- 过滤器放行Wrapper
- 表单提交中带上参数
Rest使用客户端工具
- 直接发送PUT,DELETE等请求,无需Filter
更改默认的_method
@Configuration(proxyBeanMethods = false)
public class WebConfig{
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();
methodFilter.setMethodParam("_m");
return methodFilter;
}
}
普通参数和参数注解
参数注解
@PathVaribale
路径变量
@GetMapping("/car/{id}/owner/{username}")
public Map<String,Object> getCar(@PathVarible("id") Interger id,
@PathVarible("username") String name
@PathVarible Map<String,String>)
{
Map<String,Object> map = new HashMap<>();
map.put("id",id);
map.put("name",name);
map.put("pv",pv);
return map;
}
@RequestHeader
获取请求头,可以用Map拿到所有信息
@RequestParam
获取请求参数,可以用Map拿到所有请求参数
@CookieValue
获取Cookie的值
@RequestBody
获取请求体(POST)
@RequestAttribute
获取request域属性
参数处理原理
- HandlerMapping中找到能处理请求的Handler(Controller.method())
- 为当前Handler找一个适配器(HandlerAdapter)
- 执行目标方法DispatcherServlet.doDispatch() => invokeHandlerMethod(req,resp,handlerMethod)
- 参数解析器,确定将要执行的目标方法的每一个参数的值
- 返回值处理器
Servlet API
WebRequest,ServletRequest,MultipartRequest,HttpSession,
javax.servlet.http.PushBuilder,principal,InputStream,Reader,HttpMethod,
Locale,TimeZone,Zoneld
复杂参数
Map,Model:放在req的请求域,request.setAttribute
RedirectAttributes:重定向携带数据
ServletResponse:response
自定义对象参数
可以自动类型转换与格式化,可以级联封装
/**
* 姓名:<input name="userName"/><br>
* 生日:<input name="birth"/><br>
* 年龄:<input name="age"/><br>
* 宠物姓名:<input name="pet.name"/><br>
* 宠物年龄:<input name="pet.age"/><br>
*/
@Data
public class Person{
private String userName;
private Integer age;
private Date birth;
private Pet pet;
}
@Data
public class Pet{
private String name;
private Integer age;
}
@PostMapping("/saveuser")
public Person saveUser(Person person){
return person;
}
自定义Convert
宠物: <input name="pet" value="阿猫,3">
@Bean
public WebConfiguration webMvcConfigurer(){
return new WebMvcConfigurer(){
@Override
public void addFormatters(FormatterRegistry registry){
registry.addConvert(new Convert<String,Pet>){
@Override
public Pet convert(String source){
//source="阿猫,3"
if(!StringUtils.isEmpty()){
Pet pet = new Pet();
String[] split = source.split(",");
pet.setName(split[0]);
pet.setAge(Interger.parseInt(split[1]));
return pet;
}
return null;
}
}
}
}
}
响应处理
- @ResponseBody 响应数据出去,调用RequestResponseBodyMethodProcessor处理
- Processor通过MessageConverter处理方法返回值
- 所有MessageConverter合起来可以支持各种媒体类型数据的读写
- 内容协商找到最终的messageConverter
响应数据
JSON
- web场景自动引入json依赖
- 在方法上标注
@ResponseBody - 给前端自动返回json数据
HttpMessageConverter
canRead(Class,MediaType):boolean canWrite(Class,MediaType):boolean
看是否支持将此Class类型的对象,转为MediaType类型的数据
内容协商
根据客户端接收能力不同,返回不同媒体类型数据
- 引入xml依赖
- 只需要改变请求头中Accept字段
- 开启浏览器基于请求参数的内容协商功能
- 配置文件:spring.contentnegotiation.favor-parameter:true
- 请求参数:format=json
内容协商原理
判断当前响应头中是否已经有确定的媒体类型
获取客户端支持接受的内容类型(客户端Accept请求字段)
- contentNegotiationManager 内容协商管理器,默认使用基于请求头的策略
- HeaderContentNegotiationStrategy 确定客户端可以接收的内容类型
遍历循环所有的MessageConverter,看谁支持操作返回的对象
找到支持操作对象的converter,统计converter支持的媒体类型
对客户端需求和服务端能力进行最佳匹配媒体类型
用支持将对象转为最佳匹配媒体类型的converter,调用它进行转换
自定义MessageConverter
@Configuration(proxyBeanMethods = false)
public class WebConfig{
// WebMvcConfigurer定制化SpringMVC的功能
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer(){
@Override
public void extendMessageConverters(List<HttpMessageConverter<?> converters>){
converters.add(new CustomMessageConverter());
}
}
}
}
// 自定义Converter
public class CustomMessageConverter implements HttpMessageConverter<Person>{
@Override
public boolean canRead(Class<?> clazz, MediaType mediaType){
return false;
}
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType){
return clazz.isAssignableFrom(Person.class);
}
//
@Override
public List<MediaType> getSupportedMediaTypes(){
return MediaType.parseMediaTypes("application/x-custom");
}
@Override
public Person read(Class<? extends Person> clazz,HttpInputMessage inputMessage){
return null;
}
@Override
public void write(Person person,MediaType contentType, HttpOutputMessage outputMessage){
// 自定义协议数据的写出
String data = person.getUserName()+";"+person.getAge()+";"+person.getBirth();
OutputStream body = outputMessage.getBody();
body.write(data.getBytes());
}
}
视图解析与模板引擎
springboot默认不支持JSP,需要引入第三方模板引擎技术实现页面渲染
Thymeleaf
<!--引入依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
基本语法
<html xmlns:th="http://www.thymeleaf.org">
表达式
- 变量取值:${…}获取请求域,session域对象等
- 选择变量:*{…}获取上下文对象值
- 消息:#{…}获取国际化等值
- 链接:@{…}生成链接
- 片段表达式:~{…} jsp:include作用,引入公共页面
字面量
- 文本值
- 数字
- 布尔值
- 空值null
- 变量
文本操作
- 字符串拼接:+
- 变量替换:The name is ${name}
数学运算
布尔运算
- and or
- ! not
比较运算
条件运算
- If-then:(if)?(then)
- If-then-else:(if)?(then):(else)
- Default:(value):?:(defaultvalue)
设置属性值 - th:attr
设置单个值
<form action="subscribe.html" th:attr="action=@{/subscribe}">
<fieldset>
<input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}">
</fieldset>
</form>
设置多个值
<img src="../images/gtv.png" th:attr="src=@{/images/gtv.png},title=#{logo}">
替代写法
<input type="submit" value="Subscribe!" th:value="#{subscribe.submit}">
<form action="subscribe.html" th:action="@{/subscribe}">
迭代
<tr th:each="prod : ${prods}">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}?#{true}:#{false}">Onions</td>
</tr>
条件运算
<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:if="${not #lists.isEmpty(prod.comments)}">view</a>
拦截器
- 编写拦截器实现HandlerInterceptor接口
// 配置拦截器
public class LoginInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest req,HttpServletResponse resp,Object handler){
//登录检查逻辑
HttpSession session = req.getSession();
Object loginUser = session.getAttribute("loginUser");
if(loginUser!=null){
//放行
return true;
}else{
resp.sendRedirect("/");
return false;
}
}
@Override
public void postHandle(HttpServletRequest req,HttpServletResponse resp,Object handler){
}
@Override
public void afterCompletion(HttpServletRequest req,HttpServletResponse resp,Object handler){
}
}
- 注册拦截器到容器中,WebMvcConfigurer.addInterceptors
@Configuration
public class AdminWebConfig implements WebMvcConfigurer{
@Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(new LoginInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/login","/css/**","/js/**");
// 可以为静态资源添加前置路径来进行拦截
}
}
文件上传
- 页面表单
<form role="form" th:action="@{/upload}" method="post" enctype="multipart/form-data">
<div>
<label for="exampleInputEmail">Email</label>
<input type="email" name="email" id="exampleInputEmail">
</div>
<div>
<label for="exampleInputPassword">name</label>
<input type="text" name="username" id="exampleInputPassword">
</div>
<div>
<label for="exampleInputFile">头像</label>
<input type="file" name="headerImg" id="exampleInputFile">
</div>
<div>
<label for="exampleInputFile">生活照</label>
<input type="file" name="photos" mutiple>
</div>
</form>
- 文件上传代码
@Controller
public class FormController{
@GetMapping("/form/form_layout")
public String form/form_layout(){
return "form/form_layout";
}
@PostMapping("/upload")
public String upload(@RequestParam("email") String email,
@RequestParam("username") String userName,
@RequestPart("headerImg") MultipartFile headerImg,
@RequestPart("photos") MultipartFile[] photos){
if(!headerImg.isEmpty()){
//保存到文件服务器
String filename = headerImg.getOriginFilename();
headerImg.transferto(new File("E:\\cache\\" + filename));
}
}
}
- 文件上传原理 文件上传自动配置原理 - MultipartAutoConfiguration -> MultipartProperties
- 自动配置了 StandardServletMultipartResolver(文件上传解析器)
- 原理步骤
- 使用文件上传解析器判断(isMultipart)并封装(resolveMultipart返回MultipartHttpServletRequest)文件上传
- 参数解析器来解析请求的文件内容封装成MultipartFile
- 将request中文件信息封装成一个Map
错误处理
默认规则
- 默认情况下,SpringBoot提供
/error处理所有错误的映射 - 对于机器客户端,生成JSON响应,包含错误,Http状态和异常消息的详细信息
- 对于浏览器客户端,响应一个"whitelabel"错误视图,以HTML形式呈现相同数据
- 对其进行自定义,添加View解析为error
- 完全替换默认行为,可以实现ErrorController并注册该类型的Bean定义,或添加ErrorAttributes类型的组件以使用现有机制但替换内容
- error/下的4xx,5xx页面会被自动解析
定制错误处理逻辑
自定义错误页
- error/404.html
- error/5xx.html
@ControllerAdvice + @ExceptionHandler 处理全局异常
@Slf4j @ControllerAdvice public class GlobalExceptionHandler{ @ExceptionHandler({ArithmeticException.class,NullPointerException.class}) public String handleArithException(Exception e){ log.error("异常是:{}",e) return "login"; // 视图地址 } }@ResponseStatus + 自定义异常
// 自定义异常 @ResponseStatus(value=HttpStatus.FORBIDDEN,reson = "用户数量太多") public class UserTooManyException extends RuntimeException{ public UserTooManyException(){ } public UserTooManyException(String message){ super(message); } }ErrorViewResolver 实现自定义处理异常
自定义实现 HandlerExceptionResolver 处理异常
@Order(value=0) //优先级,数字越小,优先级越高 @Component public class CustomerHandlerExceptionResolver implements HandlerExceptionResolver{ @Override public ModelAndView resolveException(HttpServletRequest req,HttpServletResponse resp,Object handler,Exception ex){ try{ resp.sendError(404,"自定义错误"); } catch (IOException e){ e.printStackTrace; } return new ModelAndView(); } }
异常处理自动配置原理
- ErrorMvcAutoConfiguration
- 类型:DefaultErrorAttributes -> id:errorAttributes
- public class DefaultErrorAttributes implements ErrorAttributes,HandlerExceptionResolver
- DefaultErrorAttributes:定义错误页面包含哪些数据
- 类型:BasicErrorController -> id:basicErrorController(json+whitelabel 适配响应)
- 处理默认/error路径的请求;页面响应new ModelAndView(“error”,model);
- 容器中有组件View id:error(响应默认错误页)
- 容器中有组件BeanNameViewResolver(视图解析器),按照返回的视图名作为组件的id去容器中寻找View对象
- 类型:DefaultErrorViewResolver -> id:conventionErrorViewResolver
- 如果发生错误,会以HTTP的状态码作为视图页地址,找到真正的页面
- error/404、5xx.html
- 类型:DefaultErrorAttributes -> id:errorAttributes
原生组件注入
- 使用Servlet API
@ServletComponentScan(basePackages="com.boot")
//指定原生Servlet组件都放在哪里
// 效果直接响应,不经过Spirng的拦截器
@WebServlet(urlPatterns="/my")
@WebFilter(urlPatterns={"/css/*","/images/*"})
@WebListener
- RegistrationBean
ServletRegistrationBean,FilterRegistrationBean,ServletListenerRegistrationBean
@Configuration
public class MyRegistrationConfig{
@Bean
public ServletRegistrationBean myServlet(){
MyServlet myServlet = new MyServlet();
return new ServletRegistrationBean(myServlet,"/my","/my1");
// 映射的路径
}
}
定制化原理
原理分析规则
场景stater -> xxxAutoConfiguration -> 导入xxx组件 -> 绑定xxxProperties -> 绑定配置文件项
定制化常见方式
- 修改配置文件
- xxxCustomizer
- 编写自定义配置类 xxConfiguration + @Bean 替换/增加容器中默认组件
- *web应用实现WebMvcConfigurer定制化web功能
- @EnableWebMvc + WebMvcConfigurer 全面接管SpirngMvc,所有规则都需要配置
数据访问
SQL
- 数据源的自动配置 - HikariDataSource
<!-- 导入JDBC场景 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<!-- 导入Mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
#修改配置项
spring:
datasource:
url:
username:
password:
driver-class-name:
- 使用Druid数据源 官方github地址:https://github.com/alibaba/druid
<!--引入依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
// 也可通过配置文件设置
@Configuration
public class MyDataSourceConfig{
@ConfigurationProperties("spring.datasource")
@Bean
public DaraSource dataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
return druidDataSource;
}
@Bean
public ServletRegistrationBean statViewServlet(){
StatViewServlet statViewServlet = new StatViewServlet();
ServletRegistrationBean<StatViewServlet> registrationBean = new ServletRegistrationBean<StatViewServlet>(statViewServlet,"/durid/*");
return registrationBean;
}
}
- 整合MyBaits
单元测试
JUnit5常用注解
@Test 表示方法是测试方法
@DisplayName 为测试类/方法设置展示名称
@BeforeEach 在每个单元测试之前执行
@AfterEach 在每个单元测试之后执行
@BeforeAll 在所有单元测试之前执行,静态方法
@AfterAll 在所有单元测试之后执行,静态方法
@Disabled 禁用测试方法
@Timeout 设置限定时间,value和unit两个属性
@RepeatedTest 设置测试重复次数
@SpringbootTest 连接spring的容器功能
断言
断言失败,后面的代码都不执行
- assertEquals(expected,actual,String message):判断相等
- assertNotEquals(expected,actual,String message):判断不相等
- assertSame():判断是否指向同一个对象
- assertNotSame():判断是否指向不同对象
- assertTrue():判断布尔值是否为True
- assertFalse():判断布尔值是否为False
- assertNull():判断给定的对象引用是否为null
- assertNotNull():判断给定的对象引用是否不为null
- assertThrows():断定业务逻辑一定出现异常
- fail():快速失败
前置条件 - assumptions
用法同断言
不满足的断言使测试方法失败,不满足的前置条件只会使测试方法终止
嵌套测试
参数化测试
- @ParameterizedTest:声明该方法是一个参数化测试
- @ValueSource(ints = {1,2,3}):为参数化测试传值
- @NullSource:提供一个null的入参
- @EnumSource:提供一个枚举入参
- @MethodSource:表示读取指定方法的返回值为参数化测试入参(方法需要返回一个流)
指标监控
Spring Boot Actuator
引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-stater-actuator</artifactId> </dependency>暴露所有监控信息为Http 命令台打开jconsole默认暴露所有Endpoints
management:
endpoints:
enabled-by-default:true #暴露所有端点信息
web:
exposure:
include:'*' #以web方式暴露
常用端点
- Health:监控状况
- Metrics:运行时指标
- Loggers:日志记录
开启禁用端点
managment.endpoint..enabled = true
定制Endpoint
定制info信息
编写配置文件
info: appName: boot-admin version: 2.0.1 mavenProjectName: @poject.atifactId@ #使用@@获取maven的pom文件值 mavenProjectVersion: @poject.version@编写infoContributor
@Compoent public class ExampleInfoContributor implements InfoContributor{ @Override public void contribute(Info.Builder,builder){ builder.withDetail("example",Collections.singletonMap("key","value")); } }
定制metrics
class MyService{
Counter counter;
public MyService(MeterRegistry meterRegistry){
counter = meterRegistry.counter("myservice.method.running.counter");
}
public void hello(){
counter.increment();
}
}
// 另一种方式
@Bean
MeterBinder queueSize(Queue queue){
return (registry) -> Gauge.builder("queueSize",queue::size).register(registry);
}
自定义Endpoint
@Component
@Endpoint(id = "container")
public class DockerEndpoint{
@ReadOperation
public Map getDockerInfo(){
return Collections.singletonMap("info","docker started..");
}
@WriteOperation
private void restartDocker(){
System.out.println("docker restarted..")
}
}
Spring Boot Admin Server - 可视化
引入依赖
<dependency> <groupId>de.codecentric</groupId> <actifactId>spring-boot-admin-starter-server</actifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <actifactId>spring-boot-starter-web</actifactId> </dependency>开启监控功能 在主控制类上添加注解
@EnableAdminServer注册客户端
<dependency> <groupId>de.codecentric</groupId> <actifactId>spring-boot-admin-starter-client</actifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <actifactId>spring-boot-starter-security</actifactId> </dependency>spring.boot.admin.client = http://localhost:8080 #以web方式暴露所有端点 management.endpoints.web.exposure.include=*
高级特性
Profile功能
实现多环境适配
application-profile
- 默认配置文件 application.yaml 任何时候都会加载
- 指定环境配置文件 application-{env}.yaml
- 激活指定环境
- 配置文件激活: spring.profiles.active=prod
- 命令行激活: java -jar xxx.jar –spring.profiles.active=prod
@Profile
让类或方法只在限定环境下生效
外部化配置
外部配置源
Java属性文件,yaml文件,环境变量,命令参数
配置文件查找位置
- classpath根路径
- classpath根路径下config目录
- jar包当前目录
- jar包当前目录的config目录
- /config子目录的直接子目录
自定义starter
starter启动原理
引入starter -> xxxAutoConfiguration -> 容器中放入组件 -> 绑定xxxProperties -> 配置项
配置META-INF
autoconfigure包中配置使用 META-INF/spring.factories 中EnableAutoConfiguration 的值,使项目启动加载指定的自动配置类
编写自动配置类
- @Configuration
- @Conditional
- @EnableConfigurationProperties
- @Bean
- …
自定义starter
hello-spring-boot-starter(启动器)
hello-spring-boot-starter-autoconfigure(自动配置包)
SpringBoot原理
SpringBoot启动过程
- 创建SpringApplication
- 保存信息
- ClassUtils - 判定当前应用的类型
- bootstrapper - 初始启动引导器:去spring.factories找Bootstrapper
- setInitializers - 找初始化器:去spring.factories找ApplicationContextInitializer
- setListeners - 找监听器:去spring.factories找ApplicationListener
- 运行SpringApplication
- stopWatch
- 记录应用启动时间
- createBootstrapContext - 创建引导上下文(Context环境)
- 获取到所有之前的 bootstrapper 轮流执行 intitialize() 来完成对引导启动器上下文的配置
- 让当前应用进入 headless 模式
- 获取所有 RunListener(运行监听器)
- getSpringFactoriesInstances 去spring.factories找SpirngApplicationRunListener
- 遍历 SpirngApplicationRunListener 调用starting方法
- 保存命令行参数 ApplicationArguments
- 准备环境 prepareEnvironment()
- 返回或创建基础环境信息对象 StandardServletEnvironment
- 配置环境信息对象
- 读取所有配置源的配置属性值
- 绑定环境信息
- 监听器调用environmentPrepared()
- 创建IOC容器 createApplicationContext()
- 根据项目类型(Servlet)创建容器
- 当前会创建 AnnotationConfigServletWebServerAplicationContext
- 准备ApplicationContext IOC容器的基本信息 prepareContext()
- 保存环境信息
- IOC容器的后置处理流程
- 应用初始化器 applyInitializer
- 遍历所有ApplicationContextInitializer 调用initialize()来对IOC容器初始化
- 遍历所有的listener调用contextPrepared
- 所有的监听器调用contextLoaded,
- 刷新IOC容器 refreshContext
- 创建容器中的所有组件(Spring注解)
- 容器刷新完成后 afterRefresh
- 所有监听器调用started
- 调用所有runners - callRunners
- 获取容器中的 ApplicationRunner
- 获取容器中的 CommandLineRunner
- 合并所有runner并且按照@Order排序
- 遍历runner调用run方法
- 如果出现异常
- 调用listener的failed方法
- 调用所有监听器的running方法
- running如果出现问题,继续通知failed
自定义事件监听组件
放在spring.factories
- ApplicationContextInitializer
- SpringApplicationRunListener
- SpringApplicationListener
放在容器中
- ApplicationRunner
- CommandLineRunner