SpringBoot Web开发

SpringBoot3-Web开发

SpringBoot的Web开发能力,由SpringMVC提供。

Web开发的三种方式

方式处理过程注意事项实现效果
全自动直接编写控制逻辑全部使用自动给配置默认效果
手自一体@Configuration、 配置WebMvcConfigurer、 配置WebMvcRegistrations不要标注 @EnableWebMvc手动配置效果 手动设置部分功能 定义MVC底层组件
全手动@Configuration 配置WebMvcConfigurer标注@EnableWebMvc禁用自动配置效果 全手动设置

给容器中放一个配置类,使用@Configuration标注,让配置类继承WebMvcConfigurer但是不要标注@EnableWebMvc注解,可以实现手自一体的效果。

1、Web场景

自动配置

①整合web场景

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

starterspring-boot-starter引入了autoconfigure功能

@EnableAutoConfiguration注解使用@Import(AutoConfigurationImportSelector.class)批量导入组件,加载"META-INF/spring/%s.imports"文件中配置的所有组件。

④在自动导入的配置类中绑定了配置文件的一堆配置项,比如:

  • SpringMVC的所有配置以spring.mvc开头

  • Web场景通用的配置在spring.web

  • 文件上传配置在spring.servlet.multipart

  • 服务器的配置server比如:编码方式

默认效果
默认配置
  1. 包含了ContentNegotiatingViewResolverBeanNameViewResolver组件,方便视图解析。

  2. 默认的静态资源处理机制:静态资源放在static文件夹下即可直接访问。

  3. 自动注册了Converter,GenericConverter, Formatter组件,适配常见的数据类型转换和格式化需求。

  4. 支持HttpMessageConverters,可以方便返回json等数据类型。

  5. 注册MessageCodesResolver,方便国际化及错误消息处理。

  6. 支持静态index.html

  7. 自动使用ConfigurableWebBindingInitializer,实现消息处理、数据绑定、类型转换、数据校验等功能。

特别提示:

①如果想保持boot mvc的默认配置,并且自定义更多的mvc配置,如:interceptors,formatters, view controllers等,可以使用@Configuration注解添加一个WebMvcConfigurer类型的配置类,但不要标注@EnableWebMvc

②如果想保持boot mvc的默认配置,但要自定义核心组件实例,比如 RequestMappingHandlerMapping, RequestMappingHandlerAdapterExceptionHandlerExceptioonResolver,给容器中放一个WebMvcRegistrations组件即可。

③如果想全面接管Spring MVC,@Configuration标注一个配置类,并加上@EnableWebMvc注解,实现WebMvcConfigurer接口。


2、静态资源

WebMvcAutoConfiguration
生效条件

效果

放入了两个filter

HiddenHttpMethodFilter:页面表单提交Rest请求(GET、POST、PUT、DELETE)

FormContentFilter:表单内容Filter,GET(数据放URL后面)、POST(数据放请求体)请求携带数据,但是PUT和DELETE的请求数据会被忽略,FormContentFilter是为了解决这个问题的。

@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled")
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
    return new OrderedHiddenHttpMethodFilter();
}
​
@Bean
@ConditionalOnMissingBean(FormContentFilter.class)
@ConditionalOnProperty(prefix = "spring.mvc.formcontent.filter", name = "enabled", matchIfMissing = true)
public OrderedFormContentFilter formContentFilter() {
    return new OrderedFormContentFilter();
}

给容器中放入了WebMvcConfigurer组件,给SpringMVC添加各种定制功能,所有的功能最终会和配置文件进行绑定

WebMvcProperties:以spring.mvc开头的配置文件

WebProperties:以spring.web开头的配置文件

@Configuration(proxyBeanMethods = false)
@Import(EnableWebMvcConfiguration.class)//额外导入了其他配置
@EnableConfigurationProperties({ WebMvcProperties.class, WebProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ServletContextAware {
}
WebMvcConfigurer接口

它提供配置SpringMVC底层的所有组件入口。

静态资源规则源码
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    if (!this.resourceProperties.isAddMappings()) {
        logger.debug("Default resource handling disabled");
        return;
    }
    addResourceHandler(registry, this.mvcProperties.getWebjarsPathPattern(),
            "classpath:/META-INF/resources/webjars/");
    addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
        registration.addResourceLocations(this.resourceProperties.getStaticLocations());
        if (this.servletContext != null) {
            ServletContextResource resource = new ServletContextResource(this.servletContext, SERVLET_LOCATION);
            registration.addResourceLocations(resource);
        }
    });
}
规则一

访问/webjars/**路径就去classpath:/META-INF/resources/webjars/下找资源(查看依赖中的内容)

规则二

规则二:

访问/**路径就去静态资源的四个默认位置下找资源 classpath:/META-INF/resources/ classpath:/resources/ classpath:/static/ classpath:/public/

registration.addResourceLocations(this.resourceProperties.getStaticLocations()

规则三

静态资源默认都有缓存规则的设置 i、cachePeriod:缓存周期(以s为单位),多久不再需要给服务器重新请求 ii、cacheControl:HTTP缓存控制 iii、useLastModified:是否使用最后一次修改。配合HTTP cache规则

浏览器访问了一个静态资源,如果这个服务没有发生变换,下次访问的时候可以直接让浏览器用自己缓存中的内容,而不用给服务器发请求。

registration.setCachePeriod(getSeconds(this.resourceProperties.getCache().getPeriod()));
registration.setCacheControl(this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl());
registration.setUseLastModified(this.resourceProperties.getCache().isUseLastModified());
EnableWebMvcConfiguration源码

属于WebMvcAutoConfiguration的一个内部类。

//SpringBoot 给容器中WebMvcConfigurationSupport组件
//如果我们自己放了WebMvcConfigurationSupport组件,Boot的WebMvcAutoConfiguration都会失效
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(WebProperties.class)
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
​
}

HandlerMapping:根据请求路径,找哪个handler处理请求。

WelcomePageHandlerMapping:

  • /**路径下的所有请求都在以前四个静态资源路径下找,欢迎页也是。

  • index.html只要静态资源的位置有一个index.html页面,项目启动就默认访问。

在容器中加入WebMvcConfigurer就能配置底层行为的原因
  • WebMvcConfiguration是一个自动配置类,它里面有一个EnableWebMvcConfiguration

  • EnableWebMvcConfiguration继承于DelegatingWebMvcConfiguration,这两个都生效

  • DelegatingWebMvcConfiguration利用DI把容器中所有WebMvcConfiguration注入进来

  • 别人调用DelegatingWebMvcConfiguration的方法配置底层规则,而它调用所有WebMvcConfiguration的配置底层方法。

默认规则
①静态资源映射

静态资源映射规则在WebMvcAutoConfiguration中进行了定义

  • /webjars/**的所有路径资源都在classpath:/META-INF/resources/webjars

  • /**的所有路径资源都在classpath:/META/resources/classpath:/resources/calsspath:/static/calsspath:/public/

  • 所有静态资源都定义了缓存规则。浏览器访问过一次,就会缓存一段时间,但此功能参数无默认值

    • period:缓存间隔。默认0s;

    • cacheControl:缓存控制。默认无

    • useLastModified:是否使用lastModified头。默认是false

②静态资源缓存

如①所述所有静态资源都定义了缓存规则。浏览器访问过一次,就会缓存一段时间,但此功能参数无默认值

测试缓存机制

在calsspath/static下放置一个静态资源(图片)

配置application.properties

#1、spring.web:
# 配置国际化的区域信息
#配置静态资源策略(开启、处理链、缓存)
#开启静态资源映射规则
spring.web.resources.add-mappings=true
#设置缓存
spring.web.resources.cache.period=3600
#缓存详细合并项控制,覆盖period配置,
# 浏览器第一次请求服务器,服务器告诉浏览器此资源缓存7200秒,7200秒以内的所有此资源访问不用发给服务器请求
spring.web.resources.cache.cachecontrol.max-age=7200
#使用资源last-modified时间,来对此服务器和浏览器的资源是否相同,相同返回304
spring.web.resources.cache.use-last-modified=true

注意:

spring.web.resources.cache.period 是用来设置静态资源在浏览器中的缓存过期时间。这个配置项告诉浏览器在多长时间内可以使用缓存的资源,而不需要向服务器发送请求。在这个例子中,缓存过期时间为1小时。

spring.web.resources.cache.cachecontrol.max-age 是用来设置服务器告诉浏览器此资源的缓存时间。这个配置项告诉浏览器在多长时间内可以使用缓存的资源,而不需要向服务器发送请求。在这个例子中,服务器会告诉浏览器缓存时间为2小时。

自定义静态资源规则

自定义静态资源路径,自定义缓存规则

①配置application.properties方式

#2、spring.mvc
##2.1.自定义webjars路径前缀
spring.mvc.webjars-path-pattern=/webjars/**
##2.2.静态资源访问路径前缀
spring.mvc.static-path-pattern=/**
#静态资源的文件夹
spring.web.resources.static-locations=classpath:/a/, classpath:/b/

spring.mvc:静态资源访问前缀路径

spring.web:静态资源目录、静态资源缓存策略

②代码方式

容器中只要有一个WebMvcConfigurer组件。配置的底层行为都会生效。

@Configuration//这是一个配置类
public class MyConfig implements WebMvcConfigurer {
    @Bean
   public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override
            public void addResourceHandlers(ResourceHandlerRegistry registry) {
                //保留之前的配置
                WebMvcConfigurer.super.addResourceHandlers(registry);
                //自己添加一些
                registry.addResourceHandler("static/**").addResourceLocations("classpath:/static/", "classpath:/templates/")
                        .setCacheControl(CacheControl.maxAge(7200, TimeUnit.SECONDS));
            }
        };
    }
}

③欢迎页

欢迎页规则在WebMvcAutoConfiguration中进行了定义 a、在静态资源目录下找index.html b、没有就在templates下找index模板页

④Favicon

favicon.ico 通常显示在浏览器标签页。在这段代码中,在静态资源目录下寻找名为 favicon.ico 的文件。

3、路径匹配

Spring5.3之后加入了更多的请求路径匹配的实现策略。以前只支持AntPathMatcher策略,现提供了PathPatternParser策略。并且可以让我们指定到底使用哪种策略。

①Ant风格路径语法

ant风格的路径模式语法具有以下规则:

规则含义
*表示任意的0个或多个字符
表示任意一个字符
**表示任意数量的目录
{}表示一个命名的模式占位符
[]表示字符集合,例如[a-z]表示小写字母
示例:

*.html:匹配任意名称,扩展名为.html的文件 /folder1/*/*.java:匹配在folder1目录下的任意两级目录下的.java文件 /folder2/**/*.jsp:匹配在folder2目录下任意目录深度的.jsp文件 /{type}/{id}.html:匹配任意文件名为{id}.html,在任意命名的{type}目录下的文件

注意: Ant风格的路径模式语法中的特殊特殊字符需要转义,如: 要匹配文件路径中的星号,则需要转义为\* 要匹配文件路径中的问好,则需要转义为\?

②模式切换
AntPathMatcher与PathPatternParser是Spring框架中用于处理路径模式匹配的工具类。 PathPatternParser在jmh基准测试下,有6~8被吞吐量提升,降低30%~40%的空间分配率 PathPatternParser兼容AntPathMatcher语法,并支持更多类型的路径模式 PathPatternParser"**"多端匹配的支持仅允许在模式末尾使用

@Slf4j
@RestController
public class HelloController {
    /**
     * springBoot默认使用新版的路径匹配(PathPatternParser)
     不能匹配双星在中间的情况,剩下的和AntPathMatcher使用方法相同
     可以在配置文件中使用:spring.mvc.pathmatch.matching-strategy=ant_path_matcher改为老版策略
     * @param request
     * @param path
     * @return
     */
    @GetMapping("/a*/b?/{p1:[a-f]+}/**")
    public String hello(HttpServletRequest request, @PathVariable("p1") String path){
        log.info("路径变量p1:{}", path);
        String url = request.getRequestURI();
        return url;
    }
}

总结: 使用默认的路径匹配规则,是由PathPatternParser提供的 如果路径中间出现问题需要有**,替换成Ant风格路径。

4、内容协商

一套系统适配多端数据返回。

①多端内容适配
  • 默认规则

    • 基于请求头的内容协商(默认开启)

      客户端向服务端发送请求,携带HTTP标准的Accept请求头Accept:application/jsontext/xmltext/yml 服务端根据客户端请求头期望的数据类型进行动态返回。

    • 基于请求参数内容协商(需要手动开启)

      a、发送请求GET/projects/spring-boot?format=json b、匹配到@GetMapping("/projects/spring-boot") c、根据参数协商,优先返回json类型数据(需要开启参数匹配设置) d、发送请求GET/projects/spring-boot?format=xml,优先返回xml类型数据。

效果演示

场景:

请求同一个接口,可以返回json和xml不同格式的数据,使用官方Postman进行测试。

实体类(Person)

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
    private Long id;
    private String name;
    private String email;
    private Integer age;
}

Controller

返回JSON格式

@Slf4j
@RestController
public class HelloController {
        /**
     * json适配,默认支持把对象转化为json,web场景默默人导入了jackson处理json的包;jackson-core
     * jackson也支持把数据转化为xml。但需要导入xml相关依赖
     * @return
     */
    @GetMapping("/person")
    public Person person(){
        Person person = new Person();
        person.setId(1L);
        person.setName("Louie");
        person.setEmail("xxx@qq.com");
        person.setAge(23);
        return person;
    }
}

返回XML格式

①引入支持xml内容的依赖

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

②实体类中标注注解

@JacksonXmlRootElement//可以写出为xml文档
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
    private Long id;
    private String name;
    private String email;
    private Integer age;
}

注意:

在浏览器中不能正常显示,可以将浏览器的Accept复制在Postman中新加一个Accept,关闭原来的Accept

  • 配置协商规则与支持类型

    • 修改内容协商方式

      #开启基于请求参数的内容协商功能。默认参数名是format
      spring.mvc.contentnegotiation.favor-parameter=true 
      #自定义参数名,默认为format
      spring.mvc.contentnegotiation.parameter-name=myParam

    • 效果

②内容协商原理-HttpMessageConverter

熟悉HttpMessageConverter怎么工作,何时工作,就可以定制HttpMessageConverter来实现多端内容协商。

实现

编写WebMvcConfigurer提供的configureMessageConverters底层,修改底层的MessageConverter

@ResponseBodyHttpMessageConverter处理,标注了@ResponseBody的返回值将会由支持它的HttpMessageConverter写给浏览器。

原理

a、controller方法的返回值标注了@ResponseBody注解

b、请求进来先来到DispartureServletdoDispatch()进行处理

c、找到HandlerAdapter适配器

d、利用适配器执行目标方法RequestMappingHandlerAdapter来执行

e、调用involeHandlerMethod执行目标方法。目标方法执行之前,HandlerMethodArgumentResolver(参数解析器)确定目标方法每一个参数值。HandlerMethodReturnValueHandler(返回值处理器)确定目标方法的返回值该增么处理。

f、incokeAndHandle()真正执行目标方法。目标方法执行完成,会导出返回值对象。

g、找到一个合适的返回值处理器HandlerMethodReturnValueHandler

h、最终找到RequestResponseBodyMethodProcessor能处理标注了@RsponseBody注解的方法

i、RequestResponseBodyMethodProcessor调用writerWithMessageConverters,利用MessageConverter把返回值写出去。

总结:

@ResponseBodyHttpMessageConverter处理,HttpMessageConverter会先进行内容协商。 WebMvcAutoConfiguration通过addDefaultHttpMessageConverters添加了默认的MessageConverter如下: ByteArrayHttpMessageConverter:支持字节数据读取 StringHttpMessageConverter:支持字符串读写 ResourceHttpMessageConverter:支持资源读写 ResourceRegionHttpMessageConverter:支持分区资源写出 AllEncompassingFormHttpMessageConverter:支持表单xml/json读写 MappingJackson2HttpMessageConverter:支持请求响应Json读写

遍历所有的MessageConverter看谁支持这种内容类型的数据,将支持的类型写出。 系统默认的MessageConverter功能有限,仅用于json或普通返回数据。额外增加新的内容协商功能,必须增加新的HttpMessageConverter

③WebMvcConfigurationSupport

该类提供了很多的默认设置。判断系统中是否有相应的类,如果有,就加入相应的HttpMessageConverter

jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
            ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
    jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
    jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
    jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader);
④自定义内容返回

场景:增加yaml返回支持

步骤
  1. 导入依赖

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-yaml</artifactId>
</dependency>
  1. 把对象写出成一个yaml

@Slf4j
@RestController
public class HelloController {
   public static void main(String[] args) throws JsonProcessingException {
        Person person = new Person();
        person.setId(1L);
        person.setName("Louie");
        person.setEmail("xxx@qq.com");
        person.setAge(23);
//        new YAMLFactory().disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER);可以去掉三个横线
        ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
        String s = mapper.writeValueAsString(person);
        System.out.println(s);
    }
}

  1. 编写配置

#增加一种新的内容类型
spring.mvc.contentnegotiation.media-types.yaml=text/yaml
  1. MyYamlHttpMessageConverter

增加HttpMessageConverter组件,专门负责把对象写出为yaml格式

public class MyYamlHttpMessageConverter extends AbstractHttpMessageConverter<Object> {

    private ObjectMapper objectMapper = null;
    //实例化
    public  MyYamlHttpMessageConverter(){
        //告诉SpringBoot这个MessageConverter支持哪种媒体类型
        super(new MediaType("text", "yaml", Charset.forName("UTF-8")));
        //把对象转化为yaml
        this.objectMapper = new ObjectMapper(new YAMLFactory());

    }


    @Override
    protected boolean supports(Class<?> clazz) {
        //只要是对象类型,不是基本类型
        return true;
    }

    @Override//@RequestBody
    protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        return null;
    }

    @Override//@ResponseBody 把对象怎么写出去
    protected void writeInternal(Object methodReturnValue, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        //try-with写法,自动关流
        try(OutputStream os = outputMessage.getBody()){
            this.objectMapper.writeValue(os, methodReturnValue);
        }

    }
}
  1. 编写配置类

@Configuration//这是一个配置类
public class MyConfig implements WebMvcConfigurer {
   @Bean
   public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override//配置一个能把对象转为yaml的messageConverter
            public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
                WebMvcConfigurer.super.configureMessageConverters(converters);
                converters.add(new MyYamlHttpMessageConverter());
            }
        };
    }
​
}

总结:

1、配置媒体类型支持

spring.mvc.contentnegotiation.media-types.yaml=text/yaml

2、编写对应的HttpMessageConverter,告诉Boot这个支持的媒体类型 3、把MessageConverter组件加入到底层。(容器中放一个WebMvcConfigurer组件,并配置底层的MessageConverter

5、模板引擎

由于SpringBoot使用了嵌入式Servlet容器,所以JSP默认不能使用,如果需要服务端页面渲染,优先考虑使用模板引擎。

SpringBoot模板引擎的自动配置包括:FreeMarker、Groovy、Thymeleaf、Mustache

Thymeleaf整合

Thymeleaf官网

基础语法
①核心用法

th:xxx动态渲染指定的html标签属性值、或者th指令(遍历、判断等) th:text:标签体内文本值渲染

<h3 th:text="${msg}">哈哈</h3>
<!--th:text:替换标签体的内容-->
<h3 th:utext="${msg}">呵呵</h3>
<!--th:utext:不会转义html标签,显示html该有的样式-->

th:属性:标签指定属性渲染

<img th:src="${url}"/> 
<!--访问动态路径-->

th:attr:标签任意属性渲染

<img src="picture.jpg" style="width: 300px" th:attr="src=${url}, style=${modelStyle}"/>
<!--访问动态路径, 和动态的样式-->

th:ifth:each...:其他th指令

<img src="${url}" th:if="${show}"/>
<!--访问动态路径, 并且由控制器传入的show来判断它是否显示-->

表达式:用来动态取值

${}:变量取值,使用model共享给页面的值都直接用${} @{}:url路径 #{}:国际化消息 ~{}:片段引用 *{}:变量选择(需要配合th:object绑定对象)

系统工具和内置对象可以查看详细文档。

param:请求参数对象

<p>参数是否存在: ${param.containsKey('parameterName')}</p>

session:session对象

<p>Session属性: ${session.getAttribute('attributeName')}</p>

application:application对象

<p>Application属性: ${application.getAttribute('attributeName')}</p>

#execInfo:模板执行信息

<p>模板名称: ${#execInfo.templateName}</p>
<p>模板行号: ${#execInfo.templateLine}</p>

#messages:国际化消息

<p>国际化消息: #{messageKey}</p>

#uris:uri/url工具

<a href="${#uris.encodeUrl('/path')}">链接</a>

#conversions:类型转换工具

<p>转换为数字: ${#conversions.stringToNumber('10')}</p>

#dates:日期工具,是java.util.Date对象的工具类

<p>当前日期: ${#dates.format(#dates.createNow(), 'yyyy-MM-dd')}</p>

#calendars:类似dates,只不过是java.util.calendar对象的工具类

#temporals:JDK8+java.time API工具类

#numbers:数字操作工具

#strings:字符串操作

#objects:对象操作

#bools:bool操作

#arrays:array工具

#lists:list工具

#sets:set工具

#maps:map工具

#aggrregates:集合聚合工具(sum、avg)

#ids:id生成工具

②语法示例
  • 表达式

    • 变量取值:${...}

    • url取值:@{...}

    • 国际化消息:#{}

    • 变量选择:*{...}

    • 片段引用:~{...}

  • 文本操作

    • 拼串:+

    • 文本替换:|The name is ${name|

  • 布尔操作

    • 二进制运算:and、or

    • 取反:!、not

  • 比较运算

    • 比较:>、<、<=、>=(gt,lt,ge,le)

    • 等值运算:==、!=(eq、ne)

  • 条件运算

    • if-then:(if)?(then)

    • if-then-else:(if)?(then):(else)

    • default:(value)?:(defaultValue)

  • 特殊语法

    • 无操作:_

示例

属性设置

①th:href="@{/product/list}" ②th:attr="calss=${active}" ③th:attr="src=@{/images/gtvglogo.peng},title=${logo}, alt=#{logo}" ④th:checked="${user:active}"

遍历

语法:th:each="元素名, 迭代状态:${集合}"

迭代状态(iterStat)包含下面的属性: index:当前遍历元素的索引,从0开始 count:当前遍历元素的索引,从1开始 size:需要遍历元素的总数量 current:当前正在遍历的元素对象 event/odd:是否偶数/奇数行 first:是否第一个元素 last:是否最后一个元素

使用方式

导入场景

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

实体类Person

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
    private Long id;
    private String name;
    private String email;
    private Integer age;
    private String pwd;
}

编写测试页面

list.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" xmlns:th="http://www.thymeleaf.org">
    <title>person列表页</title>
</head>

<body>
<table border="1px" cellpadding="0" >
    <tr>
        <th>#</th>
        <th>名字</th>
        <th>邮箱</th>
        <th>年龄</th>
        <th>密码</th>
        <th>状态信息</th>
    </tr>
    <tr th:each="person, status:${persons}">
        <td></td>
        <td th:text="${person.name}"></td>
        <td>[[${person.email}]]</td>
        <td th:text="${person.age}"></td>
        <td th:text="${person.pwd}"></td>
        <td>
            index: [[${status.index}]]<br/>
            count:[[${status.count}]]<br/>
            size: [[${status.size}]]<br/>
            current(当前对象): [[${status.current}]]<br/>
            even: [[${status.even}]]<br/>
            odd: [[${status.odd}]]<br/>
            first: [[${status.first}]]<br/>
            last: [[${status.last}]]<br/>
        </td>
    </tr>
</table>
</body>
</html>

controller

@GetMapping("/list")
public String list(Model model){
    List<Person> person = Arrays.asList(
            new Person(1L, "张三01", "aaa1@qq.com", 12, "asd"),
            new Person(2L, "张三02", "aaa2@qq.com", 18, "akd"),
            new Person(3L, "张三03", "aaa3@qq.com", 19, "ajd"),
            new Person(4L, "张三04", "aaa4@qq.com", 22, "aad"),
            new Person(5L, "张三05", "aaa5@qq.com", 23, "afd"),
            new Person(6L, "张三06", "aaa6@qq.com", 23, "abd")
    );
    model.addAttribute("persons", person);
   return "list";
}

判断th:if

<td th:text="|${person.age} / ${person.age>=18?'成年':'未成年'}|"></td>

<td th:if="${#strings.isEmpty(person.email)}" th:text="'已失联'"></td>
<!--删除两-->
<td th:if="${not #strings.isEmpty(person.email)}" th:text="${person.email}"></td>

th:switch

属性优先级
优先级特点属性
1片段包含th:insert th:replace
2遍历th:each
3判断th:if th:unless th:switch th:case
4定义本地变量th:object th:with
5通用方式属性修改th:attr th:attrprepend th:attrappend
6指定属性修改th:value th:href th:src
7文本值th:text th:utext
8片段指定th:fragment
9片段移除th:remove

注意:

在行内使用的时候,可以有如下的两种写法:

[[...]][(...)]

#如
size: [[${status.size}]]<br/>
变量选择

模板布局

将通用的内容抽取出来。

属性含义
th:fragment定义模板
~{templatename::selector}(~{模板名 :: 片段名})引用模板
th:insert、th:replace插入模板

footer表示公共页面的名称。

自动配置原理

①开启了org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration自动配置 ②属性绑定在ThymeleafProperties中,对应配置文件前缀spring.thymeleaf内容 ③所有的模板页面默认在classpath:/templates文件夹下 ④默认效果 a、所有的模板页面在classpath:/templates下面找 b、后缀名为.html

devtools

SpringBoot提供的工具,在修改页面后。在IDEA中ctrl+F9重新编译。

使用方式

方式一

<!--热启动功能-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>

java代码的修改,如果使用devtools热启动了,可能会引起一些BUG,建议使用重启。

方式二

也可以在setting的Compiler中勾选Build project automatically,然后点击ctrl + alt + shift + /打开Register,勾选其中的compiler.automake.allow.when.app.running。(当idea失去焦点5秒时自动构建。不用一直点)

热部署范围配置

默认不触发重启的目录列表

/META-INF/maven /META-INF/resource /resources /static /public /templates

可以通过修改配置文件的方式修改这个范围。

spring:
  devtools:
    restart:
      exclude: static/**, public/**, config/application.yaml

关闭热启动的功能

可以在application.yaml中设置enabled=false,但可能存在的情况是其他配置文件相同内容的覆盖,所以可在主程序中设置

public static void main(String[] args) {
    System.setProperty("spring.devtools.restart.enabled", "false");
    SpringApplication.run(SSMPApplication.class, args);
}

6、国际化

国际化的自动配置参照MessageSourceAutoConfiguration

实现步骤
  1. SpringBoot在类路径下查找message资源绑定文件。文件名为:messages.properties

  2. 多语言可以定义多个消息文件,命名为message_区域代码.properties。如:

  • messages.properties:默认

  • messages_en_US.properties:英文环境

  • messages_zh_CN.properties:中文环境

  1. 在程序中可以自动注入Message组件,获取国际化的配置项值

  2. 在页面中可以使用表达式#{}获取国际化的配置选项

实现方式
配置文件方式

配置文件

messages.properties

login=Login
sign=Sign-Up

messages_en_US.properties

login=Login
sign=Sign-Up

messages_zh_CN.properties

login=登录
sign=注册

展示页面earth.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" xmlns:th="http://www.thymeleaf.org">
    <title>Title</title>
</head>
<body>
<!--国际化:配置页面的值去哪个文件取到key-->
<div>
  <button th:text="#{login}"></button>
  <button th:text="#{sign}"></button>
</div>
</body>
</html>

controller

@GetMapping("/internationalize")
public String test(){
    return "earth";
}

代码方式

需要使用@RestController

@Autowired//国际化取消息用的组件
MessageSource messageSource;
@GetMapping("/haha")
public String haha(HttpServletRequest req){
    Locale locale = req.getLocale();
    //利用代码的方式获取国际化配置文件中指定的值
    String login = messageSource.getMessage("login", null, locale);
    return login;
}

7、错误处理

错误处理的自动配置都在ErrorMvcAutoConfiguration中,有两大核心机制 1、SpringBoot会自动适应处理错误(你设置的format是什么它就可以给你响应什么数据),响应页面或JSON数据 2、SpringMvc的错误处理机制依然保留,MVC处理不了,才会交给boot进行处理。

测试
@ExceptionHandler
@Controller
public class ExceptionController {
    @GetMapping("/exception")
    public String testException(){
        int i = 10 / 0;
        return "index";
    }
​
​
​
    /**
     * 1、@ExceptionHandler标识一个方法处理错误,默认只能处理这个类发生的的指定错误
     * 2、@ControllerAdvice统一处理所有错误
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(Exception.class)
    public String handlerException(Exception e){
        return "OHH~, 错误原因:" + e.getMessage();
    }
}

@ControllerAdvice

控制器

@Controller
public class ExceptionController {
    @GetMapping("/exception")
    public String testException(){
        int i = 10 / 0;
        return "index";
    }
}

handler

@ControllerAdvice
public class GlobalExceptionHandler {
    /**
     * 1、@ExceptionHandler标识一个方法处理错误,默认只能处理这个类发生的的指定错误
     * 2、@ControllerAdvice统一处理所有错误
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(Exception.class)
    public String handlerException(Exception e){
        return "OHH~同一处理, 错误原因:" + e.getMessage();
    }
}

错误处理类(BasicErrorController)

表示它从server.error.path读取配置文件,如果没有配置就会读取/error

  • 发生错误以后,转发给/error路径,SpringBoot在底层写好一个BasicErrorController的组件,专门处理这个请求。

  • 错误页面解析方式

//1、解析错误的视图地址
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
//2、如果解析不到错误页面的地址,默认的错误页就是error页
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);

容器中专门有一个错误视图解析器(在ErrorMvcAutoConfiguration)

@Bean
@ConditionalOnBean(DispatcherServlet.class)
@ConditionalOnMissingBean(ErrorViewResolver.class)
DefaultErrorViewResolver conventionErrorViewResolver() {
    return new DefaultErrorViewResolver(this.applicationContext, this.resources);
}

DefaultErrorViewResolver

@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
    ModelAndView modelAndView = resolve(String.valueOf(status.value()), model);
    if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
        modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
    }
    return modelAndView;
}
​
private ModelAndView resolve(String viewName, Map<String, Object> model) {
    String errorViewName = "error/" + viewName;
    TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName,
            this.applicationContext);
    if (provider != null) {
        return new ModelAndView(errorViewName, model);
    }
    return resolveResource(errorViewName, model);
}
​
private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
    for (String location : this.resources.getStaticLocations()) {
        try {
            Resource resource = this.applicationContext.getResource(location);
            resource = resource.createRelative(viewName + ".html");
            if (resource.exists()) {
                return new ModelAndView(new HtmlResourceView(resource), model);
            }
        }
        catch (Exception ex) {
        }
    }
    return null;
}

容器中有一个默认的名为error的view;提供了默认白页功能。

@Bean(name = "error")
@ConditionalOnMissingBean(name = "error")
public View defaultErrorView() {
    return this.defaultErrorView;
}
规则:
  1. 解析一个错误页

  • 如果发生了5xx,4xx ①、如果有模板引擎,默认会在classpath:/templates/error/精确码.html ②、如果没有模板引擎,在静态资源文件夹下找精确码.html(模糊匹配)

  • 如果匹配不到精确码.html,有模板引擎就去找classpath:/templates/error/5xx/4xx.html,没有模板引擎,在静态资源文件夹下找5xx.html4xx.html

  1. 如果模板引擎路径下面有error.html页面,就直接渲染。

测试

templates下error

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" xmlns:th="http://www.thymeleaf.org">
    <title>error</title>
</head>
<body>
模板引擎 ERROR
</body>
</html>

  1. 最佳处理错误方式

  • 前后端分离

    后台发生的所有错误,使用 @ControllerAdvice + @ExceptionHandler进行异常处理

  • 服务端页面渲染 - HTTP状态码规定错误 给classpath:/templates/error/放入常用精确的错误码页面,500.html/404.htmlclasspath:/templates/error/放通用模糊匹配的错误页面。4xx.html/5xx.html - 业务错误 核心业务,每种错误都因该代码控制跳转到定制错误页面。 通用业务,在classpath:/templates/error.html页面显示错误信息。(出现的错误信息通过SpringBoot都将它们放在了ModelAndView,只需要通过Thymeleaf方式取值即可)

8、嵌入式容器

Servlet容器:管理,运行Servlet组件(Servlet,Filter, Listener)的环境,一般值服务器。

自动配置原理

SpringBoot默认嵌入了Tomcat作为Servlet容器。自动配置类是ServletWebServerFactoryAutoConfiguration,EnbeddedWenServerFactoryCustomizerAutoConfiguration

@AutoConfiguration(after = SslAutoConfiguration.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
        ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
        ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
        ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
}

a、ServletWebServerFactoryAutoConfiguration配置类,绑定了所有和服务器有关的配置server b、绑定了自动配置类嵌入式容器场景。 c、ServletWebServerFactoryAutoConfiguration导入了嵌入式的三大服务器TomcatJettyUndertow i、导入TomcatJettyUndertow都有条件注解,系统中有这个类才行(相关依赖) ii、默认Tomcat配置生效 iii、它们都给容器中ServletWebServerFactory放了一个web服务器工厂(造web服务器) iV、web服务器工厂都有一个功能,getWebServer获取web服务器 d、ServletWebServerApplicationContextioc容器,启动的时候会调用(createWebServer)创建web服务器,而createWebServer会在onRefresh启动。(Spring容器刷新的时候,会预留一个时机,刷新子容器。onRefresh)

总结:

Web场景的Spring容器启动,在onRefresh的时候会调用Web服务器的方法。 Web服务器的创建是通过WebServerFactory实现,容器中又会根据导入的依赖的条件注解启动相关服务器。

用法

修改server下的相关配置即可修改服务器参数,通过给容其中放入一个ServletWebServerFactory来禁用SpringBoot默认放的工厂,实现自定义嵌入任意服务器。

9、全面接管SpringMVC

SpringBoot默认配置好了SpringMVC的所有常用特性,如果我么需要全面接管SpringMVC的所有配置并禁用默认配置,仅需要编写一个WebMvcConfigurer配置类,并标注@EnableWebMvc即可。 全手动模式: @EnableWebMvc:禁用默认设置 WebMvcConfigurer组件:定义MVC的底层行为

@EnableWebMvc原理
WebMvcAutoConfiguration:web场景的自动配置类。

SpringMVC自动场景配置了如下所有的默认行为 a、支持RESTful的filter:HiddenHttpMethodFilter b、支持非POST请求,请求体携带数据:FormContentFilter c、导入了@Import(EnableWebMvcConfiguration): i、WelcomePageHandlerMapping:欢迎页功能支持(模板引擎目录,金泰资源目录放index.html),项目访问/就默认展示这个页面 ii、LocaleResolver:国际化解析器 iii、ThemeResolver:主题解析器 iv、FlashMapManager:临时数据共享 v、FormattingConversionService:数据格式化、类型转化 vi、Validator:数据校验JSR303提供的数据校验功能 vii、RequestMappingHandlerMapping找每个请求由谁处理的映射关系 viii、WebBindingInitializer:请求参数的封装与绑定 ix、ExceptionHandlerExceptionResolver:默认的异常解析器 x、ContentNegotiationManager:内容协商管理器

d、WebMvcAutoConfigurationAdapter配置生效,它是一个WebMvcConfigurer,定义mvc底层组件 ​ i、定义好WebMvcConfigurer底层组件的默认功能 ​ ii、视图解析器:InternalResourceViewResolver ​ iii、视图解析器:BeanNameViewResolver,视图名(controller方法返回值字符串)就是组件名 ​ iV、内容协商解析器:ContentNegotiatingViewResolver ​ v、请求上下文的过滤器:RequestContextFilter:任意位置直接获取当前请求(RequestContextHolder) ​ vi、静态资源链规则 ​ vii、ProblemDetailsExceptionHandler:错误详情 ​ e、定义了默认的底层行为:WebMvcConfigurer

②@EnableWebMvc禁用默认行为

a、给容器中导入DelegatingWebMvcConfiguration组件,它是WebMvcConfigurationSupport b、WebMvcAutoConfiguration有一个核心的注解@ConditionalOnMissingBean(WebMvcConfigurationSupport.class),容器中没有WebMvcConfigurationSupport,WebMvcAutoConfiguration才生效 c、@EnableWebMvc导入WebMvcConfigurationSupport导致WebMvcAutoConfiguration失效。导致禁用了。

10、Web新特性

Problemdetails

它是RFC 7807定义的规范,用于定义错误信息返回格式。

文档:RFC 7807: Problem Details for HTTP APIs

原理
@Configuration(proxyBeanMethods = false)
//配置过一个属性spring.mvc.problemdetails.enabled=true
@ConditionalOnProperty(prefix = "spring.mvc.problemdetails", name = "enabled", havingValue = "true")
static class ProblemDetailsErrorHandlingConfiguration {
​
    @Bean
    @ConditionalOnMissingBean(ResponseEntityExceptionHandler.class)
    ProblemDetailsExceptionHandler problemDetailsExceptionHandler() {
        return new ProblemDetailsExceptionHandler();
    }
​
}

a、ProblemDetailsExceptionHandler是一个@ControllerAdvice集中处理系统异常 b、处理以下指定异常,如果系统出现以下异常,会被SpringBoot支持RFC 7807的方式返回异常

@ExceptionHandler({
        HttpRequestMethodNotSupportedException.class,
        HttpMediaTypeNotSupportedException.class,
        HttpMediaTypeNotAcceptableException.class,
        MissingPathVariableException.class,
        MissingServletRequestParameterException.class,
        MissingServletRequestPartException.class,
        ServletRequestBindingException.class,
        MethodArgumentNotValidException.class,
        NoHandlerFoundException.class,
        AsyncRequestTimeoutException.class,
        ErrorResponseException.class,
        ConversionNotSupportedException.class,
        TypeMismatchException.class,
        HttpMessageNotReadableException.class,
        HttpMessageNotWritableException.class,
        BindException.class
    })
函数式web

SpringMVC5.2之后允许我们使用函数式的方式,定义web的请求处理流程。

Web请求处理的方式: a、@Controller + @RequestMapping:耦合式(路由、业务耦合) b、函数式Web:分离式(路由、业务分离)

使用场景实例:

场景 User RESTful-CRUD GET/user/1 获取1号用户 GET/user 获取所有用户 POST/user 请求体携带JSON,新增一个用户 PUT/user/1 请求体携带JSON,修改1号用户 DELETE/user/1删除1号用户

实现步骤

核心类 RouterFunction RequestPredicate ServerRequest ServerResponse

①实体类User

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Long id;
    private String name;
    private String email;
    private Integer age;
    private String pwd;
}

②编写路由请求WebFunctionConfig

package com.louis.config;
​
import com.louis.business.UserBusinessHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.function.*;
​
/**
 * @author XRY
 * @date 2023年07月13日15:15
 */
​
/**
 *     GET/user/1       获取1号用户
 *     GET/user         获取所有用户
 *     POST/user        请求体携带JSON,新增一个用户
 *     PUT/user/1       请求体携带JSON,修改1号用户
 *     DELETE/user/1    删除1号用户
 */
@Configuration
public class WebFunctionConfig {
    /**
     * 函数式web
     * 1、给容器中放一个Bean  RouterFunction<ServerResponse>
     * 2、每个业务准备一个自己的handler,但它的声明需要使用HandlerFunction类型
     *
     * 函数式web核心四大对象
     * 1、RouterFunction:定义路由信息。发什么请求、谁来处理
     * 2、RequestPredicate:请求谓语。请求方式(Get、POST)、请求参数
     * 3、ServerRequest:封装请求完整数据
     * 4、ServerResponse:封装响应完整数据
     */
 /*   @Bean
    public RouterFunction<ServerResponse> userRoute(){
        //+s表示某个类的工具类
        RouterFunctions.route() //开始定义路由信息
                .GET("/user/{id}", RequestPredicates.accept(MediaType.ALL), request -> {
                    //业务处理
​
                    //构造响应
                    return ServerResponse.ok().build();
                }) //RequestPredicates.accept(MediaType.ALL)表示接收任意类型的参数
        return null;
    }*/
    //@Bean给容器中放组件的时候,如果参数是一个对象,它会默认从容器中取,相当于自动注入
    @Bean
    public RouterFunction<ServerResponse> userRoute(UserBusinessHandler userBusinessHandler){
​
        return RouterFunctions.route()
                .GET("/user/{id}", RequestPredicates.accept(MediaType.ALL), userBusinessHandler::getUser/*相当于调用userBusinessHandler的方法*/)
                .GET("/users", userBusinessHandler::getUsers)
                .POST("/user", RequestPredicates.accept(MediaType.APPLICATION_JSON), userBusinessHandler::saveUser)
                .PUT("/user/{id}", RequestPredicates.accept(MediaType.APPLICATION_JSON), userBusinessHandler::updateUSer)
                .DELETE("/user/{id}", userBusinessHandler::deleteUser)
                .build();
    }
}

③业务处理类UserBusinessHandler

/**
 * 专门处理User有关的业务
 */
@Slf4j
@Component
public class UserBusinessHandler {
    /**
     * 查询指定id的user
     * 他要代替原来的handlerFunction
     * public interface HandlerFunction<T extends ServerResponse> {
     *  T handle(ServerRequest request) throws Exception;
     *
     * }
     * @param request
     * @return
     */
    public ServerResponse getUser(ServerRequest request) throws Exception{
        String id = request.pathVariable("id");
        log.info("查询{}用户信息", id);
        //业务处理
        User user = new User(1L, "louis", "aa@qq.com",23, "1234");
        //构造响应
        return ServerResponse.ok().body(user);
    }
​
    /**
     * 获取所有用户
     * @param request
     * @return
     * @throws Exception
     */
    public ServerResponse getUsers(ServerRequest request) throws Exception{
        log.info("查询所有用户信息完成");
        //业务处理
        List<User> users = Arrays.asList(
                new User(1L, "张三01", "aaa1@qq.com", 12, "asd"),
                new User(2L, "张三02", "", 18, "akd"),
                new User(3L, "张三03", "", 19, "ajd"),
                new User(4L, "张三04", "aaa4@qq.com", 22, "aad"),
                new User(5L, "张三05", "aaa5@qq.com", 23, "afd"),
                new User(6L, "张三06", "aaa6@qq.com", 23, "abd")
        );
        return ServerResponse.ok().body(users);//凡是body中的数据,就是以前的@ResponseBody原理,利用HttpMessageConverter写出为json
    }
​
    /**
     * 保存用户
     * @param request
     * @return
     */
    public ServerResponse saveUser(ServerRequest request) throws ServletException, IOException {
        //提取请求体
        User body = request.body(User.class);
        log.info("保存信息:{}", body);
        return ServerResponse.ok().build();
    }
​
​
    /**
     * 更新用户
     * @param request
     * @return
     */
    public ServerResponse updateUSer(ServerRequest request) throws ServletException, IOException {
        User body = request.body(User.class);
        log.info("用户信息更新:{}", body);
        return ServerResponse.ok().build();
    }
​
​
    /**
     * 删除用户
     * @param request
     * @return
     */
    public ServerResponse deleteUser(ServerRequest request) {
        String id = request.pathVariable("id");
        log.info("删除用户-{}-信息", id);
        return ServerResponse.ok().build();
    }
}

④测试

getUser

getUsers

saveUser

updateUSer

deleteUser

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

matlab模糊控制文件m代码实现和基础理论

1、内容简介 略 15-可以交流、咨询、答疑 通过m代码来实现生成模糊文件fis文件 2、内容说明 模糊文件m代码实现和基础理论 matlab模糊控制文件m代码实现和基础理论 模糊文件、m代码和模糊基础理论 3、仿真分析 略 4、参考论文 略 链接&#xff1a;https://pan.baidu.co…

【C++】非类型模板参数 | array容器 | 模板特化 | 模板为什么不能分离编译

目录 一、非类型模板参数 二、array容器 三、模板特化 为什么要对模板进行特化 函数模板特化 补充一个问题 类模板特化 全特化与偏特化 全特化 偏特化 四、模板为什么不能分离编译 为什么 怎么办 五、总结模板的优缺点 一、非类型模板参数 模板参数分两类&#x…

【MongoDB】索引 – 文本索引(用权重控制搜索结果)

一、准备工作 这里准备一些数据 db.books.drop();db.books.insert({_id: 1, name: "Java", alias: "java 入门", description: "入门图书" }); db.books.insert({_id: 2, name: "C", alias: "c", description: "C 入…

[护网杯 2018]easy_tornado 1(两种解法!)

题目环境&#xff1a;发现有三个txt文本文件 /flag.txt/welcome.txt/hints.txt 依此点开 flag在/fllllllllllllag文件中 在hints.txt文件中发现md5计算 md5(cookie_secretmd5(filename)) 并且三个文件中都存在filehash&#xff08;文件名被哈希算法加密32位小写&#xff09; 猜…

轻量封装WebGPU渲染系统示例<23>- 可渲染对象添加到多个渲染器Pass节点(源码)

渲染和计算混合系统&#xff0c; 可以看做基于算力驱动设计理念的一种实现。 此系统中&#xff0c;可渲染(rendering)/计算(computing)实体可以任意添加到一个渲染器pass节点。若干个这样的节点相关联&#xff0c;就能构成对应的pass node graph&#xff0c;也就实现了整个3D渲…

CS224W6.2——深度学习基础

在本文中&#xff0c;我们回顾了深度学习的概念和技术&#xff0c;这些概念和技术对理解图神经网络至关重要。从将机器学习表述为优化问题开始&#xff0c;介绍了目标函数、梯度下降、非线性和反向传播的概念。 文章目录 1. 大纲2. 优化问题2.1 举例损失函数 3. 如何优化目标函…

【FISCO BCOS】十九、区块链浏览器部署

目录 一、环境依赖 检查环境 1.检查java 二、拉取安装脚本 获取部署安装包 ​编辑 解压安装包 进入目录 三、修改配置 四、部署服务 五、状态检查 检查前后端进程 1.检查后端server进程 2.检查前端的nginx进程 检查进程端口 六、使用区块链浏览器 1.配置群组…

(二)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB

一、七种算法&#xff08;DBO、LO、SWO、COA、LSO、KOA、GRO&#xff09;简介 1、蜣螂优化算法DBO 蜣螂优化算法&#xff08;Dung beetle optimizer&#xff0c;DBO&#xff09;由Jiankai Xue和Bo Shen于2022年提出&#xff0c;该算法主要受蜣螂的滚球、跳舞、觅食、偷窃和繁…

HarmonyOS 高级特性

引言 本章将探讨 HarmonyOS 的高级特性&#xff0c;包括分布式能力、安全机制和性能优化。这些特性可以帮助你构建更强大、更安全、更高效的应用。 目录 HarmonyOS 的分布式能力HarmonyOS 的安全机制HarmonyOS 的性能优化总结 1. HarmonyOS 的分布式能力 HarmonyOS 的分布…

Zabbix监控SSL证书有效期

一、介绍 由于业务需要&#xff0c;最近通过 Let’s Encrypt 申请了一些 SSL 证书&#xff0c;而证书有效期为 3 个月&#xff0c;需要在证书到期之前 renew。由于域名较多经常忘记 renew&#xff0c;导致证书过期&#xff0c;因此想通过 Zabbix 的方式监控证书的到期时间&…

linux 下非sudo安装cmake

1.查看位数 getconf LONG_BIT2.下载对应压缩包 Download CMake Source Distribution 未编译源代码 Binary Distribution已经编译好的 3.解压至文件夹 tar -zxvf cmake-3.28.0-rc4-linux-x86_64.tar.gz 4.添加环境变量 vi ~/.bashrc 最后一行添加 写到bin目录 export P…

2023亚太杯数学建模B题思路

文章目录 0 赛题思路1 竞赛信息2 竞赛时间3 建模常见问题类型3.1 分类问题3.2 优化问题3.3 预测问题3.4 评价问题 4 建模资料5 最后 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 竞赛信息 2023年第十三…

Python BeautifulSoup 库使用教程

文章目录 简介安装 BeautifulSoup 库BeautifulSoup 库的导入BeautifulSoup 库依赖的解析库创建 BeautifulSoup 对象CSS选择器1、通过标签名查找2、通过 CSS 的类名查找3、通过 Tag(标签) 的 id 查找4、通过 是否存在某个属性来查找5、通过 某个标签是否存在某个属性来查找 获取…

怎么制作安装电子版说明书?方法献上~

在现代科技发展的背景下&#xff0c;制作一份优质的电子版说明书对于帮助用户正确、高效地使用产品至关重要。无论是软件、设备还是家电产品&#xff0c;一份清晰明了的电子版说明书可以为用户提供指导和支持&#xff0c;提升用户体验和满意度。那么&#xff0c;如何制作一份出…

springcloud电影购票选座网站系统源码

开发技术&#xff1a; jdk1.8&#xff0c;mysql5.7&#xff0c;idea springcloud springboot mybatis vue elementui 功能介绍&#xff1a; 用户端&#xff1a; 登录注册 首页显示搜索电影&#xff0c;轮播图&#xff0c;电影分类&#xff0c;最近上架电影&#xff08;可…

C++进阶-STL list容器的简单认识

STL list容器的简单认识 list容器基本概念list容器构造函数list容器赋值和交换list容器大小操作list容器插入和删除list容器数据存取list容器反转和排序list排序案例 list容器基本概念 list容器是将数据进行链式存储的容器&#xff0c;链表&#xff08;list&#xff09;是一种…

如何查看反汇编(VS)

如何查看反汇编 1. 设置断点2. 运行到该处3. 右键 反汇编结果 1. 设置断点 2. 运行到该处 3. 右键 反汇编 结果 即可跳转查看反汇编

【Linux】无废话教你如何用vs code连接云服务器

目录 1. 前言2. 云服务器准备3. 插件准备4. 连接云服务器5. 如何连接一台云服务器多个用户 1. 前言 本文章针对已经成功下载vs code的朋友&#xff0c;如若还未下载移步vscode官网 2. 云服务器准备 这里首先我们需要一台云服务器。云服务器选择有很多&#xff0c;阿里云、腾讯云…

Qt贝塞尔曲线

目录 引言核心代码基本表达绘制曲线使用QEasingCurve 完整代码 引言 贝塞尔曲线客户端开发中常见的过渡效果&#xff0c;如界面的淡入淡出、数值变化、颜色变化等等。为了能够更深的了解地理解贝塞尔曲线&#xff0c;本文通过Demo将贝塞尔曲线绘制出来&#xff0c;如下所示&am…

红黑树插入节点的模拟实现

要学习红黑树节点的插入那么首先就要了解什么是红黑树&#xff0c;以及红黑树的特点。 红黑树的特点 本来AVL树已经很厉害了&#xff0c;但是红黑树的总体效率略比1AVL树高。高的大体原因。我们先来看一下红黑树和AVL树的区别。 AVL树严格的保证了左子树和右子树的高度差不超…