SpringBoot知识快速复习
Spring知识快速复习
- 启动器
- 自动装配
- @Configuration
- @Import导入组件
- @Conditional条件装配
- @ImportResource导入Spring配置文件
- @ConfigurationProperties配置绑定
- Lombok简化开发
- dev-tools
- yaml
- 请求和响应处理
- 静态资源规则与定制化
- 请求处理-Rest映射
- 请求处理-常用参数注解使用
- 请求处理-Servlet API参数
- 请求处理-Model、Map
- 请求处理-自定义Converter
- 响应处理-ReturnValueHandler
- 响应处理-内容协商
- 响应处理-基于请求参数的内容协商
- 响应处理-自定义MessageConverter
- Web开发
- 视图解析--视图解析器与视图
- 拦截器-登录检查与静态资源放行
- 拦截器-拦截器的执行时机和原理
- 文件上传-单文件与多文件上传的使用
- 文件上传-文件上传参数解析器
- 错误处理-SpringBoot默认错误处理机制
- 原生组件注入-原生注解与Spring方式注入
- 定制化springmvc
- 原理分析套路
- 数据访问
- JDBC
- Druid
- MyBatis
- MyBatisPlus
- 添加分页插件
- Redis
- JUnit5
- 常用测试注解
- 断言机制
- 前置条件
- 嵌套测试
- 参数化测试
- 迁移指南
- 指标监控
- Actuator
- 常使用的端点及开启与禁用
- 常使用的端点
- Health Endpoint
- Metrics Endpoint
- 开启与禁用Endpoints
- 暴露Endpoints
- 定制Endpoint
- 定制 Health 信息
- 定制info信息
- 定制Metrics信息
- 定制Endpoint
- 自定义starter
- starter启动原理
- 自定义starter
- SpringBoot完整启动过程
启动器
spring-boot-starter-* : 官方提供的
*-spring-boot-starter : 第三方提供的
自动装配
@Configuration
告诉SpringBoot这是一个配置类
1、配置类里面使用@Bean标注在方法上给容器注册组件,默认也是单实例的
2、配置类本身也是组件
使用代理,容器实例唯一,不使用代理,多次调用多个实例
3、proxyBeanMethods:代理bean的方法
- Full(proxyBeanMethods = true)(保证每个@Bean方法被调用多少次返回的组件都是单实例的)(默认)
- Lite(proxyBeanMethods = false)(每个@Bean方法被调用多少次返回的组件都是新创建的)
最佳实战
- 配置 类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断,设置为false
- 配置 类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式(默认),设置为true
@Import导入组件
@Bean、@Component、@Controller、@Service、@Repository,它们是Spring的基本标签,在Spring Boot中并未改变它们原来的功能。
@Import({User.class, DBHelper.class})给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名
@Conditional条件装配
@ConditionalOnMissingBean 如果容器中没有配置这个对象,就会配置该对象
条件装配:满足Conditional指定的条件,则进行组件注入
@ImportResource导入Spring配置文件
想继续复用bean.xml,使用@ImportResource
@ConfigurationProperties配置绑定
使用Java读取到properties文件中的内容,并且把它封装到JavaBean中,以供随时使用
- Spring Boot一种配置配置绑定:
@ConfigurationProperties + @Component
- Spring Boot另一种配置配置绑定(第三方包无法添加@Component注解):
@EnableConfigurationProperties + @ConfigurationProperties
对于单个值,使用@Value
使用@Value注解
@Value(“${属性值:默认值}”)
Lombok简化开发
Lombok用标签方式代替构造器、getter/setter、toString()等鸡肋代码。
@NoArgsConstructor //无参构造器
@AllArgsConstructor //全参构造器
@Data //生成已有属性的getter和setter方法
@ToString //生成tostring方法
@EqualsAndHashCode //重写equals和hashcode
dev-tools
在IDEA中,项目或者页面修改以后:Ctrl+F9。
yaml
用来做以数据为中心的配置文件
@ConfigurationProperties配置后 properties最高,yml其次,yaml最低
请求和响应处理
静态资源规则与定制化
只要静态资源放在类路径下: called /static
(or /public
or /resources
or /META-INF/resources
改变默认的静态资源路径
web:resources:static-locations: [classpath:/haha/]
静态资源访问前缀
spring:mvc:static-path-pattern: /res/**
当前项目 + static-path-pattern + 静态资源名 = 静态资源文件夹下找
请求处理-Rest映射
@xxxMapping;
- @GetMapping
- @PostMapping
- @PutMapping
- @DeleteMapping
用法
- 开启页面表单的Rest功能
- 页面 form的属性method=post,隐藏域 _method=put、delete等(如果直接get或post,无需隐藏域)
- 编写请求映射
spring:mvc:hiddenmethod:filter:enabled: true #开启页面表单的Rest功能
Rest原理(表单提交要使用REST的时候)
- 表单提交会带上
\_method=PUT
- 请求过来被
HiddenHttpMethodFilter
拦截- 请求是否正常,并且是POST
- 获取到
\_method
的值。 - 兼容以下请求;PUT.DELETE.PATCH
- 原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
- 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。
- 获取到
- 请求是否正常,并且是POST
请求处理-常用参数注解使用
@PathVariable
路径变量 。@PathVariable Map<String,String> pv 就会将所有路径变量数据放入pv中@RequestHeader
获取请求头@RequestParam
获取请求参数(指问号后的参数,url?a=1&b=2)@CookieValue
获取Cookie值@RequestAttribute
获取request域属性@RequestBody
获取请求体[POST]@MatrixVariable
矩阵变量@ModelAttribute
矩阵变量-@MatrixVariable
语法: 请求路径:/cars/sell;low=34;brand=byd,audi,yd
原理:
HandlerMapping
中找到能处理请求的Handler
(Controller.method())。- 为当前Handler 找一个适配器
HandlerAdapter
,用的最多的是RequestMappingHandlerAdapter。 - 适配器执行目标方法并确定方法参数的每一个值。
将确定每个方法参数的值的过程封装到HandlerAdapter里面,相当于大的反射工具
请求处理-Servlet API参数
- WebRequest
- ServletRequest
- MultipartRequest
- HttpSession
- javax.servlet.http.PushBuilder
- Principal
- InputStream
- Reader
- HttpMethod
- Locale
- TimeZone
- ZoneId
请求处理-Model、Map
复杂参数:
- Map
- Model(map、model里面的数据会被放在request的请求域 request.setAttribute)
- Errors/BindingResult
- RedirectAttributes( 重定向携带数据)
- ServletResponse(response)
- SessionStatus
- UriComponentsBuilder
- ServletUriComponentsBuilder
model和map放到请求域中是在渲染之前进行的
请求处理-自定义Converter
未来我们可以给WebDataBinder里面放自己的Converter;
下面演示将字符串“啊猫,3”
转换成Pet
对象。
//1、WebMvcConfigurer定制化SpringMVC的功能@Beanpublic WebMvcConfigurer webMvcConfigurer(){return new WebMvcConfigurer() {@Overridepublic void addFormatters(FormatterRegistry registry) {registry.addConverter(new Converter<String, Pet>() {@Overridepublic Pet convert(String source) {// 啊猫,3if(!StringUtils.isEmpty(source)){Pet pet = new Pet();String[] split = source.split(",");pet.setName(split[0]);pet.setAge(Integer.parseInt(split[1]));return pet;}return null;}});}};}
响应处理-ReturnValueHandler
@ResponseBody
注解,即RequestResponseBodyMethodProcessor
,它实现HandlerMethodReturnValueHandler
接口
返回值处理器ReturnValueHandler
原理:
- 返回值处理器判断是否支持这种类型返回值
supportsReturnType
- 返回值处理器调用
handleReturnValue
进行处理 RequestResponseBodyMethodProcessor
可以处理返回值标了@ResponseBody
注解的。- 利用
MessageConverters
进行处理 将数据写为json- 内容协商(浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型)
- 服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据,
- SpringMVC会挨个遍历所有容器底层的
HttpMessageConverter
,看谁能处理?- 得到
MappingJackson2HttpMessageConverter
可以将对象写为json - 利用
MappingJackson2HttpMessageConverter
将对象转为json再写出去。
- 得到
- 利用
HttpMessageConverter
: 看是否支持将 此 Class
类型的对象,转为MediaType
类型的数据。
例子:Person
对象转为JSON,或者 JSON转为Person
,这将用到MappingJackson2HttpMessageConverter
响应处理-内容协商
根据客户端接收能力不同,返回不同媒体类型的数据。Http协议中规定的,Accept字段告诉服务器本客户端可以接收的数据类型。
内容协商原理:
- 判断当前响应头中是否已经有确定的媒体类型
MediaType
。 - 获取客户端(PostMan、浏览器)支持接收的内容类型。(获取客户端Accept请求头字段application/xml)getAcceptableMediaTypes 中
contentNegotiationManager
内容协商管理器 默认使用基于请求头的策略HeaderContentNegotiationStrategy
确定客户端可以接收的内容类型
- 遍历循环所有当前系统的
MessageConverter
,看谁支持操作这个对象(Person) - 找到支持操作Person的converter,把converter支持的媒体类型统计出来。
- 客户端需要application/xml,服务端有10种MediaType。
- 进行内容协商的最佳匹配媒体类型
- 用 支持 将对象转为 最佳匹配媒体类型 的converter。调用它进行转化 。
响应处理-基于请求参数的内容协商
上面内容协商的第二步
获取客户端(PostMan、浏览器)支持接收的内容类型。(获取客户端Accept请求头字段application/xml)getAcceptableMediaTypes 中
contentNegotiationManager
内容协商管理器 默认使用基于请求头的策略HeaderContentNegotiationStrategy
确定客户端可以接收的内容类型
spring:mvc:contentnegotiation:favor-parameter: true #开启请求参数内容协商模式
开启基于请求参数的内容协商功能。内容协商管理器,就会多了一个ParameterContentNegotiationStrategy
(由Spring容器注入)然后,浏览器地址输入带format参数的URL:
http://localhost:8080/test/person?format=json
或
http://localhost:8080/test/person?format=xml
这样,后端会根据参数format的值,返回对应json或xml格式的数据。
响应处理-自定义MessageConverter
实现多协议数据兼容。json、xml、x-guigu(这个是自创的)
-
@ResponseBody
响应数据出去 调用RequestResponseBodyMethodProcessor
处理 -
Processor 处理方法返回值。通过
MessageConverter
处理 -
所有
MessageConverter
合起来可以支持各种媒体类型数据的操作(读、写) -
内容协商找到最终的
messageConverter
SpringMVC的什么功能,一个入口给容器中添加一个 WebMvcConfigurer
@Configuration(proxyBeanMethods = false)
public class WebConfig {@Beanpublic WebMvcConfigurer webMvcConfigurer(){return new WebMvcConfigurer() {@Overridepublic void extendMessageConverters(List<HttpMessageConverter<?>> converters) {converters.add(new GuiguMessageConverter());}}}
}
Web开发
视图解析–视图解析器与视图
视图解析原理流程:
-
目标方法处理的过程中(阅读
DispatcherServlet
源码),所有数据都会被放在ModelAndViewContainer
里面,其中包括数据和视图地址。 -
方法的参数是一个自定义类型对象(从请求参数中确定的),把他重新放在
ModelAndViewContainer
。 -
任何目标方法执行完成以后都会返回
ModelAndView
(数据和视图地址)。 -
processDispatchResult()
处理派发结果(页面改如何响应)-
render(mv, request, response);
进行页面渲染逻辑- 根据方法的
String
返回值得到View
对象【定义了页面的渲染逻辑】
- 所有的视图解析器尝试是否能根据当前返回值得到
View
对象 - 得到了
redirect:/main.html --> Thymeleaf new RedirectView()
。 ContentNegotiationViewResolver
里面包含了下面所有的视图解析器,内部还是利用下面所有视图解析器得到视图对象。view.render(mv.getModelInternal(), request, response);
视图对象调用自定义的render进行页面渲染工作。
RedirectView
如何渲染【重定向到一个页面】- 获取目标url地址
response.sendRedirect(encodedURL);
- 根据方法的
-
视图解析:
- 返回值以 forward:
开始: new InternalResourceView(forwardUrl);
--> 转发request.getRequestDispatcher(path).forward(request, response);
- 返回值以 redirect:
开始: new RedirectView()
--> render就是重定向
- 返回值是普通字符串:new ThymeleafView()
—>
拦截器-登录检查与静态资源放行
Filter、Interceptor的区别?
- Filter是Servlet定义的原生组件,它的好处是脱离Spring应用也能使用。
- Interceptor是Spring定义的接口,可以使用Spring的自动装配等功能。
-
编写一个拦截器实现
HandlerInterceptor
接口 -
拦截器注册到容器中(实现
WebMvcConfigurer
的addInterceptors()
) -
指定拦截规则(注意,如果是拦截所有,静态资源也会被拦截】
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {/*** 目标方法执行之前*/@Overridepublic 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;}/*** 目标方法执行完成以后*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {log.info("postHandle执行{}",modelAndView);}/*** 页面渲染以后*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {log.info("afterCompletion执行异常{}",ex);}
}
拦截器注册到容器中 && 指定拦截规则:
@Configuration
public class AdminWebConfig implements WebMvcConfigurer{@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor())//拦截器注册到容器中.addPathPatterns("/**") //所有请求都被拦截包括静态资源.excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**","/aa/**"); //放行的请求
}
拦截器-拦截器的执行时机和原理
- 根据当前请求,找到
HandlerExecutionChain
(可以处理请求的handler以及handler的所有 拦截器) - 先来顺序执行 所有拦截器的
preHandle()
方法。- 如果当前拦截器
preHandle()
返回为true
。则执行下一个拦截器的preHandle()
- 如果当前拦截器返回为
false
。直接倒序执行所有已经执行了的拦截器的afterCompletion();
。
- 如果当前拦截器
- 如果任何一个拦截器返回
false
,直接跳出不执行目标方法。 - 所有拦截器都返回
true
,才执行目标方法。 - 倒序执行所有拦截器的
postHandle()
方法。 - 前面的步骤有任何异常都会直接倒序触发
afterCompletion()
。 - 页面成功渲染完成以后,也会倒序触发
afterCompletion()
。
文件上传-单文件与多文件上传的使用
@RequestPart注解 + MultipartFile 类型
文件大小相关配置项
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=100MB
@Slf4j
@Controller
public class FormTestController {@GetMapping("/form_layouts")public String form_layouts(){return "form/form_layouts";}@PostMapping("/upload")public String upload(@RequestParam("email") String email,@RequestParam("username") String username,@RequestPart("headerImg") MultipartFile headerImg,@RequestPart("photos") MultipartFile[] photos) throws IOException {log.info("上传的信息:email={},username={},headerImg={},photos={}",email,username,headerImg.getSize(),photos.length);if(!headerImg.isEmpty()){//保存到文件服务器,OSS服务器String originalFilename = headerImg.getOriginalFilename();headerImg.transferTo(new File("H:\\cache\\"+originalFilename));}if(photos.length > 0){for (MultipartFile photo : photos) {if(!photo.isEmpty()){String originalFilename = photo.getOriginalFilename();photo.transferTo(new File("H:\\cache\\"+originalFilename));}}}return "main";}
}
文件上传-文件上传参数解析器
文件上传相关的自动配置类MultipartAutoConfiguration
有创建文件上传参数解析器StandardServletMultipartResolver
。
文件上传自动配置类—MultipartAutoConfiguration—MultipartProperties;自动配置好了 StandardServletMultipartResolver 【文件上传解析器】
原理步骤
- 请求进来使用文件上传解析器判断(isMultipart)并封装(resolveMultipart,返回MultipartHttpServletRequest)文件上传请求
- 参数解析器来解析请求中的文件内容封装成MultipartFile
- 将request中文件信息封装为一个Map;MultiValueMap<String, MultipartFile>。 FileCopyUtils,实现文件流的拷贝
错误处理-SpringBoot默认错误处理机制
默认规则:
-
默认情况下,Spring Boot提供
/error
处理所有错误的映射 -
机器客户端,它将生成JSON响应,其中包含错误,HTTP状态和异常消息的详细信息。对于浏览器客户端,响应一个“ whitelabel”错误视图,以HTML格式呈现相同的数据
定制错误处理逻辑
-
自定义错误页
error/404.html error/5xx.html;有精确的错误状态码页面就匹配精确,没有就找 4xx.html;如果都没有就触发白页 -
@ControllerAdvice+@ExceptionHandler处理全局异常;底层是
ExceptionHandlerExceptionResolver
支持的@Slf4j @ControllerAdvice public class GlobalExceptionHandler {@ExceptionHandler({ArithmeticException.class,NullPointerException.class}) //处理异常public String handleArithException(Exception e){log.error("异常是:{}",e);return "login"; //视图地址} }
-
@ResponseStatus+自定义异常 ;底层是
ResponseStatusExceptionResolver
,把responseStatus注解的信息底层调用response.sendError(statusCode, resolvedReason)
,tomcat发送的/error
@ResponseStatus(value= HttpStatus.FORBIDDEN,reason = "用户数量太多") public class UserTooManyException extends RuntimeException {public UserTooManyException(){}public UserTooManyException(String message){super(message);} }
@Controller public class TableController {@GetMapping("/dynamic_table")public String dynamic_table(@RequestParam(value="pn",defaultValue = "1") Integer pn,Model model){//表格内容的遍历List<User> users = Arrays.asList(new User("zhangsan", "123456"),new User("lisi", "123444"),new User("haha", "aaaaa"),new User("hehe ", "aaddd"));model.addAttribute("users",users);if(users.size()>3){throw new UserTooManyException();//抛出自定义异常}return "table/dynamic_table";}}
-
Spring底层的异常,如 参数类型转换异常;
DefaultHandlerExceptionResolver
处理框架底层的异常
response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage()); -
自定义实现
HandlerExceptionResolver
处理异常;可以作为默认的全局异常处理规则@Order(value= Ordered.HIGHEST_PRECEDENCE) //优先级,数字越小优先级越高 @Component public class CustomerHandlerExceptionResolver implements HandlerExceptionResolver {@Overridepublic ModelAndView resolveException(HttpServletRequest request,HttpServletResponse response,Object handler, Exception ex) {try {response.sendError(511,"我喜欢的错误");} catch (IOException e) {e.printStackTrace();}return new ModelAndView();} }
异常处理自动配置原理
ErrorMvcAutoConfiguration
自动配置异常处理规则- 容器中的组件:类型:
DefaultErrorAttributes
-> id:errorAttributes
public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver
DefaultErrorAttributes
:定义错误页面中可以包含数据(异常明细,堆栈信息等)。
- 容器中的组件:类型:
BasicErrorController
--> id:basicErrorController
(json+白页 适配响应) - 处理默认
/error
路径的请求,页面响应new ModelAndView("error", model);
- 容器中有组件
View
->id是error;(响应默认错误页) - 容器中放组件
BeanNameViewResolver
(视图解析器);按照返回的视图名作为组件的id去容器中找View
对象。
- 容器中有组件
- 容器中的组件:类型:
DefaultErrorViewResolver
-> id:conventionErrorViewResolver
- 如果发生异常错误,会以HTTP的状态码 作为视图页地址(viewName),找到真正的页面(主要作用)。
- error/404、5xx.html
- 如果想要返回页面,就会找error视图(
StaticView
默认是一个白页)。
总结:如果想要返回页面;就会找error视图【StaticView】。(默认是一个白页)
要么响应一个modelandvie(页面),要么响应一个responseentity(json)
原生组件注入-原生注解与Spring方式注入
使用原生的注解
1、添加@WebServlet(urlPatterns = “/my”)注解 ; @WebFilter(urlPatterns={“/css/“,”/images/”}) ;@WebListener
2、在主启动类添加注解@ServletComponentScan
指定原生Servlet组件都放在那里
@WebServlet(urlPatterns = "/my")
public class MyServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().write("66666");}
}
@Slf4j
@WebFilter(urlPatterns={"/css/*","/images/*"}) //my
public class MyFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {log.info("MyFilter初始化完成");}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {log.info("MyFilter工作");chain.doFilter(request,response);}@Overridepublic void destroy() {log.info("MyFilter销毁");}
}
@Slf4j
@WebListener
public class MyServletContextListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {log.info("MySwervletContextListener监听到项目初始化完成");}@Overridepublic void contextDestroyed(ServletContextEvent sce) {log.info("MySwervletContextListener监听到项目销毁");}
}
最后还要在主启动类添加注解@ServletComponentScan
@ServletComponentScan(basePackages = "com.lun")//
@SpringBootApplication(exclude = RedisAutoConfiguration.class)
public class Boot05WebAdminApplication {public static void main(String[] args) {SpringApplication.run(Boot05WebAdminApplication.class, args);}
}
定制化springmvc
@EnableWebMvc
+WebMvcConfigurer
—@Bean
可以全面接管SpringMVC,所有规则全部自己重新配置; 实现定制和扩展功能(高级功能,初学者退避三舍)。- 原理:
WebMvcAutoConfiguration
默认的SpringMVC的自动配置功能类,如静态资源、欢迎页等。- 一旦使用
@EnableWebMvc
,会@Import(DelegatingWebMvcConfiguration.class)
。 DelegatingWebMvcConfiguration
的作用,只保证SpringMVC最基本的使用- 把所有系统中的
WebMvcConfigurer
拿过来,所有功能的定制都是这些WebMvcConfigurer
合起来一起生效。 - 自动配置了一些非常底层的组件,如
RequestMappingHandlerMapping
,这些组件依赖的组件都是从容器中获取如。 public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport
。
- 把所有系统中的
WebMvcAutoConfiguration
里面的配置要能生效必须@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
。- @EnableWebMvc 导致了WebMvcAutoConfiguration 没有生效。
- 原理:
原理分析套路
场景starter - xxxxAutoConfiguration
- 导入xxx组件 - 绑定xxxProperties
- 绑定配置文件项。
数据访问
JDBC
spring:datasource:url: jdbc:mysql://localhost:3306/db_accountusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driver
Druid
数据库连接池,它能够提供强大的监控和扩展功能。
spring:datasource:url: jdbc:mysql://localhost:3306/db_accountusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driverdruid:aop-patterns: com.atguigu.admin.* #监控SpringBeanfilters: stat,wall # 底层开启功能,stat(sql监控),wall(防火墙)stat-view-servlet: # 配置监控页功能enabled: truelogin-username: adminlogin-password: adminresetEnable: falseweb-stat-filter: # 监控webenabled: trueurlPattern: /*exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'filter:stat: # 对上面filters里面的stat的详细配置slow-sql-millis: 1000logSlowSql: trueenabled: truewall:enabled: trueconfig:drop-table-allow: false
MyBatis
spring:datasource:username: rootpassword: 1234url: jdbc:mysql://localhost:3306/mydriver-class-name: com.mysql.jdbc.Driver# 配置mybatis规则
mybatis:config-location: classpath:mybatis/mybatis-config.xml #全局配置文件位置mapper-locations: classpath:mybatis/*.xml #sql映射文件位置
mybatis-config.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!-- 由于Spring Boot自动配置缘故,此处不必配置,只用来做做样。-->
</configuration>
注解与配置混合搭配,干活不累:
@Mapper
public interface UserMapper {public User getUser(Integer id);@Select("select * from user where id=#{id}")public User getUser2(Integer id);public void saveUser(User user);@Insert("insert into user(`name`) values(#{name})")@Options(useGeneratedKeys = true, keyProperty = "id")public void saveUser2(User user);}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lun.boot.mapper.UserMapper"><select id="getUser" resultType="com.lun.boot.bean.User">select * from user where id=#{id}</select><insert id="saveUser" useGeneratedKeys="true" keyProperty="id">insert into user(`name`) values(#{name})</insert></mapper>
简单DAO方法就写在注解上。复杂的就写在配置文件里。
使用@MapperScan("com.lun.boot.mapper")
简化,Mapper接口就可以不用标注@Mapper
注解。
小结
- 导入mybatis-starter。
- 编写Mapper接口,需
@Mapper
注解。 - 编写SQL映射文件并绑定Mapper接口。
- 在
application.yaml
中指定Mapper配置文件的所处位置mapper-location,以及指定全局配置文件的信息 (建议:配置在mybatis.configuration
)。 - 在启动程序Application上添加@MapperScan(“com.atguigu.admin.mapper”) 简化,其他的接口就可以不用标注@Mapper注解
MyBatisPlus
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lun.hellomybatisplus.model.User;public interface UserMapper extends BaseMapper<User> {}
/*** Service 的CRUD也不用写了*/
public interface UserService extends IService<User> {//此处故意为空
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService {//此处故意为空
}
添加分页插件
@Configuration
public class MyBatisConfig {/*** MybatisPlusInterceptor* @return*/@Beanpublic MybatisPlusInterceptor paginationInterceptor() {MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false// paginationInterceptor.setOverflow(false);// 设置最大单页限制数量,默认 500 条,-1 不受限制// paginationInterceptor.setLimit(500);// 开启 count 的 join 优化,只针对部分 left join//这是分页拦截器PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();paginationInnerInterceptor.setOverflow(true);paginationInnerInterceptor.setMaxLimit(500L);mybatisPlusInterceptor.addInnerInterceptor(paginationInnerInterceptor);return mybatisPlusInterceptor;}
}
Redis
spring:redis:
# url: redis://lfy:Lfy123456@r-bp1nc7reqesxisgxpipd.redis.rds.aliyuncs.com:6379host: r-bp1nc7reqesxisgxpipd.redis.rds.aliyuncs.comport: 6379password: lfy:Lfy123456client-type: jedisjedis:pool:max-active: 10
# lettuce:# 另一个用来连接redis的java框架
# pool:
# max-active: 10
# min-idle: 5
URL统计拦截器:
@Component
public class RedisUrlCountInterceptor implements HandlerInterceptor {@AutowiredStringRedisTemplate redisTemplate;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String uri = request.getRequestURI();//默认每次访问当前uri就会计数+1redisTemplate.opsForValue().increment(uri);return true;}
}
注册URL统计拦截器:
@Configuration
public class AdminWebConfig implements WebMvcConfigurer{@AutowiredRedisUrlCountInterceptor redisUrlCountInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(redisUrlCountInterceptor).addPathPatterns("/**").excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**","/aa/**");}
}
Filter、Interceptor的区别?
- Filter是Servlet定义的原生组件,它的好处是脱离Spring应用也能使用。
- Interceptor是Spring定义的接口,可以使用Spring的自动装配等功能。
JUnit5
常用测试注解
- @Test:表示方法是测试方法。但是与JUnit4的@Test不同,他的职责非常单一不能声明任何属性,拓展的测试将会由Jupiter提供额外测试
- @ParameterizedTest:表示方法是参数化测试。
- @RepeatedTest:表示方法可重复执行。
- @DisplayName:为测试类或者测试方法设置展示名称。
- @BeforeEach:表示在每个单元测试之前执行。
- @AfterEach:表示在每个单元测试之后执行。
- @BeforeAll:表示在所有单元测试之前执行。
- @AfterAll:表示在所有单元测试之后执行。
- @Tag:表示单元测试类别,类似于JUnit4中的@Categories。
- @Disabled:表示测试类或测试方法不执行,类似于JUnit4中的@Ignore。
- @Timeout:表示测试方法运行如果超过了指定时间将会返回错误。
- @ExtendWith:为测试类或测试方法提供扩展类引用。
import org.junit.jupiter.api.*;@DisplayName("junit5功能测试类")
public class Junit5Test {@DisplayName("测试displayname注解")@Testvoid testDisplayName() {System.out.println(1);System.out.println(jdbcTemplate);}@ParameterizedTest@ValueSource(strings = { "racecar", "radar", "able was I ere I saw elba" })void palindromes(String candidate) {assertTrue(StringUtils.isPalindrome(candidate));}@Disabled@DisplayName("测试方法2")@Testvoid test2() {System.out.println(2);}@RepeatedTest(5)@Testvoid test3() {System.out.println(5);}/*** 规定方法超时时间。超出时间测试出异常** @throws InterruptedException*/@Timeout(value = 500, unit = TimeUnit.MILLISECONDS)@Testvoid testTimeout() throws InterruptedException {Thread.sleep(600);}@BeforeEachvoid testBeforeEach() {System.out.println("测试就要开始了...");}@AfterEachvoid testAfterEach() {System.out.println("测试结束了...");}@BeforeAllstatic void testBeforeAll() {System.out.println("所有测试就要开始了...");}@AfterAllstatic void testAfterAll() {System.out.println("所有测试以及结束了...");}}
断言机制
- 简单断言
方法 | 说明 |
---|---|
assertEquals | 判断两个对象或两个原始类型是否相等 |
assertNotEquals | 判断两个对象或两个原始类型是否不相等 |
assertSame | 判断两个对象引用是否指向同一个对象 |
assertNotSame | 判断两个对象引用是否指向不同的对象 |
assertTrue | 判断给定的布尔值是否为 true |
assertFalse | 判断给定的布尔值是否为 false |
assertNull | 判断给定的对象引用是否为 null |
assertNotNull | 判断给定的对象引用是否不为 null |
@Test
@DisplayName("simple assertion")
public void simple() {assertEquals(3, 1 + 2, "simple math");assertNotEquals(3, 1 + 1);assertNotSame(new Object(), new Object());Object obj = new Object();assertSame(obj, obj);assertFalse(1 > 2);assertTrue(1 < 2);assertNull(null);assertNotNull(new Object());
}
- 数组断言
通过 assertArrayEquals 方法来判断两个对象或原始类型的数组是否相等。
@Test
@DisplayName("array assertion")
public void array() {assertArrayEquals(new int[]{1, 2}, new int[] {1, 2});
}
- 组合断言
assertAll()
方法接受多个 org.junit.jupiter.api.Executable
函数式接口的实例作为要验证的断言,可以通过 lambda 表达式很容易的提供这些断言。
@Test
@DisplayName("assert all")
public void all() {assertAll("Math",() -> assertEquals(2, 1 + 1),() -> assertTrue(1 > 0));
}
- 异常断言
在JUnit4时期,想要测试方法的异常情况时,需要用@Rule
注解的ExpectedException
变量还是比较麻烦的。而JUnit5提供了一种新的断言方式Assertions.assertThrows()
,配合函数式编程就可以进行使用。
断定业务逻辑一定出现异常
@Test
@DisplayName("异常测试")
public void exceptionTest() {ArithmeticException exception = Assertions.assertThrows(//扔出断言异常ArithmeticException.class, () -> System.out.println(1 % 0));
}
- 超时断言
JUnit5还提供了Assertions.assertTimeout()为测试方法设置了超时时间。
@Test
@DisplayName("超时测试")
public void timeoutTest() {//如果测试方法时间超过1s将会异常Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(500));
}
- 快速失败
通过 fail 方法直接使得测试失败。
@Test
@DisplayName("fail")
public void shouldFail() {fail("This should fail");
}
前置条件
assumeTrue
和 assumFalse
确保给定的条件为 true
或 false
,不满足条件会使得测试执行终止。
assumingThat
的参数是表示条件的布尔值和对应的 Executable 接口的实现对象。只有条件满足时,Executable
对象才会被执行;当条件不满足时,测试执行并不会终止。
@DisplayName("前置条件")
public class AssumptionsTest {private final String environment = "DEV";@Test@DisplayName("simple")public void simpleAssume() {assumeTrue(Objects.equals(this.environment, "DEV"));assumeFalse(() -> Objects.equals(this.environment, "PROD"));}@Test@DisplayName("assume then do")public void assumeThenDo() {assumingThat(Objects.equals(this.environment, "DEV"),() -> System.out.println("In DEV"));}
}
嵌套测试
@Nested
注解实现嵌套测试,从而可以更好的把相关的测试方法组织在一起。在内部类中可以使用@BeforeEach
和@AfterEach
注解,而且嵌套的层次没有限制。
@DisplayName("A stack")
class TestingAStackDemo {Stack<Object> stack;@Test@DisplayName("is instantiated with new Stack()")void isInstantiatedWithNew() {new Stack<>();}@Nested@DisplayName("when new")class WhenNew {@BeforeEachvoid createNewStack() {stack = new Stack<>();}@Test@DisplayName("is empty")void isEmpty() {assertTrue(stack.isEmpty());}@Test@DisplayName("throws EmptyStackException when popped")void throwsExceptionWhenPopped() {assertThrows(EmptyStackException.class, stack::pop);}@Test@DisplayName("throws EmptyStackException when peeked")void throwsExceptionWhenPeeked() {assertThrows(EmptyStackException.class, stack::peek);}@Nested@DisplayName("after pushing an element")class AfterPushing {String anElement = "an element";@BeforeEachvoid pushAnElement() {stack.push(anElement);}@Test@DisplayName("it is no longer empty")void isNotEmpty() {assertFalse(stack.isEmpty());}@Test@DisplayName("returns the element when popped and is empty")void returnElementWhenPopped() {assertEquals(anElement, stack.pop());assertTrue(stack.isEmpty());}@Test@DisplayName("returns the element when peeked but remains not empty")void returnElementWhenPeeked() {assertEquals(anElement, stack.peek());assertFalse(stack.isEmpty());}}}
}
参数化测试
- @ValueSource: 为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型
- @NullSource: 表示为参数化测试提供一个null的入参
- @EnumSource: 表示为参数化测试提供一个枚举入参
- @CsvFileSource:表示读取指定CSV文件内容作为参数化测试入参
- @MethodSource:表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)
@ParameterizedTest
@ValueSource(strings = {"one", "two", "three"})
@DisplayName("参数化测试1")
public void parameterizedTest1(String string) {System.out.println(string);Assertions.assertTrue(StringUtils.isNotBlank(string));
}@ParameterizedTest
@MethodSource("method") //指定方法名
@DisplayName("方法来源参数")
public void testWithExplicitLocalMethodSource(String name) {System.out.println(name);Assertions.assertNotNull(name);
}static Stream<String> method() {return Stream.of("apple", "banana");
}
迁移指南
官方文档 - Migrating from JUnit 4
在进行迁移的时候需要注意如下的变化:
- 注解在
org.junit.jupiter.api
包中,断言在org.junit.jupiter.api.Assertions
类中,前置条件在org.junit.jupiter.api.Assumptions
类中。 - 把
@Before
和@After
替换成@BeforeEach
和@AfterEach
。 - 把
@BeforeClass
和@AfterClass
替换成@BeforeAll
和@AfterAll。 - 把
@Ignore
替换成@Disabled
。 - 把
@Category
替换成@Tag
。 - 把
@RunWith
、@Rule
和@ClassRule
替换成@ExtendWith
。
指标监控
未来每一个微服务在云上部署以后,我们都需要对其进行监控、追踪、审计、控制等。SpringBoot就抽取了Actuator场景,使得我们每个微服务快速引用即可获得生产级别的应用监控、审计等功能。
Actuator
- 添加依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 访问
http://localhost:8080/actuator/**
。 - 暴露所有监控信息为HTTP。
management:endpoints:enabled-by-default: true #暴露所有端点信息web:exposure:include: '*' #以web方式暴露
- 测试例子
- http://localhost:8080/actuator/beans
- http://localhost:8080/actuator/configprops
- http://localhost:8080/actuator/metrics
- http://localhost:8080/actuator/metrics/jvm.gc.pause
- http://localhost:8080/actuator/metrics/endpointName/detailPath
常使用的端点及开启与禁用
常使用的端点
ID | 描述 |
---|---|
auditevents | 暴露当前应用程序的审核事件信息。需要一个AuditEventRepository组件 。 |
beans | 显示应用程序中所有Spring Bean的完整列表。 |
caches | 暴露可用的缓存。 |
conditions | 显示自动配置的所有条件信息,包括匹配或不匹配的原因。 |
configprops | 显示所有@ConfigurationProperties 。 |
env | 暴露Spring的属性ConfigurableEnvironment |
flyway | 显示已应用的所有Flyway数据库迁移。 需要一个或多个Flyway 组件。 |
health | 显示应用程序运行状况信息。 |
httptrace | 显示HTTP跟踪信息(默认情况下,最近100个HTTP请求-响应)。需要一个HttpTraceRepository 组件。 |
info | 显示应用程序信息。 |
integrationgraph | 显示Spring integrationgraph 。需要依赖spring-integration-core 。 |
loggers | 显示和修改应用程序中日志的配置。 |
liquibase | 显示已应用的所有Liquibase数据库迁移。需要一个或多个Liquibase 组件。 |
metrics | 显示当前应用程序的“指标”信息。 |
mappings | 显示所有@RequestMapping 路径列表。 |
scheduledtasks | 显示应用程序中的计划任务。 |
sessions | 允许从Spring Session支持的会话存储中检索和删除用户会话。需要使用Spring Session的基于Servlet的Web应用程序。 |
shutdown | 使应用程序正常关闭。默认禁用。 |
startup | 显示由ApplicationStartup 收集的启动步骤数据。需要使用SpringApplication 进行配置BufferingApplicationStartup 。 |
threaddump | 执行线程转储。 |
如果您的应用程序是Web应用程序(Spring MVC,Spring WebFlux或Jersey),则可以使用以下附加端点:
ID | 描述 |
---|---|
heapdump | 返回hprof 堆转储文件。 |
jolokia | 通过HTTP暴露JMX bean(需要引入Jolokia,不适用于WebFlux)。需要引入依赖jolokia-core 。 |
logfile | 返回日志文件的内容(如果已设置logging.file.name 或logging.file.path 属性)。支持使用HTTPRange 标头来检索部分日志文件的内容。 |
prometheus | 以Prometheus服务器可以抓取的格式公开指标。需要依赖micrometer-registry-prometheus 。 |
其中最常用的Endpoint:
- Health:监控状况
- Metrics:运行时指标
- Loggers:日志记录
Health Endpoint
健康检查端点,我们一般用于在云平台,平台会定时的检查应用的健康状况,我们就需要Health Endpoint可以为平台返回当前应用的一系列组件健康状况的集合。
重要的几点:
- health endpoint返回的结果,应该是一系列健康检查后的一个汇总报告。
- 很多的健康检查默认已经自动配置好了,比如:数据库、redis等。
- 可以很容易的添加自定义的健康检查机制。
Metrics Endpoint
提供详细的、层级的、空间指标信息,这些信息可以被pull(主动推送)或者push(被动获取)方式得到:
- 通过Metrics对接多种监控系统。
- 简化核心Metrics开发。
- 添加自定义Metrics或者扩展已有Metrics。
开启与禁用Endpoints
- 默认所有的Endpoint除过shutdown都是开启的。
- 需要开启或者禁用某个Endpoint。配置模式为
management.endpoint.<endpointName>.enabled = true
management:endpoint:beans:enabled: true
- 或者禁用所有的Endpoint然后手动开启指定的Endpoint。
management:endpoints:enabled-by-default: false# 关闭总开关,开启分体开关endpoint:beans:enabled: truehealth:enabled: true
暴露Endpoints
支持的暴露方式
- HTTP:默认只暴露health和info。
- JMX:默认暴露所有Endpoint。
- 除过health和info,剩下的Endpoint都应该进行保护访问。如果引入Spring Security,则会默认配置安全访问规则。
ID | JMX | Web |
---|---|---|
auditevents | Yes | No |
beans | Yes | No |
caches | Yes | No |
conditions | Yes | No |
configprops | Yes | No |
env | Yes | No |
flyway | Yes | No |
health | Yes | Yes |
heapdump | N/A | No |
httptrace | Yes | No |
info | Yes | Yes |
integrationgraph | Yes | No |
jolokia | N/A | No |
logfile | N/A | No |
loggers | Yes | No |
liquibase | Yes | No |
metrics | Yes | No |
mappings | Yes | No |
prometheus | N/A | No |
scheduledtasks | Yes | No |
sessions | Yes | No |
shutdown | Yes | No |
startup | Yes | No |
threaddump | Yes | No |
若要更改公开的Endpoint,请配置以下的包含和排除属性:
Property | Default |
---|---|
management.endpoints.jmx.exposure.exclude | |
management.endpoints.jmx.exposure.include | * |
management.endpoints.web.exposure.exclude | |
management.endpoints.web.exposure.include | info, health |
官方文档 - Exposing Endpoints
定制Endpoint
定制 Health 信息
# management是所有actuator的配置
# management.endpoint.端点名.** 对某个端点的具体配置
management:endpoints:enabled-by-default: trueweb:exposure:include: '*'endpoint:health:show-details: always #总是显示详细信息。可显示每个模块的状态信息
通过实现HealthIndicator
接口,或继承MyComHealthIndicator
类。
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;@Component
public class MyHealthIndicator implements HealthIndicator {@Overridepublic Health health() {int errorCode = check(); // perform some specific health checkif (errorCode != 0) {return Health.down().withDetail("Error Code", errorCode).build();}return Health.up().build();}}/*
构建Health
Health build = Health.down().withDetail("msg", "error service").withDetail("code", "500").withException(new RuntimeException()).build();
*/
@Component
public class MyComHealthIndicator extends AbstractHealthIndicator {/*** 真实的检查方法* @param builder* @throws Exception*/@Overrideprotected void doHealthCheck(Health.Builder builder) throws Exception {//mongodb。 获取连接进行测试Map<String,Object> map = new HashMap<>();// 检查完成if(1 == 2){
// builder.up(); //健康builder.status(Status.UP);map.put("count",1);map.put("ms",100);}else {
// builder.down();builder.status(Status.OUT_OF_SERVICE);map.put("err","连接超时");map.put("ms",3000);}builder.withDetail("code",100).withDetails(map);}
}
定制info信息
常用两种方式:
- 编写配置文件
info:appName: boot-adminversion: 2.0.1mavenProjectName: @project.artifactId@ #使用@@可以获取maven的pom文件值mavenProjectVersion: @project.version@
- 编写InfoContributor
import java.util.Collections;import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.stereotype.Component;@Component
public class ExampleInfoContributor implements InfoContributor {@Overridepublic void contribute(Info.Builder builder) {builder.withDetail("example",Collections.singletonMap("key", "value"));}}
http://localhost:8080/actuator/info 会输出以上方式返回的所有info信息
定制Metrics信息
Spring Boot支持的metrics
增加定制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 {@ReadOperationpublic Map getDockerInfo(){//端点的读操作,默认访问地址 /actuator/container ,下面是输出的内容return Collections.singletonMap("info","docker started...");}@WriteOperationprivate void restartDocker(){System.out.println("docker restarted....");}}
场景:
- 开发ReadinessEndpoint来管理程序是否就绪。
- 开发LivenessEndpoint来管理程序是否存活。
自定义starter
starter启动原理
starter只是说要引入哪些依赖
- starter的pom.xml引入autoconfigure依赖
-
autoconfigure包中配置使用
META-INF/spring.factories
中EnableAutoConfiguration
的值,使得项目启动加载指定的自动配置类 -
编写自动配置类
xxxAutoConfiguration
->xxxxProperties
-
@Configuration
@Conditional
@EnableConfigurationProperties
@Bean
- …
-
引入starter —
xxxAutoConfiguration
— 容器中放入组件 ----绑定xxxProperties
---- 配置项
自定义starter
-
目标:创建
HelloService
的自定义starter。 -
创建两个工程,分别命名为
hello-spring-boot-starter
(普通Maven工程),hello-spring-boot-starter-autoconfigure
(需用用到Spring Initializr创建的Maven工程)。 -
hello-spring-boot-starter
无需编写什么代码,只需让该工程引入hello-spring-boot-starter-autoconfigure
依赖:
<?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><groupId>com.lun</groupId><artifactId>hello-spring-boot-starter</artifactId><version>1.0.0-SNAPSHOT</version><dependencies><dependency><groupId>com.lun</groupId><artifactId>hello-spring-boot-starter-autoconfigure</artifactId><version>1.0.0-SNAPSHOT</version></dependency></dependencies></project>
hello-spring-boot-starter-autoconfigure
的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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.4.2</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.lun</groupId><artifactId>hello-spring-boot-starter-autoconfigure</artifactId><version>1.0.0-SNAPSHOT</version><name>hello-spring-boot-starter-autoconfigure</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency></dependencies>
</project>
- 创建4个文件:
com/lun/hello/auto/HelloServiceAutoConfiguration
com/lun/hello/bean/HelloProperties
com/lun/hello/service/HelloService
src/main/resources/META-INF/spring.factories
import com.lun.hello.bean.HelloProperties;
import com.lun.hello.service.HelloService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
@ConditionalOnMissingBean(HelloService.class)
@EnableConfigurationProperties(HelloProperties.class)//默认HelloProperties放在容器中
public class HelloServiceAutoConfiguration {@Beanpublic HelloService helloService(){return new HelloService();}}
import org.springframework.boot.context.properties.ConfigurationProperties;@ConfigurationProperties("hello")
public class HelloProperties {private String prefix;private String suffix;public String getPrefix() {return prefix;}public void setPrefix(String prefix) {this.prefix = prefix;}public String getSuffix() {return suffix;}public void setSuffix(String suffix) {this.suffix = suffix;}
}
import com.lun.hello.bean.HelloProperties;
import org.springframework.beans.factory.annotation.Autowired;/*** 默认不要放在容器中*/
public class HelloService {@Autowiredprivate HelloProperties helloProperties;public String sayHello(String userName){return helloProperties.getPrefix() + ": " + userName + " > " + helloProperties.getSuffix();}
}
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.lun.hello.auto.HelloServiceAutoConfiguration
-
用maven插件,将两工程install到本地。
-
接下来,测试使用自定义starter,用Spring Initializr创建名为
hello-spring-boot-starter-test
工程,引入hello-spring-boot-starter
依赖,其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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.4.2</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.lun</groupId><artifactId>hello-spring-boot-starter-test</artifactId><version>1.0.0-SNAPSHOT</version><name>hello-spring-boot-starter-test</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- 引入`hello-spring-boot-starter`依赖 --><dependency><groupId>com.lun</groupId><artifactId>hello-spring-boot-starter</artifactId><version>1.0.0-SNAPSHOT</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
- 添加配置文件
application.properties
:
hello.prefix=hello
hello.suffix=666
- 添加单元测试类:
import com.lun.hello.service.HelloService;//来自自定义starter
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
class HelloSpringBootStarterTestApplicationTests {@Autowiredprivate HelloService helloService;@Testvoid contextLoads() {// System.out.println(helloService.sayHello("lun"));Assertions.assertEquals("hello: lun > 666", helloService.sayHello("lun"));}}
SpringBoot完整启动过程
-
加载配置文件
Spring Boot 应用程序启动时,会自动加载 application.properties 或 application.yml 配置文件中的属性。这些属性可以通过 @Value 注解或 @ConfigurationProperties 注解注入到代码中。 -
创建 Spring 容器
Spring Boot 会自动创建 Spring 容器,并加载所有的组件,包括控制器、服务、仓库等。Spring 容器会根据依赖注入(DI)和面向切面编程(AOP)等原理,完成组件之间的依赖关系和处理逻辑。 -
扫描组件
Spring Boot 会扫描项目中的所有组件,包括控制器、服务、仓库等,并将其加载到 Spring 容器中。 -
启动 Tomcat
Spring Boot 应用程序中默认集成了 Tomcat 服务器。启动 Spring Boot 应用程序时,会自动启动内嵌的 Tomcat 服务器,提供 HTTP 服务。 -
处理请求
Tomcat 接收到请求后,会将其转发给 Spring Boot 应用程序。Spring Boot 应用程序会根据请求的 URL,匹配到对应的控制器,并调用相应的方法处理请求。 -
返回响应
处理完请求后,Spring Boot 应用程序会将处理结果封装成相应的 HTTP 响应,返回给客户端。
总的来说,Spring Boot 应用程序的启动过程是一个自动化的过程,可以快速地搭建出一个 Web 应用程序,并提供相应的 HTTP 服务。
相关文章:
SpringBoot知识快速复习
Spring知识快速复习启动器自动装配ConfigurationImport导入组件Conditional条件装配ImportResource导入Spring配置文件ConfigurationProperties配置绑定Lombok简化开发dev-toolsyaml请求和响应处理静态资源规则与定制化请求处理-Rest映射请求处理-常用参数注解使用请求处理-Ser…...
SpringBoot+React博客论坛系统 附带详细运行指导视频
文章目录一、项目演示二、项目介绍三、项目运行截图四、主要代码一、项目演示 项目演示地址: 视频地址 二、项目介绍 项目描述:这是一个基于SpringBootReact框架开发的博客论坛系统。首先,这是一个前后端分离的项目,文章编辑器…...
C++ primer 之 extern
C primer 之 extern什么是声明什么是定义两者有什么区别ertern的作用什么是声明 就是使得名字为程序所知,一个文件如果想使用别处定义的名字就必须包含对那个名字的声明。 什么是定义 负责创建与名字关联的实体。 两者有什么区别 变量声明和声明都规定了变量的…...
Linux 练习二 (VIM编辑器 + GCC编译器 + GDB调试)
文章目录VIM命令思维导图GCC编译器1、GCC编译文件练习2、静态库动态库制作练习将此函数编译成动态库将此函数编译成静态库GCC优化选项 -OnGDB调试命令练习练习一:编写一个程序,通过gdb调试,使用到gdb的b,n,s࿰…...
python3 连接数据库 mysql PyMysql
python3PyMysql PyMySQL 是在 Python3.x 版本中用于连接 MySQL 服务器的一个库 , 遵循 Python 数据库 API v2.0 规范 。 PyMySQL 安装 pip install PyMySQLPyMySQL 连接数据库 import pymysql pymysql.Connect(hostlocalhost,port 3306,user root,password **…...
昇腾AI新技能,还能预防猪生病?
国药集团动物保健股份有限公司(简称“国药动保”)是专业从事动物保健产品研发、生产和销售的国家高新技术企业,是国内少数几家具备新产品原创能力的动物保健企业。其中,猪圆环病毒灭活疫苗等市场份额位居行业前列。 “猪圆环病毒…...
模板方法模式(Template Method)
模式结构图 说明 基本方法是模板方法的组成部分。基本方法分为一下三种: 抽象方法 由抽象类声明,由其具体子类实现。C中就是纯虚函数。 具体方法 由抽象类或具体类声明并实现,子类可以进行覆盖也可以继承。C中是虚函数。 钩子方法 由抽象类…...
C C++ typedef的使用
一、为基本数据类型起别名 typedef int myint; myint x 5; "myint"是"int"的别名,可以使用"myint"来代替"int"声明变量,这个很好理解,但是也很少有人这么用吧。 二、为结构体起别名 …...
Laravel框架03:DB类操作数据库
Laravel框架03:DB类操作数据库一、概述二、数据表的创建与配置三、增删改操作1. 增加信息2. 修改数据3. 删除数据四、查询操作1. 取出基本数据2. 取出单行数据3. 获取一个字段的值4. 获取多个字段的值5. 排序6. 分页五、执行任意的SQL语句一、概述 按照MVC的架构&a…...
数据结构期末复习总结(前章)
作者的话 作为一名计算机类的学生,我深知数据结构的重要性。在期末复习前,我希望通过这篇博客给大家一些复习建议。希望能帮助大家夯实数据结构的基础知识,并能够更好地掌握数据结构和算法的应用。 一、绪论 数据:信息的载体&am…...
设计环形队列
文章目录1.思路分析1.1队列空满分析1.2出队分析2.循环队列设计1.思路分析 1.1队列空满分析 首先我们假设一个长度为4的环形队列 队头front 队尾rear 当队列为空时 frontrear 当队列满时 frontrear 所以我们无法判断队列是满的或者空的 因此我们多加入一个空间使队列长度为5&am…...
面向对象之-接口鉴权
1 需求 1.1 需求背景 为了保证接口调用的安全性,我们希望设计实现一个接口调用鉴权功能,只有经过认证之后的系统才能调用我们的接口,没有认证过的系统调用我们的接口会被拒绝。 2 需求分析 2.1 基础分析 对于如何做鉴权这样一个问题&…...
Python 多进程多线程线程池进程池协程
目录 一、线程与进程很简单的介绍 1.1 线程与进程的区别 二、多进程Process 2.1 多进程与多线程的区别 2.2 多进程为啥要使用队列 2.3 控制进程运行顺序 2.3.1 join , 2.3.1 daemon 守护进程 2.4 进程id 2.5 进程 存活状态is_alive() 2.5 实现自定义多…...
【自然语言处理】基于句子嵌入的文本摘要算法实现
基于句子嵌入的文本摘要算法实现人们在理解了文本的含义后,很容易用自己的话对文本进行总结。但在数据过多、缺乏人力和时间的情况下,自动文本摘要则显得至关重要。一般使用自动文本摘要的原因包括: 减少阅读时间根据摘要,选择自…...
fiddler抓包
一、工具介绍Fiddler是一个通过代理的方式来进行抓包工具,运行时会在本地建立一个代理服务,默认地址:127.0.0.1:8888。Fiddler开启之后,配置本机代理,再打开IE浏览器,IE的PROXY会自动变成127.0.0.1:8888&am…...
【Linux】网络套接字编程
前言 在掌握一定的网络基础,我们便可以先从代码入手,利用UDP协议/TCP协议进行编写套接字程序,明白网络中服务器端与客户端之间如何进行连接并且通信的。 目录 一、了解源目的IP、端口、网络字节序、套接字 端口号: 套接字&…...
break与continue关键字
1.概述 不知道大家有没有这样一种感受哈,有的时候容易混淆break语句和continue语句的用法,总是模棱两可,不敢确定自己是否使用正确了。正好,我们本篇的重点就是break和continue关键字的用法。 2.使用场景 Java中为啥会诞生break…...
kafka使用入门案例与踩坑记录
每次用到kafka时都会出现各种奇怪的问题,综合实践,下面汇总下主要操作步骤: Docker镜像形式启动 zookeeper启动 docker run -d --name zookeeper -p 2181:2181 -t wurstmeister/zookeeperkafka启动 docker run --name kafka01 -p 9092:909…...
系统启动太慢,调优后我直呼Nice
问题背景最近在负责一个订单系统的业务研发,本来不是件困难的事。但是服务的启动时间很慢,慢的令人发指。单次启动的时间约在10多分钟左右,基本一次迭代、开发,大部分的时间都花在了启动项目上。忍无可忍的我,终于决定…...
java知识点
文章目录异常写法JVM加载反射访问private调用方法动态代理注解元数据:TargetRetention元注解泛型编写泛型擦拭法局限通配符无限定通配符(<?>)集合重写方法和实现类IO流字节与字符转换同步和异步可以设置编码的类Print*类Files时间与日期时区一种二种三种异常…...
文件的打开关闭和顺序读写
目录 一、文件的打开与关闭 (一)文件指针 (二) 文件的打开和关闭 二、文件的顺序读写 (一)fputc 1. 介绍 2. 举例 (二)fgetc 1. 介绍 2. 举例1 3. 举例2 (三&…...
(十八)操作系统-进程互斥的软件实现方法
文章目录一、知识总览二、单标志法三、双标志先检查法四、双标志后检查法五、Peterson算法六、总结一、知识总览 二、单标志法 算法思想:两个进程在访问临界区后,会把使用临界区的权限转交给另一个进程。也就是说每个进程进入临界区的权限只能被另一个进…...
2023年三月份图形化一级打卡试题
活动时间 从2023年3月1日至3月21日,每天一道编程题。 本次打卡的规则如下: 小朋友每天利用10~15分钟做一道编程题,遇到问题就来群内讨论,我来给大家答疑。 小朋友做完题目后,截图到朋友圈打卡并把打卡的截图发到活动群…...
linux 防火墙管理-firewalld
什么是Firewalld 当前很多linux系统中都默认使用 firewalld(Dynamic Firewall Manager of Linux systems,Linux系统的动态防火墙管理器)服务作为防火墙配置管理工具。 “firewalld”是firewall daemon。它提供了一个动态管理的防火墙&#x…...
2023年最新大厂开发面试题(滴滴,华为,京东,腾讯,头条)
2023年最新大厂开发面试题!!! 滴滴篇 B树、B-树的区别? 数据库隔离级别,幻读和不可重复读的区别? 有 hell, well, hello, world 等字符串组,现在问能否拼接成 helloworld,代码实现。 快排算…...
2023年三月份图形化三级打卡试题
活动时间 从2023年3月1日至3月21日,每天一道编程题。 本次打卡的规则如下: 小朋友每天利用10~15分钟做一道编程题,遇到问题就来群内讨论,我来给大家答疑。 小朋友做完题目后,截图到朋友圈打卡并把打卡的截图发到活动群…...
蓝桥杯算法模板
模拟散列表拉链法import java.io.*; import java.util.*; public class a1 {static int n;static int N100003;static int[] hnew int[N];static int[] enew int[N];static int[] nenew int[N]; static int idx; static void insert(int x){int k(x%NN)%N;e[idx]x;ne[idx]h[k];…...
python之并发编程
一、并发编程之多进程 1.multiprocessing模块介绍 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程。Python提供了multiprocessing。 multiprocess…...
Vue.js自定义事件的使用(实现父子之间的通信)
vue v-model修饰符:.lazy、.number、.trim $attrs数据的透传,在组件(这个是写在App.vue中),数据就透传到student组件中,在template中可以直接使用{{$attrs.students}}获取数据 通过defineProps定义的属性在attrs中就…...
第12天-商品维护(发布商品、商品管理、SPU管理)
1.发布商品流程 发布商品分为5个步骤: 基本信息规格参数销售属性SKU信息保存完成 2.发布商品-基本信息 2.1.会员等级-会员服务 2.1.1.会员服务-网关配置 在网关增加会员服务的路由配置 - id: member_routeuri: lb://gmall-memberpredicates:- Path/api/member/…...
最新seo快排技术qq/广州seo工作
<题目链接> 题目大意: 在一个节点标号为1~n的无向图中,求出一条1~n的路径,使得路径上的第K1条边的边权最小。 解题分析:直接考虑情况比较多,所以我们采用二分答案,先二分枚举第K1条路的边权ÿ…...
不买服务器做网站/seo搜索引擎优化步骤
转载:http://www.cnblogs.com/duanxz/p/3511695.html java类中serialversionuid 作用 是什么?举个例子说明 serialVersionUID适用于Java的序列化机制。简单来说,Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的。在进行反序列化时&a…...
网站的日志文件/磁力狗最佳搜索引擎
1、C中有两个方面体现重用: (1)面向对象的思想:继承和多态,标准类库。 (2)泛型程序设计(generic programming) 的思想: 模板机制,以及标准模板库 STL。 将一些常用的数据…...
河南郑州app建设网站/网络推广外包内容
Visual Studio 中预定于修饰层的实际显示优先级。转载于:https://www.cnblogs.com/NanaLich/archive/2013/05/20/3088438.html...
威海千淼网站建设/互联网营销师有什么用
题目:(最小操作次数使数组元素相等)给你一个长度为 n 的整数数组,每次操作将会使 n - 1 个元素增加 1 。返回让数组所有元素相等的最小操作次数。 示例 1: 输入:nums [1,2,3] 输出:3 解释&…...
企业网站建设及前期准备/百度题库
这里是吧容器的本地日志目录挂载filebeat,然后filebeat 读取日志写入到kafka --- apiVersion: v1 kind: ConfigMap metadata:name: filebeat-confignamespace: kube-systemlabels:k8s-app: filebeat data:filebeat.yml: |-filebeat.inputs:- type: logpaths:- /var/lib/docke…...