spring mvc文档阅读笔记——02

news2024/9/29 4:05:20

目录标题

    • 一、Asynchronous Requests(异步请求)
    • (一)阻塞和非阻塞,同步和异步
    • (二)DeferredResult
    • (三)Callable
    • 二、跨域请求CORS
    • (一)实现跨域请求的方式
    • (二)判断是否跨域
    • (三)@CrossOrigin实现跨域
    • (四)@CrossOrigin 属性
    • (五)Java config实现跨域
    • (六)XML 配置实现跨域
    • (七)Spring Security 完全CORS支持
    • 三、HTTP缓存
    • 四、MVC Config
    • (一)addFormatters(类型转换)
    • (二)getValidator(校验、验证)
    • (三)addInterceptors(拦截器)
    • (四)configureContentNegotiation(内容协商)
    • (五)configureAsyncSupport(异步请求配置)
    • (六)addViewControllers(视图控制器)
    • (七)configureViewResolvers(视图解析器)
    • (八)addResourceHandlers(静态资源处理器)
    • (九)其他
    • 五、过滤器
    • 六、RestTemplate
    • (一)get
    • (二)post
    • (三)Exchange
    • 七、WebSocket
    • 八、模板引擎

一、Asynchronous Requests(异步请求)

(一)阻塞和非阻塞,同步和异步

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 同步异步指的是通信模式,即被调用者结果返回时通知进程/线程的一种通知机制。涉及回调函数

  2. 阻塞和非阻塞指的是调用结果返回前进程/线程的状态。涉及线程挂起

(二)DeferredResult

DeferredResult 方式主要通过setResult(“123”);方法实现异步。当设置了setResult(“123”);后。才会响应请求结果给用户。可以通过其他线程设置Result的值。

在这里插入图片描述

@RequestMapping(value = "/async/demo")
@ResponseBody
public DeferredResult<String> async(){
    // 创建 DeferredResult,设置超时时间 60s。通过构造函数可以设置超时时间。new DeferredResult<>((long)(60*1000));
    DeferredResult<String> deferredResult = new DeferredResult<>();

    new Thread(()->{
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        deferredResult.setResult("123");
        System.out.println("DeferredResult设置值成功。可以响应请求了。");
    }).start();
    
    System.out.println("DeferredResult中有值才会响应请求:"+deferredResult.hasResult());
    System.out.println("主线程结束,异步等待DeferredResult值设置。。。end!!!");
    return deferredResult;
}

值得注意的是:setResult(“index”); 可以返回一个视图。去掉@ResponseBody 注解。会返回视图。

返回一个页面:

@RequestMapping(value = "/async/demo")
//@ResponseBody
public DeferredResult<String> async(){
   // 创建 DeferredResult,设置超时时间 60s。通过构造函数可以设置超时时间。new DeferredResult<>((long)(60*1000));
   DeferredResult<String> deferredResult = new DeferredResult<>();

   new Thread(()->{
       try {
           Thread.sleep(5000);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       //index是一个页面
       deferredResult.setResult("index");
       System.out.println("DeferredResult设置值成功。可以响应请求了。");
   }).start();
   System.out.println("DeferredResult中有值才会响应请求:"+deferredResult.hasResult());
   System.out.println("主线程结束,异步等待DeferredResult值设置。。。end!!!");
   return deferredResult;
}

(三)Callable

Callable 的使用方式与DeferredResult差不多,只有子线程中return了才会响应给用户。同样也能返回一个视图。

@ResponseBody
@RequestMapping("/async02")
public Callable<String> async01() {

    System.out.println("主线程start...." + Thread.currentThread()+"-"+System.currentTimeMillis());
    Callable<String> callable = new Callable<String>() {
        @Override
        public String call() throws Exception {
            System.out.println("callable线程start...." + Thread.currentThread() + "-" + System.currentTimeMillis());
            Thread.sleep(3000);
            System.out.println("callable线程end...." + Thread.currentThread() + "-" + System.currentTimeMillis());
            return "ok";
        }
    };
    System.out.println("主线程end...." + Thread.currentThread()+"-"+System.currentTimeMillis());
    return callable;
}

二、跨域请求CORS

Spring MVC允许你处理CORS(跨源资源共享)。
在这里插入图片描述

(一)实现跨域请求的方式

实现跨域请求的方式(让url支持跨域请求。):配置xml、java config 和使用注解。

在这里插入图片描述

(二)判断是否跨域

在这里插入图片描述

(三)@CrossOrigin实现跨域

@RestController
@RequestMapping("/account")
public class AccountController {

    @CrossOrigin
    @GetMapping("/{id}")
    public Account retrieve(@PathVariable Long id) {
        // ...
    }

    @DeleteMapping("/{id}")
    public void remove(@PathVariable Long id) {
        // ...
    }
}

在这里插入图片描述

@CrossOrigin 注解支持类级别和方法级别。

//表示只有http://domain2.com这个域能调用以下url
@CrossOrigin(origins = "http://domain2.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {

    @GetMapping("/{id}")
    public Account retrieve(@PathVariable Long id) {
        // ...
    }

    @DeleteMapping("/{id}")
    public void remove(@PathVariable Long id) {
        // ...
    }
}

(四)@CrossOrigin 属性

  1. origins
    特定来源的允许来源列表,例如“https://domain1.com“,或”*“表示所有原点。
    飞行前实际CORS请求的访问控制允许源响应标头中列出了匹配的源。
    默认情况下,允许所有原点。
    注意:CORS检查使用“Forwarded”(RFC 7239)、“X-Forwarded-Host”、“X-Forwarded-Port”和“X-Forward-Proto”标头(如果存在)中的值,以反映客户端发起的地址。考虑使用ForwardedHeaderFilter,以便从中心位置选择是提取和使用,还是丢弃此类标头。有关此筛选器的更多信息,请参阅Spring Framework参考。
  2. allowedHeaders
    实际请求中允许的请求头列表,可能为“*”以允许所有头。
    允许的标头列在飞行前请求的访问控制允许标头响应标头中。
    根据CORS规范,如果头名称是缓存控制、内容语言、过期、上次修改或Pragma之一,则不需要列出。
    默认情况下,允许所有请求的标头。
  3. exposedHeaders
    用户代理将允许客户端访问实际响应的响应标头列表,而不是“简单”标头,即缓存控制、内容语言、内容类型、过期、上次修改或Pragma,
    暴露的标头列在实际CORS请求的访问控制暴露标头响应标头中。
    特殊值“*”允许为非认证请求公开所有标头。
    默认情况下,没有显示标题。
  4. maxAge
    飞行前响应的缓存持续时间的最大期限(秒)。
    此属性控制飞行前请求的Access Control Max Age响应标头的值。
    将其设置为合理的值可以减少浏览器所需的飞行前请求/响应交互的数量。负值表示未定义。
    默认设置为1800秒(30分钟)。
  5. allowCredentials
    浏览器是否应向带注释的端点发送凭据,如cookie以及跨域请求。已配置的值设置在飞行前请求的访问控制允许凭据响应标头上。
    注意:请注意,此选项与配置的域建立了高度信任,并通过暴露敏感的用户特定信息(如cookie和CSRF令牌)增加了web应用程序的表面攻击。
    默认情况下,未设置此项,在这种情况下,也未设置Access Control Allow Credentials标头,因此不允许使用凭据。
  6. methods
    支持的HTTP请求方法列表。(get\post等)
    默认情况下,支持的方法与控制器方法映射到的方法相同。

(五)Java config实现跨域

在这里插入图片描述

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
		//这里配置的东西和注解的属性对应的意思一样。
        registry.addMapping("/api/**")
            .allowedOrigins("http://domain2.com")
            .allowedMethods("PUT", "DELETE")
            .allowedHeaders("header1", "header2", "header3")
            .exposedHeaders("header1", "header2")
            .allowCredentials(true).maxAge(3600);

        // Add more mappings...
    }
}

(六)XML 配置实现跨域

<mvc:cors>

    <mvc:mapping path="/api/**"
        allowed-origins="http://domain1.com, http://domain2.com"
        allowed-methods="GET, PUT"
        allowed-headers="header1, header2, header3"
        exposed-headers="header1, header2" allow-credentials="true"
        max-age="123" />

    <mvc:mapping path="/resources/**"
        allowed-origins="http://domain1.com" />

</mvc:cors>

(七)Spring Security 完全CORS支持

三、HTTP缓存

HTTP 缓存,对于前端的性能优化方面来讲,是非常关键的,从缓存中读取数据和直接向服务器请求数据,完全就是一个在天上,一个在地下。

我们最熟悉的是 HTTP 服务器响应返回状态码 304,304 代表表示告诉浏览器,本地有缓存数据,可直接从本地获取,无需从服务器获取浪费时间

@RestController
public class HttpCacheController {

    private  int i = 0;
    private  int version = 0;

    @GetMapping("/httpCache")
    public ResponseEntity<String> httpCache() {
        i++;
        return ResponseEntity
                .ok()
                .cacheControl(CacheControl.maxAge(30, TimeUnit.SECONDS))
                // 如果版本不变,那么不会刷新body的值
                .eTag("version"+version)
                .body("i="+i);
    }

    @GetMapping("/setVersion/{version}")
    public String setVersion(@PathVariable int version) {
        this.version = version;
        return "version"+version;
    }

}

第一次请求:响应码:200。响应结果i=1

在这里插入图片描述
再次请求:响应码 304 。响应结果i=1 (正常是i=2)
在这里插入图片描述

改变version。
在这里插入图片描述
再次请求:响应码 200。响应结果i=3 (刷新body)
在这里插入图片描述

四、MVC Config

Java config 配置 spring mvc 有两种方式一种是实现 WebMvcConfigurer 接口(推荐);一种是继承WebMvcConfigurerAdapter(已废弃)

@Configuration
//@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    // Implement configuration methods...
}

注意 如果使用了这个注解@EnableWebMvc,那么会默认取消spring boot一些默认配置,也就是完全接管mvc配置。一般不使用这个注解。不用这个注解会叠加配置,优先使用自己配置的。

(一)addFormatters(类型转换)

在这里插入图片描述

类型转换方式有:

  1. 通过数据绑定@InitBinder注解。
    参考上一篇内容的数据绑定@InitBinder
  2. 通过java config 配置mvc

/**
 * @author lihua
 * @date 2022/12/28 11:38
 **/
@Configuration
//@EnableWebMvc
public class WebConfig implements WebMvcConfigurer{

    @Override
    public void addFormatters(FormatterRegistry registry) {
        //registry.addParser();
//        registry.addPrinter();
        registry.addFormatter(new Formatter<User>() {

            @Override
            public String print(User object, Locale locale) {
                System.out.println(object.toString());
                return object.toString();
            }

            @Override
            public User parse(String text, Locale locale) throws ParseException {
                User user =new User();

                //将id-1|name-lihua解析为user
                System.out.println(text);
                //这里的|需要转义
                String[] split = text.split("\\|");
                for (String s : split) {
                    String[] split1 = s.split("-");
                    if ("id".equals(split1[0])){
                        user.setId(split1[1]);
                    }else if ("name".equals(split1[0])){
                        user.setName(split1[1]);
                    }else {

                    }
                }
                return user;
            }
        });
    }
}
class User{
    private String id;
    private String name;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

@RestController
class TestConfigController{
    //http://localhost:8080/testAddFormatters/id-1|name-lihua
    @GetMapping("/testAddFormatters/{user}")
    public User testAddFormatters(@PathVariable("user") User user){
        if(Objects.isNull(user)){
            return null;
        }
        return user;
    }
}
  1. 通过注解@NumberFormat,@DateTimeFormat

@DateTimeFormat:

//标注到实体类上,或者controller的方法参数上
class User{
    private String id;
    private String name;

    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
    private Date date;
 }
//http://localhost:8080/testAddFormatters/2023-1-11 10:54:20
@GetMapping("/testAddFormatters/{loginTime}")
public Date testAddFormatters(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") @PathVariable Date loginTime){

   Date date = loginTime;

   return date;
}

注意这个注解是将前台传入的时间字符串转换成Date类型。不能将Data类型转换成时间字符串给前台使用,如果你需要转换成时间字符串给前台,你可以试试@JsonFormat(pattern=“yyyy-MM-dd”,timezone = “GMT+8”)注解。

  • 注解@JsonFormat主要是后台到前台的时间格式的转换

  • 注解@DataFormAT主要是前后到后台的时间格式的转换

@NumberFormat:
@NumberFormat可以应用于任何JDK Number类型,如Double和Long。
在这里插入图片描述

//在实体类上使用
class User{
    private String id;
    private String name;

    @NumberFormat(style= NumberFormat.Style.NUMBER,pattern="#,###")
	private int total;
	
	@NumberFormat(style= NumberFormat.Style.PERCENT)
	private double discount;
	
	@NumberFormat(style= NumberFormat.Style.CURRENCY)
	private double money;
 }


//在controller方法上使用,http://localhost:8080/testAddFormatters2/5,500,0
@GetMapping("/testAddFormatters2/{total}")
public @NumberFormat(style= NumberFormat.Style.NUMBER,pattern="#,###") String testAddFormatters2(@NumberFormat(style= NumberFormat.Style.NUMBER,pattern="#,###") @PathVariable Long total){
    System.out.println(total);

    return"5,500";
}

(二)getValidator(校验、验证)

没有理解如果使用,官网介绍较少。估计跟Spring Boot集成的hibernate validator框架用法一样。
在这里插入图片描述

过滤器和拦截器的区别:
1.过滤器是servlet中的对象,拦截器是框架中的对象
2.过滤器实现Filter接口对象,拦截器是实现HandleInterceptor
3.过滤器是用来设置request,response参数、属性,侧重对数据的过滤;拦截器是用来验证请求的,能截断请求
4.过滤器是在拦截器之前执行的
5.过滤器是tomcat服务器创建的对象,拦截器是springmvc容器创建的对象
6.过滤器是一个执行时间点;拦截器是三个执行时间点
7.过滤器可以处理jsp、js、html等;拦截器是侧重拦截Controller的对象,如果你的请求不能被DispatcherServlet接收,这个请求不会执行拦截器的内容
8.拦截器拦截普通类方法执行,过滤器过滤servlet请求响应
在这里插入图片描述

(三)addInterceptors(拦截器)

在这里插入图片描述

/**
 * @author lihua
 * @date 2022/12/28 11:38
 **/
@Configuration
//@EnableWebMvc
public class WebConfig implements WebMvcConfigurer{


    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(new HandlerInterceptor() {
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

                System.out.println("1");

                //当返回true时才,请求才能通过
                String userName=null;
                Cookie[] cookies = request.getCookies();
                for (Cookie cookie : cookies) {
                    if("userName".equals(cookie.getName())){
                       userName= cookie.getValue();
                       break;
                    }
                }
                String isLogin = (String) request.getSession().getAttribute("login-" + userName);

                if(Objects.isNull(isLogin)){
                    //重定向
                    response.sendRedirect("/mvcConfig/login");
                    return false;
                }
                //返回true才会执行请求对应的controller 处理方法。
                return true;
            }

            @Override
            public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
                System.out.println("2");
            }

            @Override
            public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

                System.out.println("3");
            }
        }).addPathPatterns("/**").excludePathPatterns("/mvcConfig/index","/mvcConfig/login").order(1); //拦截除了index的所有请求
    }
}
@Data
@ToString
class User{
    private String id;
    private String userName;
}

@Controller
@RequestMapping("/mvcConfig")
class TestConfigController{
    @GetMapping("/login")
    public  String loginIndex(){

        return "login";
    }

    @GetMapping("/index")
    public  String index(){

        return "index";
    }

    @PostMapping("/login")
    @ResponseBody
    public  String login(User user,HttpServletRequest request){
        if(Objects.isNull(user)){
            user = new User();
            user.setUserName("lihua");
        }
        Cookie cookie = new Cookie("userName",user.getUserName());
        cookie.setMaxAge(60*60*24);

        request.getSession().setAttribute("login-"+user.getUserName(),"true");
        return "ok";
    }

}

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>
<h1>login-index</h1>
<form action="/mvcConfig/login" method="post">
    First name: <input type="text" name="userName"><br>
    Last name: <input type="text" name="lname"><br>
    <input type="submit" value="提交">
</form>
</body>
</html>

配置拦截器时可以通过以下方法配置拦截规则。
在这里插入图片描述

  • order:拦截器执行顺序,数值越小越早执行。
  • excludePathPatterns:哪些路径不需要拦截,比如/index, /login(首页和登录界面) 注意可以使用通配符*、** ,多个路径通过逗号(,)分隔
  • addPathPatterns:拦截哪些。 注意可以使用通配符*、** ,多个路径通过逗号(,)分隔

(四)configureContentNegotiation(内容协商)

理解HTTP内容协商
在这里插入图片描述

客户端在请求时可以与服务端协商需要返回的内容类型(application/json、text/html、application/xml等)。

1、客户端指定内容类型的方式有后缀 .json . xml(如:http://localhost:8080/mvcConfig/contentNegotiation.json;http://localhost:8080/mvcConfig/contentNegotiation.html
2、请求参数 (如:http://localhost:8080/mvcConfig/contentNegotiation?format=xml
3、 HTTP请求头Accept在这里插入图片描述
4、其他方式

@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
   // 自定义策略
   configurer.favorParameter(true)// 是否开启请求参数来决定mediaType,默认false
           .ignoreAcceptHeader(true)// 不检查Accept请求头
           .parameterName("mediaType") //指定一个名字用来接收内容类型(不设置默认是:format) http://localhost:8080/mvcConfig/contentNegotiation?mediaType=json
           .defaultContentType(MediaType.APPLICATION_JSON)// 设置默认的MediaType
           .mediaType("html", MediaType.TEXT_HTML)// 请求以.html结尾的会被当成MediaType.TEXT_HTML
           .mediaType("json", MediaType.APPLICATION_JSON)// 请求以.json结尾的会被当成MediaType.APPLICATION_JSON
           .mediaType("xml", MediaType.APPLICATION_ATOM_XML);// 请求以.xml结尾的会被当成MediaType.APPLICATION_ATOM_XML
}

(五)configureAsyncSupport(异步请求配置)

可以结合Asynchronous Requests(异步请求)一起使用

@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
   // 注册callable拦截器
   configurer.registerCallableInterceptors(new CallableProcessingInterceptor() {
       @Override
       public <T> void beforeConcurrentHandling(NativeWebRequest request, Callable<T> task) throws Exception {
           
       }

       @Override
       public <T> void preProcess(NativeWebRequest request, Callable<T> task) throws Exception {

       }

       @Override
       public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) throws Exception {

       }

       @Override
       public <T> Object handleTimeout(NativeWebRequest request, Callable<T> task) throws Exception {
           return null;
       }

       @Override
       public <T> Object handleError(NativeWebRequest request, Callable<T> task, Throwable t) throws Exception {
           return null;
       }

       @Override
       public <T> void afterCompletion(NativeWebRequest request, Callable<T> task) throws Exception {

       }
   });
   // 注册deferredResult拦截器
   configurer.registerDeferredResultInterceptors(new DeferredResultProcessingInterceptor() {
       @Override
       public <T> void beforeConcurrentHandling(NativeWebRequest request, DeferredResult<T> deferredResult) throws Exception {
           
       }

       @Override
       public <T> void preProcess(NativeWebRequest request, DeferredResult<T> deferredResult) throws Exception {

       }

       @Override
       public <T> void postProcess(NativeWebRequest request, DeferredResult<T> deferredResult, Object concurrentResult) throws Exception {

       }

       @Override
       public <T> boolean handleTimeout(NativeWebRequest request, DeferredResult<T> deferredResult) throws Exception {
           return false;
       }

       @Override
       public <T> boolean handleError(NativeWebRequest request, DeferredResult<T> deferredResult, Throwable t) throws Exception {
           return false;
       }

       @Override
       public <T> void afterCompletion(NativeWebRequest request, DeferredResult<T> deferredResult) throws Exception {

       }
   });
   // 异步请求设置默认超时时间,单位毫秒
   configurer.setDefaultTimeout(1000);
   // 设定异步请求线程池callable等, spring默认线程不可重用
   configurer.setTaskExecutor(new ThreadPoolTaskExecutor());
}

(六)addViewControllers(视图控制器)

  1. 如果只需要实现简单的页面跳转,可以通过配置addViewControllers 实现。不需要写一些controller专门用来跳转页面。比如:请求路径/index 只需要跳转到index.html页面,不需要处理业务逻辑,那么你可以试试这个配置。
  2. 快速配置重定向
  3. 简单配置一个路径只返回一个状态码
@Override
public void addViewControllers(ViewControllerRegistry registry) {
    //重定向
    registry.addRedirectViewController("/mvcConfig/index","/index");
    //请求/mvcConfig/login 返回login.html视图(绑定视图)
    registry.addViewController("/mvcConfig/login").setViewName("login");

    //访问路径返回指定状态码,Get请求
    registry.addStatusController("/mvcConfig/code", HttpStatus.OK);

}

(七)configureViewResolvers(视图解析器)

官网配置jsp视图解析器的一个例子:
在这里插入图片描述

@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
	//允许使用ContentNegotiatingViewResolver前置所有其他配置的视图解析器,并根据客户端请求的媒体类型(例如在Accept标头中)在所有选定视中进行选择。如果多次调用,则提供的默认视图将添加到可能已配置的任何其他默认视图中。使用Jackson2ObjectMapperBuilder提供的默认配置构建一个新的MappingJackson2JsonView,并将内容类型设置为application/json。
	registry.enableContentNegotiation(new MappingJackson2JsonView());
	registry.jsp();
}

registry.jsp(); 源码如下

public UrlBasedViewResolverRegistration jsp() {
	return jsp("/WEB-INF/", ".jsp");
}
public UrlBasedViewResolverRegistration jsp(String prefix, String suffix) {
	InternalResourceViewResolver resolver = new InternalResourceViewResolver();
	resolver.setPrefix(prefix);
	resolver.setSuffix(suffix);
	this.viewResolvers.add(resolver);
	return new UrlBasedViewResolverRegistration(resolver);
}

如果你想要配置 thymeleaf 模板引擎的视图解析器,spring boot项目推荐你在配置文件:application.properties 中配置。常见配置如下:

application.properties

# THYMELEAF (ThymeleafAutoConfiguration)
# 开启模板缓存(默认值: true )
spring.thymeleaf.cache=true
# 检查模板是否存在,然后再呈现
spring.thymeleaf.check-template=true
# 检查模板位置是否正确(默认值 :true )
spring.thymeleaf.check-template-location=true
#Content-Type 的值(默认值: text/html )
spring.thymeleaf.content-type=text/html
# 开启 MVC Thymeleaf 视图解析(默认值: true )
spring.thymeleaf.enabled=true
# 模板编码
spring.thymeleaf.encoding=UTF-8
# 要被排除在解析之外的视图名称列表,⽤逗号分隔
spring.thymeleaf.excluded-view-names=
# 要运⽤于模板之上的模板模式。另⻅ StandardTemplate-ModeHandlers( 默认值: HTML5)
spring.thymeleaf.mode=HTML5
# 在构建 URL 时添加到视图名称前的前缀(默认值: classpath:/templates/ )
spring.thymeleaf.prefix=classpath:/templates/
# 在构建 URL 时添加到视图名称后的后缀(默认值: .html )
spring.thymeleaf.suffix=.html

如果你需要java config的方式配置可以参考下面:

	@Configuration
	@EnableWebMvc
	@ComponentScan
	public class WebViewConfig implements WebMvcConfigurer {
    /**
     * @Description: 注册jsp视图解析器
     */
    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/"); //配置放置jsp文件夹
        resolver.setSuffix(".jsp");
        resolver.setViewNames("jsp/*");  //重要 setViewNames 通过它识别为jsp页面引擎
        resolver.setOrder(2);
        return resolver;
    }
    /**
     * @Description: 注册html视图解析器
     */
    @Bean
    public ITemplateResolver templateResolver() {
        SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
        templateResolver.setTemplateMode("HTML");
        templateResolver.setPrefix("classpath:/templates/");
        templateResolver.setSuffix(".html");
        templateResolver.setCharacterEncoding("utf-8");
        templateResolver.setCacheable(false);
        return templateResolver;
    }

    /**
     * @Description: 将自定义tml视图解析器添加到模板引擎并主持到ioc
     */
    @Bean
    public SpringTemplateEngine templateEngine() {
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(templateResolver());
        return templateEngine;
    }
    /**
     * @Description: Thymeleaf视图解析器配置
     */
    @Bean
    public ThymeleafViewResolver viewResolverThymeLeaf() {
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setTemplateEngine(templateEngine());
        viewResolver.setCharacterEncoding("utf-8");
        viewResolver.setViewNames(new String[]{"thymeleaf"});
        viewResolver.setOrder(1);
        return viewResolver;
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    /**
     * @Description: 配置静态文件映射
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**").addResourceLocations("/WEB-INF/static/");
    }

参考Springboot2.x配置thymeleaf和jsp双视图解析器

(八)addResourceHandlers(静态资源处理器)

静态资源处理器可以将请求的url解析成静态文件(.txt、.jpg、.mp4等)。比如:访问http://localhost:8080/file/yes.png 就能获取到 C盘下的一张图片C:/file/yes.png

 @Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/file/**").addResourceLocations("file:C:/file/");
}

注意:配置好静态资源处理器后,会存在跨域问题。跨域是无法访问到静态资源的。可以参考Java config实现跨域 解决。

配置静态资源访问路径的跨域支持可能因为拦截器失效。参考以下解决内容来源
在这里插入图片描述

@Bean
public FilterRegistrationBean<CorsFilter> corsFilter() {
    // 跨域配置
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins(Arrays.asList("*"));
    configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "HEAD", "DELETE", "OPTION")));
    configuration.setAllowedHeaders(Arrays.asList("*"));
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);

    // 有多个filter时此处可设置改CorsFilter的优先执行顺序,保证CorsFilter在其他过滤器之前执行(避免其他过滤器执行异常,导致CorsFilter没执行,从而导致跨域失效)
    FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
    bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
    return bean;
}

(九)其他

SpringBoot配置接口:WebMvcConfigurer

五、过滤器

过滤器和拦截器的区别:
1.过滤器是servlet中的对象,拦截器是框架中的对象
2.过滤器实现Filter接口对象,拦截器是实现HandleInterceptor
3.过滤器是用来设置request,response参数、属性,侧重对数据的过滤;拦截器是用来验证请求的,能截断请求
4.过滤器是在拦截器之前执行的
5.过滤器是tomcat服务器创建的对象,拦截器是springmvc容器创建的对象
6.过滤器是一个执行时间点;拦截器是三个执行时间点
7.过滤器可以处理jsp、js、html等;拦截器是侧重拦截Controller的对象,如果你的请求不能被DispatcherServlet接收,这个请求不会执行拦截器的内容
8.拦截器拦截普通类方法执行,过滤器过滤servlet请求响应
在这里插入图片描述

使用过滤器:参考过滤器配置的两种方法

  1. 使用注解@Order(1) + @WebFilter(filterName = “myFilter1”,urlPatterns = {“/*”})
@Order(1)
@WebFilter(filterName = "myFilter1",urlPatterns = {"/*"})
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("初始化过滤器");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("进入目标资源之前先干点啥");
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("处理一下服务端返回的response");
    }

    @Override
    public void destroy() {
        System.out.println("过滤器被销毁了");
    }
}
  1. java config 配置
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("初始化过滤器");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("进入目标资源之前先干点啥");
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("处理一下服务端返回的response");
    }

    @Override
    public void destroy() {
        System.out.println("过滤器被销毁了");
    }
}

@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean registFilter(){
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new MyFilter());
        registrationBean.addUrlPatterns("/*");
        registrationBean.setName("Filter1");
        registrationBean.setOrder(1);
        return registrationBean;
    }
}

六、RestTemplate

RestTemplate官网

(一)get

get请求一般有以下参数:

  • url–请求的地址
  • responseType–返回值的类型
  • uriVariables–请求url上的请求url参数。比如:/get/2/{id}/{name} 或者 /get/2?id={id}&name={name}
package com.lihua.springbootweb.controller;


/**
 * @author lihua
 * @date 2023/1/29 9:11
 **/
@Controller
public class RestTemplateController {

    @Autowired
    private RestTemplate restTemplate;

    //----------------get--------------- 返回 Object 类型。

    @GetMapping("/get/1")
    @ResponseBody
    public String get1(){
        return "/get/1-> ok";
    }

    /**
     * 不带请求参数的get请求。
     * getForObject 返回 一个Object
     * @return
     */
    @GetMapping("/restTemplate/get/1")
    @ResponseBody
    public String restTemplateGet1(){
        String url = "http://127.0.0.1:8080/get/1";
        String forObject = restTemplate.getForObject(url, String.class);
        return forObject;
    }

    @GetMapping("/get/2/{id}/{name}")
    @ResponseBody
    public String get2(@PathVariable("name") String name,@PathVariable("id") String id){

        return "/get/2-> ok。name="+name +", id="+id;
    }

    /**
     * 带请求参数的get请求。
     * getForObject 返回 一个Object
     * @return
     */
    @GetMapping("/restTemplate/get/2")
    @ResponseBody
    public String restTemplateGet2(){
        String name="lihua";
        String id="111";
        //注意,这里会根据url的占位符{} 将对于的参数注入到url上
        String url = "http://127.0.0.1:8080/get/2/{id}/{name}";
        // String url = "http://127.0.0.1:8080/get/2?id={id}&name={name}";
        String forObject = restTemplate.getForObject(url, String.class,id,name);

        //当然你也传递一个map类型的url参数。
        //Map<String,String> requestPram = new HashMap<>();
        //requestPram.put("id","111");
        //requestPram.put("name","lihua");
        //String forObject1 = restTemplate.getForObject(url, String.class, requestPram);

        return forObject;
    }

    //----------------get--------------- 返回 Entity 类型。


    @GetMapping("/get/3")
    @ResponseBody
    public String get3(){
        return "/get/3-> ok";
    }

    /**
     * 不带请求参数的get请求。
     * getForEntity 返回 一个Entity
     *
     * getForEntity 也是有三个重载的方法,用法基本和getForObject一致
     *
     * @return
     */
    @GetMapping("/restTemplate/get/3")
    @ResponseBody
    public String restTemplateGet3(){
        String url = "http://127.0.0.1:8080/get/3";
        ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);
        String body = forEntity.getBody();

        System.out.println(forEntity.getStatusCode());
        System.out.println(forEntity.toString());
        HttpHeaders headers = forEntity.getHeaders();
        return body;
    }

}

(二)post

在这里插入图片描述

翻译:通过将给定对象POST到URI模板来创建新资源,并返回在响应中找到的表示。
URI模板变量使用给定的映射展开。
请求参数可以是HttpEntity,以便向请求添加额外的HTTP头。
实体的主体或请求本身可以是创建多部分请求的MultiValueMap。MultiValueMap中的值可以是表示部件主体的任何Object,也可以是表示具有主体和头部的部件的HttpEntity。

参数:

  • url–请求的地址
  • request–post请求携带的内容。比如请求参数和请求头
  • responseType–返回值的类型
  • uriVariables–请求url上的请求url参数。比如:/get/2/{id}/{name} 或者 /get/2?id={id}&name={name}

Family.java

/**
 * @author lihua
 * @date 2022/12/27 17:15
 **/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Family {
    private String name;
    private int num;
}
package com.lihua.springbootweb.controller;

/**
 * @author lihua
 * @date 2023/1/29 9:11
 **/
@Controller
public class RestTemplateController {

    @Autowired
    private RestTemplate restTemplate;

    //----------------post--------------- 返回 Object 类型。

    @PostMapping("/post/1/{v}")
    @ResponseBody
    public String post1(@RequestBody Family family,@PathVariable("v") String v){
        System.out.println(v);
        System.out.println(family);
        return "/post/1-> ok";
    }

    /**
     * post请求。
     * postForObject 返回 一个Object
     * @return
     */
    @PostMapping("/restTemplate/post/1")
    @ResponseBody
    public String restTemplatePost1(){
        String url = "http://127.0.0.1:8080/post/1/{v}";

        String v="1";

        //方式一:无请求头
        HashMap<String, String> request1 = new HashMap<>();
        request1.put("name","lihua");
        request1.put("num","123");
        //String s = restTemplate.postForObject(url, request1, String.class, v);


        //方式二:有请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HashMap<String, Object> body1 = new HashMap<>();
        body1.put("name","lihua");
        body1.put("num","123");
        HttpEntity<HashMap<String, Object>> httpEntity = new HttpEntity<>(body1,headers);
        //String s1 = restTemplate.postForObject(url, httpEntity, String.class, v);



        //方式三:java Obj
        Family family = new Family();
        family.setName("lihua");
        family.setNum(11);
        String s2 = restTemplate.postForObject(url, family, String.class, v);
        return s2;
    }

}

如果不需要uriVariables参数可以删除掉。postForEntity的用法跟postForObject基本一致,只是返回值不一样。

注意:并不是post请求一定要使用MultiValueMap 类型存放请求参数(MultiValueMap<String,0bject> paramMap = new LinkedMultiValueMap<>( );)。需要根据你调用的请求的参数决定。

MultiValueMap 与 Map的区别。MultiValueMap 是这样定义的:public interface MultiValueMap<K, V> extends Map<K, List< V >> 也就是一个key有多个value。Map一个key只有一个value。

服务提供者的参数如果是对象类型需要使用@RequestBody 修饰。否则数据无法绑定成功。

服务提供者:

@PostMapping("/post/1/{v}")
@ResponseBody
public String post1(@RequestBody Family family,@PathVariable("v") String v){
    System.out.println(v);
    System.out.println(family);
    return "/post/1-> ok";
}

使用 MultiValueMap的例子。

@RequestMapping("/producer")
@RestController
public class ProducerController {

    @PostMapping("/upload")
    public JSONObject upload(@RequestParam("file") MultipartFile file, HttpServletRequest request)  {
        //处理文件上传业务代码
        return new JSONObject();
    }

}
@RequestMapping("/consumer")
@RestController
public class ConsumerController {

    @PostMapping("/invokeUpload")
    public JSONObject invokeUpload(){
        RestTemplate restTemplate = new RestTemplate();
		String url = "http://127.0.0.1:8080/producer/upload";
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);

        File file = new File("/Users/zk/Downloads/readme.txt");
        MultiValueMap<String, Object> form = new LinkedMultiValueMap<>();
        form.add("file", new FileSystemResource(file));
        form.add("filename",file.getName());

        HttpEntity<MultiValueMap<String, Object>> files = new HttpEntity<>(form, headers);

        JSONObject result = restTemplate.postForObject(url, files, JSONObject.class);
        return result;
    }
}

(三)Exchange

Exchange方式既可以访问get、也可以访问post。

可以查看postForObject 方法的源码,发现postForObject方法是通过调用exchange方法实现的。

在这里插入图片描述


@Component
public class RestTemplateUtils {

    private static RestTemplate restTemplate;

    public RestTemplateUtils(RestTemplate restTemplate) {
        RestTemplateUtils.restTemplate = restTemplate;
    }

    public static String sendSimple(String url) {
        return sendSimple(url, null, HttpMethod.GET, new HttpHeaders());
    }

    public static String sendSimple(String url, Map<String, ?> urlParam) {
        return sendSimple(url, urlParam, HttpMethod.GET);
    }

    public static String sendSimple(String url, Map<String, ?> urlParam, HttpHeaders headers) {
        return sendSimple(url, urlParam, HttpMethod.GET, headers);
    }

    public static String sendSimple(String url, Map<String, ?> urlParam, HttpMethod method) {
        return sendSimple(url, urlParam, method, new HttpHeaders());
    }

    /**
     * 发送简单请求,不含body
     *
     * @param url      url
     * @param urlParam 用?和&拼接在url后面的参数
     * @param method   请求方式
     * @param headers  请求头
     * @return body
     */
    public static String sendSimple(String url, Map<String, ?> urlParam, HttpMethod method, HttpHeaders headers) {
        if (urlParam == null) {
            urlParam = new HashMap<>(0);
        }
        // url参数拼接
        url = handleUrlParam(url, urlParam);

        HttpEntity<MultiValueMap<String, ?>> requestEntity = new HttpEntity<>(null, headers);

        return restTemplate.exchange(url, method, requestEntity, String.class, urlParam).getBody();
    }

    public static String sendForm(String url, Map<String, ?> body) {
        return sendForm(url, null, body, HttpMethod.POST, new HttpHeaders());
    }

    public static String sendForm(String url, Map<String, ?> urlParam, Map<String, ?> body) {
        return sendForm(url, urlParam, body, HttpMethod.POST, new HttpHeaders());
    }

    public static String sendForm(String url, Map<String, ?> urlParam, Map<String, ?> body, HttpMethod method) {
        return sendForm(url, urlParam, body, method, new HttpHeaders());
    }

    public static String sendForm(String url, Map<String, ?> urlParam, Map<String, ?> body,
                                  HttpMethod method, HttpHeaders headers) {
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        return send(url, urlParam, body, method, headers);
    }

    public static String sendJson(String url, Map<String, ?> body) {
        return sendJson(url, null, body, HttpMethod.POST, new HttpHeaders());
    }

    public static String sendJson(String url, Map<String, ?> urlParam, Map<String, ?> body) {
        return sendJson(url, urlParam, body, HttpMethod.POST, new HttpHeaders());
    }

    public static String sendJson(String url, Map<String, ?> urlParam, Map<String, ?> body, HttpMethod method) {
        return sendJson(url, urlParam, body, method, new HttpHeaders());
    }

    public static String sendJson(String url, Map<String, ?> urlParam, Map<String, ?> body,
                                  HttpMethod method, HttpHeaders headers) {
        headers.setContentType(MediaType.APPLICATION_JSON);
        return send(url, urlParam, body, method, headers);
    }

    /**
     * 复杂请求发送
     *
     * @param url      url
     * @param urlParam 用?和&拼接在url后面的参数
     * @param body     请求体
     * @param method   请求方式
     * @param headers  请求头
     * @return body
     */
    public static String send(String url, Map<String, ?> urlParam, Map<String, ?> body, HttpMethod method,
                              HttpHeaders headers) {
        if (urlParam == null) {
            urlParam = new HashMap<>(0);
        }
        // url参数拼接
        url = handleUrlParam(url, urlParam);

        HttpEntity<Map<String, ?>> requestEntity = null;
        if (Objects.equals(headers.getContentType(), MediaType.APPLICATION_JSON)) {
            requestEntity = new HttpEntity<>(body, headers);
        }
        if (Objects.equals(headers.getContentType(), MediaType.APPLICATION_FORM_URLENCODED)) {
            // body参数处理
            MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
            Iterator<? extends Map.Entry<String, ?>> iterator = body.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, ?> next = iterator.next();
                param.add(next.getKey(), next.getValue());
            }
            requestEntity = new HttpEntity<>(param, headers);
        }

        return restTemplate.exchange(url, method, requestEntity, String.class, urlParam).getBody();
    }

    /**
     * url参数拼接
     *
     * @param url
     * @param urlParam
     * @return
     */
    private static String handleUrlParam(String url, Map<String, ?> urlParam) {
        if (urlParam == null || urlParam.isEmpty()) {
            return url;
        }
        Iterator<? extends Map.Entry<String, ?>> iterator = urlParam.entrySet().iterator();
        StringBuilder urlBuilder = new StringBuilder(url);
        urlBuilder.append("?");
        while (iterator.hasNext()) {
            Map.Entry<String, ?> entry = iterator.next();
            urlBuilder.append(entry.getKey()).append("={").append(entry.getKey()).append("}").append("&");
        }
        urlBuilder.deleteCharAt(urlBuilder.length() - 1);
        return urlBuilder.toString();
    }
}


七、WebSocket

spring boot Websocket(使用笔记)

最全面的SpringMVC教程(六)——WebSocket

八、模板引擎

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据持久化-RDB-AOF

定义 将数据从掉电易失的内存放到永久储存的设备上 因为所有的数据都在内存是&#xff0c;所有必须得持久化 redis提供两种持久化方案 RDB默认开启、AOF RDB 1,保存真是的数据 2&#xff0c;将服务器包含的所有数据库数据以二进制文件形式保存到磁盘里面 3&#xff0c;默认…

JDBC管理事务

基本介绍 就是处理在mysql的事务 复习一下:事务是一组sql语句需要开启和提交&#xff0c;事务中的sql语句要么全部生效&#xff0c;要么全部不生效&#xff0c;提交之后就是全部生效&#xff0c;中间可以设置保存点&#xff0c;回退到保存点&#xff0c;或直接回退到最开始事务…

1.2.1存储结构:层次化存储结构、外存(辅存)、内存(主存)、CPU内部的寄存器、Cache(相联存储器)

1.2.1存储结构&#xff1a;层次化存储结构、外存&#xff08;辅存&#xff09;、内存&#xff08;主存&#xff09;、CPU内部的寄存器、Cache&#xff08;相联存储器&#xff09;存储系统--层次化存储结构外存&#xff08;辅存&#xff09;内存&#xff08;主存&#xff09;CPU…

并发编程学习(八):ReentrantLock

ReentrantLock 是java.util.concurrent.locks包下的类。相对于synchronized,它具备如下特性&#xff1a;可中断。可以设置超时时间。可以设置公平锁。支持多个条件变量。即可以有个多个waitset等待队列。与synchronized都支持可重入。ReentrantLock的基本语法&#xff1a;// 获…

数学建模相关竞赛零基础上手与入门介绍

文章目录1、赛事介绍与报名2、学习与训练2.1 比赛题目选择范围2.2 赛前组队与分工2.3 比赛时间分配1、赛事介绍与报名 什么是数学建模&#xff1f; 定义&#xff1a; 生活中的各种问题(如股票预测、火灾报警统计等)&#xff0c;运用数学的方式去阐述并解决它。 数学建模赛事 …

cisp证书含金量怎么样?值不值得考?

这是CISP考试报名条件参考&#xff1a; 成为CISP&#xff0c;必须满足以下基本要求&#xff1a; 申请CISE、CISO注册资质&#xff0c;需满足以下教育和工作经验要求&#xff1a; &#xff08;1&#xff09;教育和工作经历要求&#xff1a;硕士及硕士以上学历&#xff0c;具备…

LabVIEW在实时目标上使用文件路径

LabVIEW在实时目标上使用文件路径文件路径和结构因目标操作系统而异。本文档讨论了推荐的LabVIEW编码实践&#xff0c;用于指定文件路径&#xff0c;以便应用程序可以无缝地从目标移动目标。实时操作系统选项所有NI实时控制器运行三种不同的操作系统之一&#xff0c;即PharLap、…

Find My资讯|美国航班取消,出行者疯狂购买苹果AirTag追踪行李箱

美国西南航空&#xff08;Southwest Airlines Co.&#xff09;由于所使用的 SkySolver 系统在圣诞假期间崩溃&#xff0c;导致航班出现大面积延误或取消&#xff08;大约 13000 个航班受到影响&#xff09;&#xff0c;让公司损失超过 8 亿美元&#xff08;当前约 53.76 亿元人…

5.kafka--生产调优

文章目录Leader Partition负载均衡消费者初始化流程消费者再平衡生产者和消费者如何提高吞吐量如何发送大消息Leader Partition负载均衡 参数名称描述auto.leader.rebalance.enable默认是true。自动LeaderPartition平衡。生产环境中&#xff0c;leader重选举的代价比较大&…

ADB快速入门

ADB快速入门 一、 简介 Android Debug Bridge&#xff0c;我们一般简称为adb&#xff0c;主要存放在sdk安装目录下的platform-tools文件夹中&#xff0c;它是一个非常强大的命令行工具&#xff0c;通过这个工具用来连接电脑和Android设备&#xff08;手机、电脑、电视、平板、…

LIO-SAM代码解析——imuPreintegration.cpp

目录imuPreintegration.cpp1. TransformFusion 类1.1. lidarOdometryHandler1.2. imuOdometryHandler2. IMUPreintegration 类2.1. imuHandler2.2. odometryHandler⭐2.2.1. 初始化系统, 把初始的lidar位姿&#xff0c;速度&#xff0c;零偏加入到因子图中2.2.2. 将两帧之间的i…

【深度学习】简述CNN分类网络的演变脉络及各自的贡献与特点

问题 简述CNN分类网络的演变脉络及各自的贡献与特点 综述 深度学习的浪潮就是从CNN开始的,它结构形态的变化也见证着这门技术的发展。现在涌进来学习深度学习的大部分人都是做计算机视觉的,因为这个门槛相对较低,业界数据集开源了很多,也比较直观,而且对硬件设备的要求…

2023年二月份图形化四级打卡试题

活动时间 从2023年 1月1日至1月21日&#xff0c;每天一道编程题。 本次打卡的规则如下&#xff1a; &#xff08;1&#xff09;小朋友每天利用10~15分钟做一道编程题&#xff0c;遇到问题就来群内讨论&#xff0c;我来给大家答疑。 &#xff08;2&#xff09;小朋友做完题目后&…

Spring Boot 整合Redis分布式锁 Lua脚本

参考&#xff1a;微服务 Spring Boot 整合Redis分布式锁 Lua脚本 实现优惠卷秒杀 一人一单_Bug 终结者的博客-CSDN博客 一、什么是Lua&#xff1f; Lua 是一个小巧的脚本语言。 其设计目的是为了通过灵活嵌入应用程序中从而为应用程序提供灵活的扩展和定制功能。Lua由标准C编…

MySQL事务学习笔记

事务就是保证一组数据库操作&#xff0c;要么全部成功&#xff0c;要么全部失败。事务的实现是在引擎层&#xff0c; 因此我们说的是InnoDB的事务。为何需要事务&#xff1f;比如有一个转钱的业务&#xff0c;A给B转100&#xff0c; 那么就是两条sql语句&#xff0c;一个是A的钱…

Spring Boot 热部署(热加载)

idea 热部署作用&#xff1a;自动帮开发者重启 spring boot 项目&#xff0c;从而达到修改代码之后能够“实时”的看到最新的效果1.添加热部署框架支持<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</…

Spring和Spring Boot的区别

&#x1f3c6;今日学习目标&#xff1a; &#x1f340;Spring和Spring Boot的区别 ✅创作者&#xff1a;林在闪闪发光 ⏰预计时间&#xff1a;30分钟 &#x1f389;个人主页&#xff1a;林在闪闪发光的个人主页 &#x1f341;林在闪闪发光的个人社区&#xff0c;欢迎你的加入: …

双目立体匹配(传统/深度)方法总结

双目立体匹配工作--2022年度总述投影几何标定单目标定双目标定校正立体匹配传统方法深度学习方法合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式总述 投影几何 标定 摄像机参数&#xff1a;一般一共15个相关参数&#xff1a; &#xff08;1&#xff09;外参数…

详解目前最火的自主泊车技术

/ 导读 /谈起目前的自动驾驶技术&#xff0c;民众们最为熟悉的一定是已经走入寻常百姓家的辅助驾驶功能。对比起L4甚至更高级别的自动驾驶&#xff0c;司机们更相信将方向盘能时刻掌握在自己手中&#xff0c;如果有突发情况可以及时地进行接管。而目前消费者已经能体验到的辅助…

prometheus安装及使用入门

文章目录前言一. prometheus介绍1.1 prometheus的起源2.1 prometheus的特点二. prometheus的安装2.1 实验环境2.2 安装前准备2.3 开始安装prometheus2.3.1 下载并解压prometheus server2.3.2 安装过程2.3.3 启动并查看端口2.3.4 打开浏览器查看图形界面总结前言 云原生四象限&…