框架5:SpringBoot 2 - 核心功能

SpringBoot2 - 基础入门【一 ~ 五】,详见:

六、配置文件

6.1 properties文件格式

同之前的用法。

6.2 yaml文件格式【推荐】

YAML本意:“YAML”不是一种标记语言。但在开发中,实际把它理解为:“Yet Another Markup Language”(仍是一种标记语言)
戏称为:薛定谔的YAML

非常适合以数据为中心的配置文件
后缀:.yaml 或 .yml

【语法】
(1) key: value \color{red}{冒号后一定要加空格!}
 ● 对于数组、List、Set的每个元素:
  ● 行内写法:[x1,x2,x3, ...]
  ● 层次写法:每一层使用 - x1
 ● 对于Map、Object的每个元素:
  ● 行内写法:{key1: value1, key2: value2, ...}
  ● 层次写法:空格缩进后,每一层直接写key : value

(2) 使用空格缩进表示层级关系\color{blue}{比properties更清晰}
  ● 数量不重要,只要相同层级对齐即可
  ● 不允许使用tab,只允许空格,但在IDE中我们不用关心

(3) 字符串我们可以不用加引号
 ● '...' 单引号:表示保留字符串原始内容,尽管是转义字符,也不会进行转义
 ● "..." 双引号:表示会将转义字符转义

(4) 大小写敏感

(5) #表示注释

e.g
@Data
public class Person{private String userName;private Boolean boss;private Date birth;private Integer age;private Pet pet;private String[] interests;private List<String> animals;private Map<String, Object> scores;private Set<Double> salarys;private Map<String, List<Pet>> allPets;
}
@Data
class Pet{private String name;private Double weight;
}application.yml
person: userName: zhangsanboss: truebirth: 2019/12/9age: 18interests: - 篮球- 足球- 18动漫animals: [阿猫, 阿狗]scores: english: 80math: 90salarys: - 9999.98- 9999.99pet: name: 阿狗weight: 99.99allPets: sick: - {name: 阿狗, weight: 99.99}- name: 阿猫- weight: 88.88- name: 阿虫- weight: 77.77health: - {name: 阿花, weight:199.99}- {name: 阿明, weight:11.55}

⭕ 自定义类绑定的配置提示(配置处理器)

<dependency>  <groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><!-- 某个依赖项是可选的。这意味着当其他项目依赖于该项目时,该依赖项不会自动传递给其他项目,除非其他项目也显式地声明了该依赖项。 --><optional>true</optional>
</dependency><!-- 官方建议:移除这个配置处理器,新版本SpringBoot自动移除了 -->
<build><plugins><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin<artifactId><configuration><excludes><exclude><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor<artifactId></exclude></excludes></configuration></plugins>
</build>

七、Web开发

7.1 静态资源

7.1.1 存放静态资源的目录

(1) 默认情况下,当用户键入:当前项目根路径/ + 静态资源文件
SpringBoot就会在项目的/static(或/public、/resources、/META-INF/resources)目录下查找静态资源

● 原因:默认的静态资源映射路径为:/**
当请求进来后,先会去Controller处理器中看看能不能处理,如果不能处理,则交给默认的静态资源处理器去查找

(2) 修改存放静态资源的目录
SpringBoot会在你修改后的目录中找静态资源

application.yaml
spring:resources:static-locations: [classpath:/myResource/]add-mappings: false # 这里可以禁用查找静态资源(默认是true)
7.1.2 静态资源的映射路径

(1) 默认情况下:无前缀(即/**)

(2) 修改静态资源的映射路径【推荐】
这样当我们后续设置Filter时,不会拦截到静态资源

application.yaml
spring:mvc:static-path-pattern: /res/**

● 当用户键入:当前项目 + static-path-pattern + 静态资源名 = SpringBoot就会前往静态资源目录中寻找对应的资源

7.1.3 webjar(了解)

一些第三方静态资源也打成了jar包,当需要获取时,可以通过加上前缀:/webjar/...


访问jQuery示例:http://localhost:8080/webjars/jquery/3.5.1/jquery.js
7.1.4 欢迎页和自定义Favicon

(1) 欢迎页
当用户键入项目根目录时,Spring Boot会:
● 先找ReqeustMappingHandlerMapping【保存了我们所有controller的映射规则】,即查找@RequestMapping("/")的处理器;
● 找不到,再去找WelcomePageHandlerMapping查找静态资源目录下的欢迎页作为默认首页。还找不到,才会去寻找模板,即对应的Controller中写了@RequestMapping("/index")并返回"index"视图名的处理器方法

源码

⭕ 注意:当配置了自定义的静态资源的映射路径时,用户键入项目根路径将无法找到index.html这类静态资源。

(2) favicon
在静态资源目录下,放入名为"favicon.ico"的静态资源,则会自动的将icon显示,作为网页的图标

⭕ 注意:当配置了自定义的静态资源的映射路径时,用户键入项目根路径将无法显示favicon.ico图标。

7.2 请求参数处理

7.2.1 支持Rest风格的HiddenHttpMethodFilter \color{blue}{注意}

在SpringBoot的WebMvcAutoConfiguration自动配置类中,已经帮我们自动配置了一个该过滤器对象,但是它需要配置文件的支持!

底层源码

如果是表单中使用,需要手动开启HiddenHttpMethodFilter!

application.yaml
spring: mvc: hiddenmethod: filter: enabled: true

因为有很多客户端(如Postman),可以发送DELETE、PUT等请求,这一项是选择性开启。

7.2.2 自定义转换器

(若干个)参数解析器中大多都会有一个类WebDataBinder,它用来做数据绑定,里面有一个conversionService对象,注册了(若干)转换器converter,用来做数据类型的转换

⭕ 假设现在有一个需求:表单中提交name="pet",value="阿猫, 18";服务器(SpringBoot)的handler中参数接收一个Pet类型的数据。
默认converter没有办法处理这种String→Pet的转换,会报错
● 我们自定义一个converter,并把它注册进容器中。

    //1、WebMvcConfigurer定制化SpringMVC的功能@Beanpublic WebMvcConfigurer webMvcConfigurer(){return new WebMvcConfigurer() {@Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {UrlPathHelper urlPathHelper = new UrlPathHelper();// 不移除;后面的内容。矩阵变量功能就可以生效urlPathHelper.setRemoveSemicolonContent(false);configurer.setUrlPathHelper(urlPathHelper);}@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;}});}};}

7.3 数据响应和内容协商

数据响应分类
7.3.1 原理
HttpMessageConverter规范

SpringBoot【处理@ResponseBody响应】中,根据handler返回值类型(假设是自定义Person类)确定一个返回值处理器(有若干个),在这个处理器内部,先获取浏览器可以接收的数据类型,然后去(第一次)遍历HttpMessageConverter,根据【canWrite方法】,拿到所有可以将Person写成别的数据类型的转换器,将这些转换器支持的MediaType保存起来,即获取服务器可以写出的数据类型,然后做浏览器和服务器的【内容协商】,确定最终匹配的MediaType(可能有多个),最后,(第二次)遍历MessageConverter,仍然根据【canWrite方法】,找到含有最终匹配MediaType的这些转换器。然后按照优先级排序,选出【第一个】可以将Person转换成最终的MediaType的转换器,使用它将Person转换为最终格式并返回给浏览器。
● 第一次遍历HttpMessageConverter:服务器具备写哪些数据格式的能力
● 第二次遍历HttpMessageConverter:浏览器和服务器协商后,找到可以对应的转换器

⭕ 另:canRead:带有@RequestBody的请求参数值是否可以读成handler形参中的类型。

7.3.2 如何响应Json和xml

响应json:

● json相关依赖 + @ResponseBody

(1) json相关依赖
引入spring-boot-starter-web场景启动器,内部就含有spring-boot-starter-json场景启动器,主要是通过jackson来实现的。

(2) 在handler方法或者类上标注@ResponseBody

响应xml:

(1) 引入xml依赖
由于spring-boot-starter-web中没有内置对xml的解析支持,所以需要引入额外的依赖。

 <dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-xml</artifactId>
</dependency>

(2) 在handler方法或者类上标注@ResponseBody

7.3.3 开启浏览器:使用请求参数,来进行内容协商
spring:contentnegotiation:favor-parameter: true  #开启请求参数内容协商模式

发请求:(带上键为"format"的请求参数,如:json)
http://localhost:8080/test/person?format=json
http://localhost:8080/test/person?format=xml

内容协商管理器
参数内容协商管理器支持的数据类型(想要协商其他的可以自定义)
7.3.4 自定义MessageConverter(修改服务器需要的MediaType)

假定业务需求,有一种自定义的输出格式"application/myFormat",它要求将Person的属性值按照分号间隔,返回给客户端。

在@Confiugration配置类中添加WebMvcConfig的Bean组件,并在里面添加自定义的MessageConverter功能。

省略了Person的JaveBean@RestController
public class testController{@GetMapping("/test")public Person getPerson(){Person p = new Person("张三", 18);return p;}
}// 自定义的转换器
class MyMessageConverter implements HttpMessageConverter<Person>{@Overridepublic boolean canRead(Class<?> clazz, MediaType mediaType){return false; // 这部分是@requestBody需要做的,我们现在不关心}// 自定义判断是否可写@Overridepublic boolean canWrite(Class<?> clazz, MediaType mediaType){return clazz.isAssignableFrom(Person.class);}@Overridepublic List<MediaType> getSupportMediaTypes(){return MediaType.parseMediaType("application/myFormat"); // 自定义MediaType,即可以处理的数据类型}@Overridepublic Person read(Class<? extends Person> clazz, HttpInputMessage inputMessage)throws IOException{return null; // 这部分是@requestBody需要做的,我们现在不关心}// 自定义如何将Person写出 @Overridepublic Person write(Class<? extends Person> clazz, HttpOutputMessage outputMessage)throws IOException{String data = person.getUserName + ";" + person.getAge();OutputStream os = outputMessage.getBody()os.write(data.getBytes());}
}在我们的含有@Configuration配置类中自定义WebMvcConfig的Bean组件,对其中的功能(这里是添加MessageConverter)进行定制。@Beanpublic WebMvcConfigurer webMvcConfigurer(){return new WebMvcConfigurer() {// 这个方法是追加MessageConverter,并不会覆盖掉原来的@Overridepublic void extendMessageConverters(List<HttpMessageConverter<?>> converters) {converters.add(new MyMessageConverter()); // 添加自定义MessageConverter}}}
7.3.5 自定义ContentNegotiationStrategy(通过携带自定义参数,修改浏览器需要的MediaType)

开启【基于参数format的】,在WebMvcConfigurer里配置一个新的ParameterContentNegotiationStrategy

在我们的含有@Configuration配置类中自定义WebMvcConfig的Bean组件,对其中的功能(这里是覆盖内容协商管理器的策略)进行定制。@Beanpublic WebMvcConfigurer webMvcConfigurer(){return new WebMvcConfigurer() {// 配置自定义内容协商器@Overridepublic void configureContentNegotiation(ContentNegotiationConfigurer configurer) {Map<String, MediaType> mediaTypes = new HashMap<>();map.put("json", MediaType.APPLICATION_JSON);map.put("xml", MediaType.APPLICAION_XML);map.put("myFormat", MediaType.parseMediaType("application/myFormat")); // 自定义MediaType数据格式ParameterContentNegotiationStrategy strategy = new ParameterContentNegotiationStrategy(mediaTypes);configurer.strategies(Arrays.asList(strategy)); //【这里会覆盖原有的内容协商管理器的策略,可以使用别的方法,实现新增】// configurer.strategies(Arrays.asList(strategy, new HeaderContentNegotiationStrategy());}}}

● 使用这种方法会覆盖原有的请求头内容协商策略,导致默认功能失效,我们尽量修改的【原则是新增,而不是覆盖】!(参照SpringBoot用别的方法开发自定义内容协商部分)

7.4 视图解析与模板引擎

SpringBoot默认不支持JSP(需要服务器解压,并提供Java编译器,带来额外的负担),需要引入第三方模板引擎技术(自身的模板引擎技术是支持JSP的)。

● 引入Thymeleaf模板引擎场景启动器

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency>
</dependencies>

它帮我们配置了ThymeleafViewResolver,并绑定到了ThymeleafProperties类。


默认前缀、后缀
7.4.1 视图解析原理

(1) 所有的handler执行完成后会返回ModelAndView对象
(2) DispatcherServlet调用processDispatchResult方法,遍历各个视图解析器根据视图名称确定可以处理的解析器;
(3) 解析器会给我们返回View对象(View是接口,里面有render方法定义了渲染逻辑
(4) View调用render方法渲染视图
e.g:
● RedirectView:render方法最终会调用:

response.sendRedirect("浏览器解析的路径");

● InternalResourceView:render方法最终会调用:

request.getRequestDispatcher("服务器解析的路径").forward(request, response);

● TymeleafView:有自己的渲染规则...

7.5 拦截器 Interceptor

通常情况下,我们除了登录页面之外,其他页面的都需要用户登录后才能进入,我们会使用拦截器实现这个功能。

(1) 自定义拦截器

/*** 登录检查* 1、配置好拦截器要拦截哪些请求* 2、把这些配置放在容器中*/
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {/*** 目标方法执行之前* @param request* @param response* @param handler* @return* @throws Exception*/@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;}/*** 目标方法执行完成以后* @param request* @param response* @param handler* @param modelAndView* @throws Exception*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {log.info("postHandle执行{}",modelAndView);}/*** 页面渲染以后* @param request* @param response* @param handler* @param ex* @throws Exception*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {log.info("afterCompletion执行异常{}",ex);}
}

(2) 将拦截器添加注册到WebMvcConfigurer中

/*** 1、编写一个拦截器实现HandlerInterceptor接口* 2、拦截器注册到容器中(实现WebMvcConfigurer的addInterceptors)* 3、指定拦截规则【如果是拦截所有,静态资源也会被拦截】*/
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**")  //所有请求都被拦截包括静态资源.excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**"); //放行的请求}
}

拦截器的原理详见SpringMVC:框架3:SpringMVC - 简书 (jianshu.com)

7.6 文件上传

SpringBoot中帮我们自动配置了【MultipartAutoConfiguration】文件上传自动配置类,它里面帮我们配置好了StandardServletMultipartResolver文件上传解析器,并和MultipartProperties绑定(我们可以通过修改application的全局配置文件来修改,诸如:单个文件上传最大大小、整个请求上传的最大大小...)。

@RequestPart注解:当有多个文件需要上传时,显式指定提交表单项的"name"名称,用于确定哪一个上传的文件。
(当然如果页面中使用<input type="file" name="files" multiple/>,handler中可以使用形参MultipartFile[]接收)

7.6.1 文件上传源码分析

(1) DispatcherServlet中的doDispach()方法,接收请求,然后会使用multipartResolver文件上传解析器判断当前是不是一个分段请求(即文件上传请求),如果是:会将当前Request封装成新的MultipartHttpServletRequest对象
⭕ 具体的判断条件:

multipartResolver.isMultipart(request);
↓
StringUtils.startWithIgnoreCase(request.getContentType(), prefix:"multipart/");

\color{red}{本质:判断request对象里的ContentType的值是否以"multipart/"为前缀!文件上传ContentType为"multipart/form"!}
(2) 然后DispatcherServlet再去找到对应的HandlerMapping,做后续的处理……
(3) 在使用HandlerAdapter处理器适配器去执行handler时,匹配到RequestPartMethodParameterResolver参数解析器,会把封装过的request对象中的文件封装成MultipartFile对象,放到handler的形参列表中,对应的handler就可以进行处理了。

7.7 异常处理

SpringBoot在默认的情况下,对于来自机器客户端的错误,将生成JSON响应,其中包括错误、HTTP状态、异常消息的详细信息;对于浏览器的错误,会响应一个"whitelabel"错误视图,以HTML格式呈现同样的数据。

两种默认的响应方式
7.7.1 ErrorMvcAutoConfiguration

这是SpringBoot为异常处理提供的自动配置类。里面主要包含三大组件:

(1) DefaultErrorAttributes——handlerExceptionResolver实现类之一
● 作用:它主要是用来定义错误页面可以包含哪些数据,并将它保存到request域中,以便在错误页面展示。
\color{red}{它是比较特殊的异常处理解析器,不会返回ModelAndView对象(null),仅仅做异常信息保存,异常会继续交给下一个解析器}

(2) BasicErrorController——handler
● 作用:可以理解为是一个普通的handler,但他是专门处理默认的"/error"请求默认情况下(没有任何的自定义处理异常),会使用SpringBoot放在容器中的默认错误页,即id为"error"的View组件【这就是浏览器错误白页的由来】。\color{red}{这是SpringBoot错误页面的最后一道屏障,由SpringBoot支持的。没有它,只能显示原生Tomcat的错误页面。}

(3) DefaultErrorViewResolver——视图解析器
● 作用:ErrorMvcAutoConfiguration默认配置,专门用来处理异常视图的解析器。它首先会先根据状态响应码,对状态码进行匹配。先进行精确匹配,如"404";如果匹配不到,则进行序列匹配,如"4xx"、"5xx"【可以在"/error"目录下,自定义4xx、5xx、400等html页面】。如果找不到,则解析上述的"error"默认错误视图\color{red}{无论是否自定义,都是由这个解析器最后对视图进行解析。我们一般不会自定义新的异常解析器}

7.7.2 异常处理步骤

(1) 执行目标方法,如果方法运行期间出现异常,会被捕获,并标志请求结束。
(2) 进入视图解析阶段,调用processDispatchResult(request, response, handler, mv, exception)方法,派发返回页面。
 ● 遍历所有的handlerExceptionResolver,看谁能处理当前异常。【HandlerExceptionResolver是一个接口,它里面只定义了一个方法,即resolveException,如何处理异常,返回类型是ModelAndView】

系统默认的异常解析器

   a) 第一个执行的解析器是 DefaultErrorAttributes,它仅 定义了将哪些错误信息放到request域,供错误页面使用,返回ModelAndView为null。
   b) 执行第二个解析器,该解析器中定义了三个自带的异常处理解析器。
    ● ExceptionHandlerExceptionResolver:携带了注解@ExceptionHandler,并能够处理当前异常的解析器【它就是@ControllerAdvice + @ExceptionHandler 的底层支持】
    ● ResponseStatusExceptionResolver:携带了注解@ResponseStatus,并能够处理当前异常的解析器【它就是@ResponseStatus 的底层支持】
    ● DefaultHandlerExceptionResolver:SpringBoot专门用来处理内部异常的解析器,如参数不存在...
   c) 自己实现了HandlerExceptionResolver接口的解析器类【可以通过@Order来更改优先级】

 ● 只要上述任意一个异常处理解析器返回的ModelAndView对象不为空,则直接跳出。如果找到了解析器,那么就使用该解析器解析,处理异常;如果所有的异常处理解析器都无法处理,那么会直接将异常抛出去,给Tomcat处理。但是SpringBoot在这里进行了封装,它不会直接由Tomcat的默认错误页处理,而是会转发"/error"请求,被SpringBoot中的BasicErrorController组件处理

7.7.3 自定义异常处理

● 方式一:自定义错误页
在静态资源目录,或者templates目录下,创建一个"/error"目录,里面存放"404"、"4xx"、"5xx"等以状态码命名的html页面。DefaultErrorViewResolver会先找这样的错误模板页,如果找不到才使用默认的错误视图"error"。

● 方式二:@ControllerAdvice + @ExceptionHandler【推荐】
 它可以:自定义如果解析异常信息,并返回ModelAndView。底层由ExceptionHandlerExceptionResolver支持。

● 方式三:@ResponseStatus 自定义状态码,以及详细的错误信息
 它不能定义如何解析这个异常以及返回ModelAndView,只能手动标识一个HTTP状态码和详细错误信息【可以将注解加到控制器方法或异常类上】。最终还是会调用response.sendError(statusCode, revolcedReason),即转发"/error"请求(由BasicErrorController处理,由DefaultErrorViewResolver解析)。可以通过这个搭配自定义错误页(方式一)使用!底层由ResponseStatusExceptionResolver支持。

@ResponseStatus
● value/code:类型HttpStatus,它是由Spring框架提供的枚举类型,用于表示HTTP响应码
● reason:自定义的相应信息
(没有办法自己创建一个HttpStatus对象)

● 方式四:实现HandlerExceptionResolver接口的解析器类
实现里面的resolveException方法,并返回ModelAndView对象。【用的比较少】,因为它可以更改优先级,会改变SpringBoot默认的处理异常的顺序。

7.8 Web原生组件 和 嵌入式Servlet容器

7.8.1 注入Servlet、Filter、Listener

(1) 方式一:@ServletComponentScan(basePackage="...") + ( @WebServlet / @WebFilter / @WebListener )
如,可以像原生Servlet一样书写,并且在类上标注@WebServlet,然后在主程序类(主配置类)上加上@ServletComponentScan注解,如果不写basePackage,则默认是主配置类所在包目录

(2) 方式二:使用RegistrationBean
可以在@Configuration配置类中,添加@Bean组件——ServletRegistrationBean、FilterRegistrationBean、ServletListenerRegistrationBean

@Configuration
public class MyRegistConfig {@Beanpublic ServletRegistrationBean myServlet(){MyServlet myServlet = new MyServlet();return new ServletRegistrationBean(myServlet,"/my","/my02");}@Beanpublic FilterRegistrationBean myFilter(){MyFilter myFilter = new MyFilter();
//        return new FilterRegistrationBean(myFilter,myServlet());FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/css/*"));return filterRegistrationBean;}@Beanpublic ServletListenerRegistrationBean myListener(){MySwervletContextListener mySwervletContextListener = new MySwervletContextListener();return new ServletListenerRegistrationBean(mySwervletContextListener);}
}
7.8.2 嵌入式Servlet容器

SpringBoot默认支持的WebServer:Tomcat、Jetty、Undertow

(1) 原理
SpringBoot应用在启动时,检测到是Web应用,就会创建一个ServletWebServerApplicationContext的IOC容器
● 该容器启动时,会寻找ServletWebServerFactory
● SpringBoot底层拥有ServletWebServerFactoryAutoConfiguration自动配置类。该配置类中装配了Tomcat、Jetty、Undertow这些Servlet容器。并不会全部生效!导入了哪个,生效哪个!
● 生效的ServletWebServerFactory会创建对应的Servlet容器,并启用它。

(2) 如何切换服务器
默认的web场景启动器,内置了Tomcat的Servlet容器。可以排除它,并引入其他的Servlet容器。

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><!-- 排除Tomcat --><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></exclusion></exclusions></dependency><!-- 引入undertow --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-undertow</artifactId></dependency>
</dependencies>

八、数据访问

SpringBoot中有关数据访问的场景启动器:【spring-boot-starter-data-*】

8.1 JDBC场景

8.1.1 引入 场景启动器 + 驱动 依赖

(1) 导入spring-boot-starter-data-jdbc

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>   

里面内置了HikariCp连接池这个数据源,以及Spring中支持jdbc、事务相关的包

jdbc场景

(2) 引入驱动的依赖
⭕ 为什么官方不给我们提供在jdbc场景中呢?——因为它不知道我们要使用哪个数据库!所以我们需要自己引入

<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><!--<version>5.1.49</version>-->
</dependency>想要修改版本
1、直接依赖引入具体版本(maven的就近依赖原则)
2、依照parent的properties重新声明版本(maven的属性的就近优先原则)<properties><java.version>1.8</java.version><mysql.version>5.1.49</mysql.version></properties>

spring-boot-starter-parent里面给我们管理了主流的驱动依赖。我们可以不用写版本,但是一定要看看驱动版本和我们自己用的数据库对不对应的上!

● 配置驱动的连接信息

spring:datasource:url: jdbc:mysql://localhost:3306/db_accountusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driver

(3) 测试

@Slf4j
@SpringBootTest
class Boot05WebAdminApplicationTests {@AutowiredJdbcTemplate jdbcTemplate;@Testvoid contextLoads() {//        jdbcTemplate.queryForObject("select * from account_tbl")
//        jdbcTemplate.queryForList("select * from account_tbl",)Long aLong = jdbcTemplate.queryForObject("select count(*) from account_tbl", Long.class);log.info("记录总数:{}",aLong);}}
8.1.2 DataSource的自动配置类

● DataSourceConfiguration
 数据库连接池的自动配置类,和DataSourceProperties绑定(prefix="spring.datasource"),当容器中没有自己的DataSource时才自动配置
底层配置好的连接池:HikariDataSource
● DataSourceTransactionManagerAutoConfiguration
 事务管理器的自动配置类
● JdbcTemplateAutoConfiguration
 JdbcTemplate的自动配置类,可以用来对数据库进行crud操作。

8.2 使用Druid数据源(第三方)

8.2.1 自定义整合

(1) 引入Druid连接池数据源

<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.17</version>
</dependency>

(2) 给Spring容器中添加DruidDataSource组件

● 使用【@ConfigurationProperties】,指定application全局配置文件的前缀,会自动地为当前组件的属性注入值。【底层:反射,需要依赖于无参构造+set】

@Configuration
public class MyDataSourceConfiguration{@ConfigurationProperties("spring.datasource")@Beanpublic DataSource dataSource(){DruidDataSource druidDataSource = new DruidDataSource();// druidDataSource.serUrl("...");// druidDataSource.serUsername("...");// druidDataSource.serPassword("...");return druidDataSource;}
}

(3) 添加Druid功能到我们的自动配置类
功能很多,比如:查询慢SQL记录等等...

● 基本原则:参照官方文档说明,只要有<bean>标签,就可以在配置类中添加一个@Bean组件进Spring容器,并在里面放好指定的属性,就可以完成功能。

官方链接:
https://github.com/alibaba/druid

8.2.2 starter整合

(1) 引入druid-spring-boot-starter

<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.17</version>
</dependency>

(2) 根据官方的自动配置类,配置文件

给个示例:application.yamlspring: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

8.3 整合MyBatis

https://github.com/mybatis

● 引入mybatis提供的场景启动器

mybatis-spring-boot-starter包含

可以在官方中查看pom.xml文件提取坐标信息,也可以使用Spring Initializer向导帮助我们创建MyBatis Spring Boot Application。

<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.4</version>
</dependency>
8.3.0 回顾原生mybatis整合spring

(1) mybatis自己的配置
● 在mybatis-config.xml全局配置文件中,指定mapper包路径
● 在XXXmapper.xml映射sql文件中,编写sql语句,并在namespace、sql标签id上绑定对应接口
通过sqlSessionFactory,创建sqlSession对象,调用getMapper方法时传入接口类名,创建代理对象,执行sql。

(2) 整合配置
需要在Spring容器中(Spring配置文件或者Spring配置类中),放入SqlSessionFactory组件(里面配置了dataSource信息、通过configLocation配置了mybatis-config.xml全局配置文件路径),并开启Mapper接口的扫描

而在springboot中,提供了三种模式供我们进行整合。

8.3.1 配置模式【重要】

(0) 导入mybatis官方starter
(1) 编写Mapper接口,标注@Mapper注解。【推荐】或者在SpringBoot主启动类中标注@MapperScan("Mapper接口的包路径")【AutoConfiguredMapperScannerRegistrar自动帮我们扫描该路径下的Mapper接口】
使用 @Mapper 注解时,需要在每个 Mapper 接口上都标注 @Mapper 注解。如果应用中的 Mapper 接口比较多,这样做会显得繁琐。此时,可以使用 @MapperScan 注解指定 Mapper 接口的扫描路径,以避免重复标注 @Mapper 注解。
(2) 编写sql映射文件并通过namespace、sql标签id,分别绑定接口类名、接口方法名。
(3) 在application.yaml中指定mybatis-config.xml全局配置文件路径,和mapper映射文件位置。

application.yaml# 配置mybatis规则
mybatis:
#  config-location: classpath:mybatis/mybatis-config.xml# 这里配置了,mybatis全局配置中就不用指定mapper的包路径了,它的作用:创建代理对象时找到对应的接口mapper-locations: classpath:mybatis/mapper/*.xml configuration:map-underscore-to-camel-case: true

⭕ 注意:
● 在yaml中配置mybatis.configuration,相当于在mybatis-config.xml(MyBatis全局配置文件)中配置。只能二选一
● (1)只是帮我们把实现了接口的代理对象作为组件放入Spring的IOC容器中;(2)、(3)是mybatis中的逻辑,需要指定sql映射文件的位置、指定全局配置文件路径、以及两处绑定接口的操作。 \color{purple}{理解}

8.3.2 注解模式

直接省略Sql的映射文件,在接口方法上书写sql。

@Mapper
public interface CityMapper {@Select("select * from city where id=#{id}")public City getById(Long id);
}
8.3.3 混合模式(最佳实践)

● 简单方法直接注解方式
● 复杂方法编写mapper.xml进行绑定映射

8.4 整合MyBatis-Plus

MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

● 建议安装 MybatisX 插件
● 只需要我们的Mapper继承 BaseMapper 就可以拥有简单的crud能力
● 一些Service接口的实现类直接调用Dao方法,没有什么额外的业务逻辑。我们可以让它的接口实现 IService<T>,让该实现类继承 ServiceImpl<Mapper, Bean>(Mapper是操作数据库的Mapper接口,Bean是返回封装的JavaBean)

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService {ServiceImpl<Mapper, Bean>是Mybatis-Plus提供给我们的
}public interface UserService extends IService<User> {IService是Mybatis-Plus提供给我们的
}

65、数据访问-整合MyBatisPlus操作数据库_哔哩哔哩_bilibili

8.5 整合Redis

Redis是一个开源的(BSD许可),内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。

● 引入redis的场景启动器

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
</dependencies>

自动装配了RedisAutoConfiguration配置类,绑定了RedisProperties属性类(前缀为"spring.redis")
 ● 配置了 客户端连接工厂:Lettuce、Jedis(早期默认Jedis,高版本默认Lettuce)
 ● 配置了 RedisTemplate<Object, Object>组件。redisTemplate 是 【Spring Framework 提供的】一个用于【简化 Redis 操作的模板类】,它可以使用多种 Redis 客户端实现,包括 Jedis 和 Lettuce 等。
 ● 配置了 StringRedisTemplate组件,它的key、value都是String

● 高版本的redis场景启动器,默认的客户端是Lettuce,如果要切换客户端到Jedis

(1) 引入Jedis依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><!-- 这里可以排除Lettuce,不排除的话,相当于有两个客户端包。在application.yaml中配置一下使用的客户端为Jedis也可以 -->
</dependency><!--导入jedis-->
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId>
</dependency>
(2) applcaition.yaml全局指定redis客户端
spring:redis:host: r-bp1nc7reqesxisgxpipd.redis.rds.aliyuncs.comport: 6379password: lfy:Lfy123456client-type: jedisjedis:pool:max-active: 10

● 简单测试

class Test{@Testvoid testRedis(){ValueOperations<String, String> operations = redisTemplate.opsForValue();operations.set("hello","world");String hello = operations.get("hello");System.out.println(hello);}
}

九、单元测试

SpringBoot 2.2.0 版本开始引入JUnit5作为单元测试默认库。
SpringBoot 2.4 以上版本移除了默认对Vintage的依赖。如果需要兼容JUnit4,需要自行引入

● 引入测试场景启动器

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- 以下是对Junit4的兼容 --><dependency><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.hamcrest</groupId><artifactId>hamcrest-core</artifactId></exclusion></exclusions></dependency>
</dependencies>

在JUnit4中,我们在SpringBoot中测试:@SpringBootTest + @RunWith(SpringRunner.class)
在JUnit5中,我们在SpringBoot中测试:@SpringBootTest 即可。JUnit类具有Spring的功能。如可以使用@Autowired自动注入,使用@Transactional标注事务,测试完成后会自动回滚……

9.1 常用的测试注解

(1) @Test:无参数测试

(2) @ParameterizedTest:参数化测试
参数化测试时JUnit5很重要的一个新特性,它使得不同的参数多次运行测试成为了可能。
 ● @ValueSource
 ● @NullSource
 ● @EnumSource
 ● @CsvSource
 ● @MethodSource:表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流,并且方法是静态的)
⭕ 另外:它可以支持外部的各类入参。如:CSV,YML,JSON 文件甚至方法的返回值也可以作为入参。只需要去实现ArgumentsProvider接口,任何外部文件都可以作为它的入参。

@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");
}

(3) @RepeatedTest(次数):表示方法可重复执行的次数

(4) @DisplayName("名字"):为测试类或者测试方法设置展示名字

(5) @BeforeEach:在【每个】单元测试方法之前执行

(6) @AfterEach:在【每个】单元测试方法之后执行

(7) @BeforeAll:在【所有】单元测试方法之前执行,只执行一次

(8) @AfterAll:在【所有】单元测试方法之后执行,只执行一次

(9) @Tag("标签名"):设置单元测试的类别,可以在运行的时候筛选

(10) @Disabled:测试类或者测试方法不执行(报告方法跳过,而不是错误)

(11) @Timeout:当测试方法运行超出了时间会返回错误
● 可以设置时间单位
● 设置具体的时间数

(12) @ExtendWith:给测试类或者测试方法提供【扩展类】的引用
例如:
● @SpringBootTest:复合注解
 ● @ExtendWith(SpringExtension.class)——对接了Spring的测试驱动
 ● @BootstrapWith(SpringBootTestContextBootstrapper.class)

其他的注解:JUnit 5 User Guide

import org.junit.jupiter.api.Test; //注意这里使用的是jupiter的Test注解!!@DisplayName("我的测试类")
public class TestDemo {@Test@DisplayName("第一次测试")public void firstTest() {System.out.println("hello world");}

9.2 断言

断言(Assertions)是测试方法中的核心部分,是org.junit.jupiter.api.Assertions包下的静态方法。
在maven项目中进行test,会生成一个详细的测试报告。

(1) 简单断言
● 参数列表:期望值(Boolean类型的就不用期望值了)、实际运行值、String(message)(可以为空。当断言失败时,输出的消息)

简单断言

(2) 数组断言:判断两个数组长度和内容上是否相等
● 参数列表:期望值、实际运行值、String(message)(可以为空。当断言失败时,输出的消息)

assertArrayEquals(new int[]{1, 2}, new int[] {1, 2});

(3) 组合断言:多个断言组合起来,只有全部断言成功,才成功;否则,报告所有失败的断言【即所有断言均会执行,无论是否存在失败】
● 参数列表:String(heading,指定一个组合名字用于测试报告显示)、Executable可变形参

Executable是一个接口,支持Lambda表达式编写多个断言

assertAll("Math",() -> assertEquals(2, 1 + 1),() -> assertTrue(1 > 0));

(4) 异常断言:如果出现指定异常,才断言成功;否则失败
● 参数列表:期望值、Executable、String(message)

@Test
@DisplayName("异常测试")
public void exceptionTest() {ArithmeticException exception = Assertions.assertThrows(//扔出断言异常ArithmeticException.class, () -> {int i = 10 / 0;}, message:"业务逻辑居然正常运行?");}

(5) 超时断言:断定方法会在指定时间内运行完成
● 参数列表:期望值、Executable、String(message)

@Test
@DisplayName("超时测试")
public void timeoutTest() {//如果测试方法时间超过1s将会异常Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(500));
}

(6) 快速失败
fail方法,直接使得测试失败

@Test
@DisplayName("fail")
public void shouldFail() {fail("This should fail");
}

9.3 前置条件

JUnit5 中的前置条件(Assumptions)类似于断言,都会在不满足条件时,终止执行。
● 区别在于:断言会报告方法失败,而前置条件不满足会报告方法被跳过

@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"));}
}

assumeTrue 和 assumeFalse 确保给定的条件为 true 或 false,不满足条件会使得测试执行终止。
assumingThat 的参数是表示条件的布尔值和对应的 Executable 接口的实现对象。只有条件满足时,Executable 对象才会被执行

9.4 嵌套测试

● @Nested:加在测试类的【内部类】上。
 ● 测试【内部类】的方法,【会】驱动外部类的Before(After)Each/All方法;
 ● 测试【外部类】的方法,【不会】驱动内部类的Before(After)Each/All方法。

@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());}}}
}

十、指标监控

未来每一个微服务在云上部署以后,我们都需要对其进行监控、追踪、审计、控制等。SpringBoot就抽取了Actuator场景,使得我们每个微服务快速引用即可获得生产级别的应用监控、审计等功能。

笔记:08、指标监控 (yuque.com)
视频:77、指标监控-SpringBoot Actuator与Endpoint_哔哩哔哩_bilibili

十一、原理解析

11.1 多环境适配——Profile

生产环境、开发环境、测试环境等等...配置文件内容、配置类等可能会随着环境的不同而有不同的设置。

11.1.1 配置文件application.yaml相关

如果我们每次都在一个配置文件上修改,显得太麻烦了。SpringBoot允许我们使用多个配置文件。

● 默认的配置文件:application.yaml——任何时候都会加载;
● 自定义环境配置文件:application-{env}.yaml

激活自定义环境配置文件
 ● 方式一:在默认的配置文件中指定

假定自定义环境配置文件:application-myEnv1.yaml、application-myEnv2.yaml(1) 在默认配置文件中【激活某个】自定义的环境配置文件:
application.yaml 
spring.profiles.active = myEnv1(2) 在默认配置文件中,对profile进行分组,【激活组】:
spring.profiles.group.myEnv[0] = myEnv1
spring.profiles.group.myEnv[0] = myEnv2
如果在默认配置文件中:spring.profiles.active = myEnv,则两个配置文件都会生效

 ● 方式二:命令行激活(我们的spring-boot项目最终都可以被打包成一个jar包部署到服务器)
 可以在外部修改配置文件的任何信息

java -jar xxx.jar --spring.profiles.active=prod  --person.name=haha
11.1.2 @Profile条件装配

● 可以加在配置类,也可以加在方法上。可以指定在某个环境生效。(application.yaml中spring.profiles.active指定)

application.yaml
spring.profile.active: production@Configuration(proxyBeanMethods = false)
@Profile("production") 【默认是"default",可以写成数组形式】
public class ProductionConfiguration {// ...}

11.2 外部化配置

将数据不是写死在代码中,而是抽取成外部文件,集中管理。
SpringBoot中支持各种外部化配置,其中包括:Java Properties文件,YAML文件,环境变量和命令行参数。

举例:直接获取系统环境变量的值

\color{blue}{生效规则(按照先后顺序,后面的覆盖前面的):配置文件 → 外部命令行修改}
\color{blue}{配置文件:类路径 → 类路径下的/config → jar包所在目录 → jar包所在目录/config → jar包同级/config的直接子目录}

11.3 自定义starter

模拟源码编写自定义starter。

● 步骤

(1) 编写一个maven项目,用来作为myservice-spring-boot-starter,只写pom.xml,里面引入了MyServiceAutoConfiguration自动配置类的依赖(除了pom文件以外,没有任何其他的Java代码、配置文件)

(2) 编写一个maven项目,用来作为MyServiceAutoConfiguration自动配置类(被自定义starter引用),编写自动配置类(@Configuration + @EnableConfigurationProperties)、业务组件(@Bean + 条件装配)、MyServiceProperties类(@ConfigurationProperties + 绑定到自动配置类)

(3) 在MyServiceAutoConfiguration项目中resources目录下准备META-INF目录,存放spring.factories文件,里面编写项目启动时自动加载的配置类"xxx.xxx.EnableAutoConfiguration=MyServiceAutoConfiguration自动配置类的全类名"

(4) 在外部项目中,引入my-spring-boot-starter即可使用我们自定义的功能。

● myservice-spring-boot-starter:maven项目pom.xml
...
<!-- my-spring-boot-starter坐标 -->
<groupId>xxxx</groupId>
<artifactId>myservice-spring-boot-starter</artifactId>
<version>1.0</version>
...
<dependencies><dependency><!-- 引入MyAutoConfiguration自动配置类 --><groupId>xxx</groupId><artifactId>myservice-spring-boot-starter-autoconfigure</artifactId><version>1.0</version></dependency>
</dependencies>
...
● MyServiceAutoConfiguration项目myservice-spring-boot-starter-autoconfigure:maven项目pom.xml
...
<!-- 自己的坐标 -->
<groupId>xxx</groupId>
<artifactId>myservice-spring-boot-starter-autoconfigure</artifactId>
<version>1.0</version>
...// 逻辑业务组件
@Service
public class MyService{@Autowiredprivate MyServiceProperties myServiceProperties;public string sayHello(String name){return myServiceProperties.getHelloWord() + name;}
}@ConfigurationProperties(prefix="my.myService")
public class MyServiceProperties{private String helloWord;public String getHelloWord(){return helloWord;}
}application.yaml
my: myService: helloWord: hello~@Configuration
@EnableConfigurationProperties(MyServiceProperties.class)
public class MyServiceAutoConfiguration{@ConditionalOnMissingBean(MyService.class)@Beanpublic MyService myService(){MyService myService = new MyService();return myService;}
}在项目resources文件夹下创建/META-INF,创建spring.factories文件
spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
MyServiceAutoConfiguration全类名
● 自己测试myservice-spring-boot-starter,创建一个外部项目
pom.xml
...
<!-- 自己的坐标(略) -->
<!-- 引入我们自定义的starter -->
<dependencies><dependency><groupId>xxxx</groupId><artifactId>myservice-spring-boot-starter</artifactId><version>1.0</version></dependency>
</dependencies>
...@RestController
public class MyTestController{@Autowiredprivate MyService myService;@RequestMapping("/hello")public String sayHello(){return myService.sayHello("张三");}
}

11.4 原理总结

【创建】并【运行】SpringApplication。
● 【创建】:主要是通过spring.factories文件去创建一些关键组件。如:启动引导器(BootStrapppers)、IOC初始化器(ApplicationContextInitializer)、应用监听器(ApplicationListener)
● 【运行】:找到spring.factories中所有SpringApplicationRunListener,对每个阶段进行监听。包括准备环境(读取并绑定系统配置、外部配置源等等)、根据Web项目类型创建IOC容器刷新IOC容器(创建Bean组件)Runner启动(应用启动后会调用run方法)等等。

——以上ApplicationContextInitializer、ApplicationListener、SpringApplicationRunListener、Runner均可以自定义。

最后编辑于:2024-11-17 10:24:30


喜欢的朋友记得点赞、收藏、关注哦!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/483245.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

行为型设计模式之《责任链模式》实践

定义 责任链模式&#xff08;Chain Of Responsibility Pattern&#xff09;顾名思义&#xff0c;就是为请求创建一条处理链路&#xff0c;链路上的每个处理器都判断是否可以处理请求&#xff0c;如果不能处理则往后走&#xff0c;依次从链头走到链尾&#xff0c;直到有处理器可…

Vue前端开发-路由树配置

一个配置路由的文件由导入路由模块、创建路由对象和导出路由对象三个部分组成&#xff0c;在创建路由对象时&#xff0c;需要构建路由数组&#xff0c;路由数组中包括一级、二级和多级路由结构&#xff0c;因此&#xff0c;这种结构的路由配置&#xff0c;又称为路由树配置。 …

2.mysql 中一条更新语句的执行流程是怎样的呢?

前面我们系统了解了一个查询语句的执行流程&#xff0c;并介绍了执行过程中涉及的处理模块。 相信你还记得&#xff0c;一条查询语句的执行过程一般是经过连接器、分析器、优化器、执行器等功能模块&#xff0c;最后到达存储引擎。 那么&#xff0c;一条更新语句的执行流程又…

JavaScript根据数据生成柱形图

分析需求 // 定义一个数组来存储四个季度的数据 dataArray = []// 循环4次,获取用户输入的数据并存储到数组中 for i from 0 to 3// 获取用户输入的数据inputData = 获取用户输入的第(i + 1)季度的数据// 将数据存入数组dataArray[i] = inputData// 遍历数组,根据数据生成柱…

实验13 使用预训练resnet18实现CIFAR-10分类

1.数据预处理 首先利用函数transforms.Compose定义了一个预处理函数transform&#xff0c;里面定义了两种操作&#xff0c;一个是将图像转换为Tensor&#xff0c;一个是对图像进行标准化。然后利用函数torchvision.datasets.CIFAR10下载数据集&#xff0c;这个函数有四个常见的…

【AI系统】代数简化

代数简化 代数简化&#xff08;Algebraic Reduced&#xff09;是一种从数学上来指导我们优化计算图的方法。其目的是利用交换率、结合律等规律调整图中算子的执行顺序&#xff0c;或者删除不必要的算子&#xff0c;以提高图整体的计算效率。 代数化简可以通过子图替换的方式完…

多人聊天室项目 BIO模型实现

BIO模型聊天室项目大体设计 BIO编程模型 Acceptor是服务器端负责监听具体端口的Socket每有一个客户端Client连接到服务器端&#xff0c;Acceptor就创建一个新的线程Handler来处理客户端发送的消息每一个客户端都有一个唯一的Handler来对应处理其事务为保证线程安全&#xff0c…

腾讯云平台 - Stable Diffusion WebUI 下载模型

1&#xff09;进入控制台&#xff0c;点击算力连接 》 JupyterLab 2&#xff09;进入模型目录&#xff08;双击&#xff09; 3&#xff09;上传模型 例如&#xff1a;我要上传大模型

夜神模拟器+Charles+postern+Mgisk+TrustMeAlready实现抓包

[实测有用]夜神模拟器CharlesposternMgiskTrustMeAlready实现抓包 PS:此贴仅做为技术交流,禁止非法用途。 1.初始化条件 A.安装MUMU模拟器安卓12版本 B.按图示选择&#xff0c;设置好代理端口8889 C.查看本机IP地址 D.导出证书&#xff0c;安装配置&#xff0c;暂时保存…

【closerAI ComfyUI】物体转移术之图案转移,Flux三重控制万物一致性生图,实现LOGO和图案的精准迁移

更多AI前沿科技资讯,请关注我们:closerAI-一个深入探索前沿人工智能与AIGC领域的资讯平台 closerAIGCcloserAI,一个深入探索前沿人工智能与AIGC领域的资讯平台,我们旨在让AIGC渗入我们的工作与生活中,让我们一起探索AIGC的无限可能性! 【closerAI ComfyUI】物体转移术之图…

新质驱动·科东软件受邀出席2024智能网联+低空经济暨第二届湾区汽车T9+N闭门会议

为推进广东省加快发展新质生产力&#xff0c;贯彻落实“百县千镇万村高质量发展工程”&#xff0c;推动韶关市新丰县智能网联新能源汽车、低空经济与数字技术的创新与发展&#xff0c;充分发挥湾区汽车产业链头部企业的带动作用。韶关市指导、珠三角湾区智能网联新能源汽车产业…

vue+mars3d给影像底图叠加炫酷效果

话不多说&#xff0c;直接上效果图&#xff1a; 在这里墙体其实是有一个不太明显的流动效果 实现方式&#xff1a;这里我使用了PolylineEntityWallPrimitive&#xff0c;开始我用的是polygonEntity但是发现实现效果并不好&#xff0c;所有直接改用了线 map.vue文件&#xff1…

【模电】常见电路参数计算

1.恒流源输出电阻 2.射极电压跟随器输出电阻 3.差分放大电路 3.1差模特性 3.1.1差模输入电阻Rid 3.1.2差模输出电阻Ro 3.1.3差模电压增益Avd 3.2共模特性 3.2.1共模输入电阻Ric 3.2.2共模电压增益Avc 4.组合放大电路 4.1单级放大器 4.1.1微变等效电路 4.1.1.1共射级 4.1.…

Linux-虚拟环境

文章目录 一. 虚拟机二. 虚拟化软件三. VMware WorkStation四. 安装CentOS操作系统五. 在VMware中导入CentOS虚拟机六. 远程连接Linux系统1. Finalshell安装2. 虚拟机网络配置3. 连接到Linux系统 七. 虚拟机快照 一. 虚拟机 借助虚拟化技术&#xff0c;我们可以在系统中&#…

Kafka如何保证消息可靠?

大家好&#xff0c;我是锋哥。今天分享关于【Kafka如何保证消息可靠&#xff1f;】面试题。希望对大家有帮助&#xff1b; Kafka如何保证消息可靠&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Kafka通过多种机制来确保消息的可靠性&#xff0c;主要包…

ONVIF协议网络摄像机客户端使用gsoap获取RTSP流地址GStreamer拉流播放

什么是ONVIF协议 ONVIF&#xff08;开放式网络视频接口论坛&#xff09;是一个全球性的开放式行业论坛&#xff0c;旨在促进开发和使用基于物理IP的安全产品接口的全球开放标准。 ONVIF规范的目标是建立一个网络视频框架协议&#xff0c;使不同厂商生产的网络视频产品完全互通。…

javaweb_Day05

1.请求响应 1.1 概述 1.2 请求 1.2.1 请求参数 1.2.2 响应 2.分层解耦 2.1 三层架构 &#xff08;1&#xff09;代码分层 2.2 分层解耦 2.3 IOC&DI入门 &#xff08;1&#xff09;控制反转IOC &#xff08;2&#xff09;依赖注入DI &#xff08;3&#xff09;汇总 …

Stable Diffusion 3详解

&#x1f33a;系列文章推荐&#x1f33a; 扩散模型系列文章正在持续的更新&#xff0c;更新节奏如下&#xff0c;先更新SD模型讲解&#xff0c;再更新相关的微调方法文章&#xff0c;敬请期待&#xff01;&#xff01;&#xff01;&#xff08;本文及其之前的文章均已更新&…

[VUE]框架网页开发02-如何打包Vue.js框架网页并在服务器中通过Tomcat启动

在现代Web开发中&#xff0c;Vue.js已经成为前端开发的热门选择之一。然而&#xff0c;将Vue.js项目打包并部署到生产环境可能会让一些开发者感到困惑。本文将详细介绍如何将Vue.js项目打包&#xff0c;并通过Tomcat服务器启动运行。 1. 准备工作 确保你的项目能够正常运行,项…

网络分层模型( OSI、TCP/IP、五层协议)

1、网络分层模型 计算机网络是一个极其复杂的系统。想象一下最简单的情况&#xff1a;两台连接在网络上的计算机需要相互传输文件。不仅需要确保存在一条传输数据的通路&#xff0c;还需要完成以下几项工作&#xff1a; 发起通信的计算机必须激活数据通路&#xff0c;这包括发…