【JavaEE进阶】——SpringBoot 统⼀功能处理

news2024/9/22 7:37:00

目录

🚩拦截器

🎈什么是拦截器?

🎈如何使用拦截器

🎓自定义拦截器

🎓注册拦截器

🎈拦截器详解

🎓拦截路径

🎓拦截器执⾏流程

🔴DispatcherServlet 源码分析(了解)

🚩适配器模式

🎈适配器模式的实现

🚩统⼀数据返回格式

🎈使用

🎈测试

🎓返回的类型是Result类型

🎓返回的类型是String类型 

🎓返回的类型是Integer/Boolean类型

🚩统⼀异常处理

🚩总结


🚩拦截器

我们再实现了图书管理系统之后,如果不登录,就不能进行进入图书列表页和不能参与一些功能操作。所以我们需要强制登录操作。在之前的登录操作都是从HttpSession中获取session对象,看是否存在,如果不存在就表示未登录,如果存在就表示登陆状态。这种操作是比较繁琐的,每个接口都需要进行校验。

 //1.判断用户是否登录
        //如果用户信息为空, 说明用户未登录
        UserInfo loginUserInfo = (UserInfo) session.getAttribute(constants.USER_SESSION_KEY);
        if (loginUserInfo==null || loginUserInfo.getId()<=0){
            return Result.nologin("用户未登录");
        }

有一种简单的办法,统一拦截所有的请求,并进行session校验——拦截器.


如果用户未登录,那么就直接拦截,不用每个接口都要进行session校验。


🎈什么是拦截器?

拦截器是Spring框架提供的核⼼功能之⼀, 主要⽤来拦截⽤⼾的请求, 在指定⽅法前后, 根据业务需要执⾏预先设定的代码。
也就是说,允许开发人员 提前预定义一些逻辑,在用户的请求响应前后执行,也就是在用户请求前阻止其执行。
在拦截器中,开发人员可以在应用程序中做一些通用性的操作,比如通过拦截器来拦截前端发来的请求,判断session中是否有登录用户的信息,如果有就可以放行,如果没有就进行拦截。

举个例子:

比如我们去银行办理业务中,在办理业务的前后,就可以进行一些拦截的操作。

办理业务之前,先取号,如果带身份证了那么就取号成功,如果没有带那么取号失败。

业务办理结束之后,要给业务办理人员的服务进行评价。

这些都是拦截器做的工作。


🎈如何使用拦截器

🎓自定义拦截器

一、实现HandlerInterceptor接⼝,并重写其所有⽅法

Interceptor是拦截器的意思。

/**
 * 自定义拦截器
 */
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
    //请求处理前,执行的逻辑,类似于安保查证件(true放行 ,false拦截)
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("LoginInterceptor preHandle");
        return true;
    }
    //请求处理之后,处理的逻辑
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("LoginInterceptor postHandle");
    }
    //视图渲染后,执行的逻辑
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("LoginInterceptor afterCompletion");
    }
}
  • preHandle()⽅法:⽬标⽅法执⾏前执⾏. 返回true: 继续执⾏后续操作; 返回false: 中断后续操作.
  • postHandle()⽅法:⽬标⽅法执⾏后执⾏
  •  afterCompletion()⽅法:视图渲染完毕后执⾏,最后执⾏(后端开发现在⼏乎不涉及视图, 暂不了解)

🎓注册拦截器

二、注册配置拦截器:实现WebMvcConfigurer接⼝,并重写addInterceptors⽅法
package com.example.cl.config;

import com.example.cl.interceptor.LoginInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 注册配置拦截器
 */
@Configuration
@Slf4j
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    public LoginInterceptor loginInterceptor;//自定义拦截器

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //注册自定义拦截器
        registry.addInterceptor(loginInterceptor).addPathPatterns("/**")
                .excludePathPatterns("/user/login")
                .excludePathPatterns("/**/*.js")
                .excludePathPatterns("/**/*.css")
                .excludePathPatterns("/**/*.pic")
                .excludePathPatterns("/**/*.html");
    }
}

1.和用户是否登录无关,都能打印再控制台上。 (还没有进行具体拦截操作,首先打印日志观察效果)

2.用户未登录状态

请求处理前,如果用户登录那么就返回true,就执行下面操作,如果用户未登录就返回false,拦截后面的操作。再观察运⾏结果

    //请求处理前,执行的逻辑,类似于安保查证件(true放行 ,false拦截)
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session=request.getSession(false);
        if(session!=null&&session.getAttribute(constants.USER_SESSION_KEY)!=null){
            log.info("LoginInterceptor preHandle");
           return true;
        }
        response.setStatus(401);
        log.info("————————————登录失败,用户未登录————————————");
        return false;
    }

返回false,直接拦截后续的操作。

3.用户登录状态


可以看到preHandle ⽅法执⾏之后就放⾏了, 开始执⾏⽬标⽅法, ⽬标⽅法执⾏完成之后执⾏
postHandle和afterCompletion⽅法.

🎈拦截器详解

拦截器的⼊⻔程序完成之后,接下来我们来介绍拦截器的使⽤细节。拦截器的使⽤细节我们主要介绍 两个部分:
  • 1. 拦截器的拦截路径配置
  • 2. 拦截器实现原理

🎓拦截路径

拦截路径是指我们定义的这个拦截器, 对哪些请求⽣效.
我们在注册配置拦截器的时候, 通过 addPathPatterns() ⽅法指定要拦截哪些请求 . 也可以通过 excludePathPatterns() 指定不拦截哪些请求.
上述代码中, 我们配置的是 /** , 表⽰拦截所有的请求.
⽐如⽤⼾登录校验, 我们希望可以对除了登录之外所有的路径⽣效
 //注册自定义拦截器
        registry.addInterceptor(loginInterceptor).addPathPatterns("/**")
                .excludePathPatterns("/user/login")
                .excludePathPatterns("/**/*.js")
                .excludePathPatterns("/**/*.css")
                .excludePathPatterns("/**/*.pic")
                .excludePathPatterns("/**/*.html");

🎓拦截器执⾏流程

有了拦截器之后,会在调⽤ Controller 之前进⾏相应的业务处理,执⾏的流程如

  • 1. 添加拦截器后, 执⾏Controller的⽅法之前, 请求会先被拦截器拦截住. 执⾏ preHandle() ⽅法, 这个⽅法需要返回⼀个布尔类型的值. 如果返回true, 就表⽰放⾏本次操作, 继续访问controller中的 ⽅法. 如果返回false,则不会放⾏(controller中的⽅法也不会执⾏).
  • 2. controller当中的⽅法执⾏完毕后,再回过来执⾏ postHandle() 这个⽅法以及afterCompletion() ⽅法,执⾏完毕之后,最终给浏览器响应数据

🔴DispatcherServlet 源码分析(了解)

观察我们的服务启动日志

当Tomcat启动之后, 有⼀个核⼼的类DispatcherServlet, 它来控制程序的执⾏顺序.
所有请求都会先进到 DispatcherServlet ,执⾏ doDispatch 调度⽅法.进行初始化操作
如果有 拦截器, 会先执⾏拦截器 preHandle() ⽅法的代码, 如果 preHandle() 返回true
继续访问 controller中的⽅法 . controller 当中的⽅法执⾏完毕后,再回过来执⾏ postHandle() afterCompletion() ,返回给 DispatcherServlet, 最终给浏览器响应数据.


🚩适配器模式

HandlerAdapter 在 Spring MVC 中使⽤了适配器模式。
适配器模式定义
  • 适配器模式, 也叫包装器模式. 将⼀个类的接⼝,转换成客⼾期望的另⼀个接⼝, 适配器让原本接⼝不兼容的类可以合作⽆间.
  • 简单来说就是⽬标类不能直接使⽤, 通过⼀个新类进⾏包装⼀下, 适配调⽤⽅使⽤. 把两个不兼容的接⼝通过⼀定的⽅式使之兼容.
⽐如下⾯两个接⼝, 本⾝是不兼容的(参数类型不⼀样, 参数个数不⼀样等等)
可以使用适配器方式,进行兼容

日常生活中,适配器模式是非常常见的,比如转换插头,网络转换头等等。

出国旅⾏必备物品之⼀就是转换插头. 不同国家的插头标准是不⼀样的, 出国后我们⼿机/电脑充电器 可能就没办法使⽤了. ⽐如美国电器 110V,中国 220V,就要有⼀个适配器将 110V 转化为 220V. 国内也经常使⽤转换插头把两头转为三头, 或者三头转两头
适配器模式⻆⾊
Target: ⽬标接⼝ (可以是抽象类或接⼝), 客⼾希望直接⽤的接⼝
Adaptee: 适配者, 但是与Target不兼容
Adapter: 适配器类, 此模式的核⼼. 通过继承或者引⽤适配者的对象, 把适配者转为⽬标接⼝
client: 需要使⽤适配器的对象

🎈适配器模式的实现

场景: 前⾯学习的slf4j 就使⽤了适配器模式, slf4j提供了⼀系列打印⽇志的api, 底层调⽤的是log4j 或者logback来打⽇志, 我们作为调⽤者, 只需要调⽤slf4j的api就⾏了.
/**
 * slf4j接口
 */
public interface Slf4jLog { 
    void log(String message);
}
/**
 * log4j接口
 */
public class Log4j {
    public void log4jPrint(String message){
        System.out.println("我是Log4j,打印日志内容为:"+message);
    }
}


/**
 *  slf4j和log4j适配器
 */
public class Log4jAdapter implements Slf4jLog{
    private Log4j log4j;

    public Log4jAdapter(Log4j log4j) {
        this.log4j = log4j;
    }

    @Override
    public void log(String message) {
        log4j.log4jPrint("我是适配器,打印日志为:"+message);
    }
}

/**
 * 客户端调用
 */
public class Main {
    public static void main(String[] args) {
        Slf4jLog slf4jLog=new Log4jAdapter(new Log4j());
        slf4jLog.log("我是客户端");
    }
}

可以看出, 我们不需要改变log4j的api,只需要通过适配器转换下, 就可以更换⽇志框架, 保障系统的平稳运⾏.
适配器模式应⽤场景
⼀般来说,适配器模式可以看作⼀种"补偿模式",⽤来补救设计上的缺陷. 应⽤这种模式算是"⽆奈之举", 如果在设计初期,我们就能协调规避接⼝不兼容的问题, 就不需要使⽤适配器模式了 ,所以适配器模式更多的应⽤场景主要是对正在运⾏的代码进⾏改造, 并且希望可以复⽤原有代码实现新的功能. ⽐如版本升级等.


🚩统⼀数据返回格式

强制登录案例中, 我们共做了两部分⼯作
1. 通过Session来判断⽤⼾是否登录
2. 对后端返回数据进⾏封装, 告知前端处理的结果
后端统一返回结果


🎈使用

统⼀的数据返回格式使⽤ @ControllerAdvice 和 ResponseBodyAdvice 的⽅式实现
@ControllerAdvice 表⽰控制器通知类 ,添加类 ResponseAdvice , 实现 ResponseBodyAdvice 接⼝, 并在类上添加 @ControllerAdvice 注解
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    @Autowired
    private ObjectMapper objectMapper;
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @Override
    @SneakyThrows
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        return Result.success(body);
    }
}
  • supports⽅法: 判断是否要执⾏beforeBodyWrite⽅法. true为执⾏, false不执⾏. 通过该⽅法可以选择哪些类或哪些⽅法的response要进⾏处理, 其他的不进⾏处理.
  • beforeBodyWrite⽅法: 对response⽅法进⾏具体操作处理

🎈测试

🎓返回的类型是Result类型

postman测试登录功能,我们发现嵌套了一层。

我们可以改进一下。

    @Override
    @SneakyThrows
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if(body instanceof Result<?>){
            return body;
        }
        return Result.success(body);
    }

如果body的类型就是Result,那么直接返回body即可。

此时返回数据正常。


🎓返回的类型是String类型 

@RequestMapping("/user")
@RestController
public class UserController {
    @Autowired
    public UserService userService;
    @RequestMapping(value = "/login",produces = "application/json")
    public String login(String userName, String password, HttpSession session){
        //1.校验参数
        if(!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)){
            return "用户名或者密码为空";
        }
        //2.验证密码是否正确
        UserInfo userInfo=userService.getUserInfoByName(userName);//从前端获取到用户名和密码
        if(userInfo==null){
            return "用户不存在";
        }
        if(!password.equals(userInfo.getPassword())){
            return "密码错误";
        }
        //3.正确的情况
        //setAttributes中的参数是键值对方式,当后面获取session的时候,用constants.USER_SESSION_KEY即可获得userInfo
        session.setAttribute(constants.USER_SESSION_KEY,userInfo);
        return "";
    }
}

解决方案:

        if(body instanceof String){
            return objectMapper.writeValueAsString(Result.success(body));
        }


🎓返回的类型是Integer/Boolean类型

@RestController
@RequestMapping("/test")
public class TestController {
    @RequestMapping("/t2")
    public Integer t2(){
        return 1;
    }
    @RequestMapping("/t3")
    public Boolean t3(){
        return true;
    }

}


多测试⼏种不同的返回结果, 发现只有 返回结果为String类型时才有这种错误发⽣.
如果返回结果为String类型, 使⽤SpringBoot内置提供的Jackson来实现信息的序列化

  • 1. ⽅便前端程序员更好的接收和解析后端数据接⼝返回的数据
  • 2. 降低前端程序员和后端程序员的沟通成本, 按照某个格式实现就可以了, 因为所有接⼝都是这样返回的.
  • 3. 有利于项⽬统⼀数据的维护和修改.
  • 4. 有利于后端技术部⻔的统⼀规范的标准制定, 不会出现稀奇古怪的返回内容
@Data
public class Result<T> {
    private ResultStatus code; //业务码  不是Http状态码  200-成功  -2 失败  -1 未登录
    private String errMsg; //错误信息, 如果业务成功, errMsg为空
    private T data;
}

🚩统⼀异常处理

统⼀异常处理使⽤的是 @ControllerAdvice + @ExceptionHandler 来实现的,
@ControllerAdvice 表⽰控制器通知类, @ExceptionHandler 是异常处理器,两个结合表
⽰当出现异常的时候执⾏某个通知,也就是执⾏某个⽅法事件 .
/**
 * 全局异常处理
 * @RestControllerAdvice=@ControllerAdvice+@ResponseBody
 */
@ControllerAdvice
@Slf4j
@ResponseBody
public class ExceptionAdvice {
    @ExceptionHandler
    public Result handException(Exception e){
        log.error("发生异常, e:"+e);
        return Result.fail("内部错误");
    }
}
类名, ⽅法名和返回值可以⾃定义, 重要的是注解
接⼝返回为数据时, 需要加 @ResponseBody 注解
以上代码表⽰,如果代码出现Exception异常(包括Exception的⼦类), 就返回⼀个 Result的对象, Result 对象的设置参考 Result.fail("内部错误")//一般抛出异常是不会具体告诉你什么错误.

我们可以针对不同的异常, 返回不同的结果
@ControllerAdvice
@Slf4j
@ResponseBody
public class ExceptionAdvice {
    @ExceptionHandler
    public Result handException(Exception e){
        log.error("发生异常, e:"+e);
        return Result.fail("内部错误");
    }

    @ExceptionHandler
    public Result handException(NullPointerException e){
        log.error("发生异常,e:"+e);
        return Result.fail("发生空指针异常");
    }

    @ExceptionHandler
    public Result handException(ArithmeticException e){
        log.error("发生异常,e:"+e);
        return Result.fail("发生算术异常");
    }
}

模拟制造异常

@RestController
@RequestMapping("/test")
public class TestController {
    @RequestMapping("/t1")
    public String t1(){
        int i=1/0;
        System.out.println(i);
        return "String";
    }
    @RequestMapping("/t2")
    public Integer t2(){
        String ret=null;
        System.out.println(ret.length());
        return 1;
    }
    @RequestMapping("/t3")
    public Boolean t3(){
        Integer[] integers=new Integer[]{1,2,3,4};
        System.out.println(integers[5]);
        return true;
    }
}
当有多个异常通知时,匹配顺序为 当前类及其⼦类向上依次匹配 .
由于i=1/0 算术异常,首先匹配的是自己当前异常类,再次是父类。
 String ret=null;
        System.out.println(ret.length());
 Integer[] integers=new Integer[]{1,2,3,4};
        System.out.println(integers[5]);
越界异常,再定义的异常中,没有,那么就找父类。

🚩总结

本章节主要介绍了SpringBoot 对⼀些统⼀功能的处理⽀持.
  • 1. 拦截器的实现主要分两部分: 1. 定义拦截器(实现HandlerInterceptor 接⼝) 2. 配置拦截器
  • 2. 统⼀数据返回格式通过@ControllerAdvice + ResponseBodyAdvice 来实现
  • 3. 统⼀异常处理使⽤@ControllerAdvice + @ExceptionHandler 来实现, 并且可以分异常来处理

山高路远。

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

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

相关文章

手机怎么看WiFi的IP地址

在如今数字化快速发展的时代&#xff0c;无线网络已成为我们日常生活中不可或缺的一部分。无论是工作、学习还是娱乐&#xff0c;我们可能都离不开WiFi的陪伴。然而&#xff0c;在使用WiFi的过程中&#xff0c;有时我们可能需要查看其IP地址&#xff0c;以便更好地管理我们的网…

大数据hive表和iceberg表格式

iceberg: https://iceberg.apache.org/ iceberg表&#xff0c;是一种面向大型分析数据集的开放表格式&#xff0c;旨在提供可扩展、高效、安全的数据存储和查询解决方案。它支持多种存储后端上的数据操作&#xff0c;并提供 ACID 事务、多版本控制和模式演化等特性&#xff0c…

K8S 中的 CRI、OCI、CRI shim、containerd

哈喽大家好&#xff0c;我是咸鱼。 好久没发文了&#xff0c;最近这段时间都在学 K8S。不知道大家是不是和咸鱼一样&#xff0c;刚开始学 K8S、Docker 的时候&#xff0c;往往被 CRI、OCI、CRI shim、containerd 这些名词搞得晕乎乎的&#xff0c;不清楚它们到底是干什么用的。…

持续集成01--Git版本管理及基础应用实践

前言 本系列文章旨在深入探讨持续集成/持续部署&#xff08;Continuous Integration/Continuous Deployment, CI/CD&#xff09;流程中的各个环节&#xff0c;而本篇将聚焦于Git版本管理及其基本应用。通过本文&#xff0c;读者将了解到Git的基本原理、安装配置、基本命令以及如…

当农业遇见智能:机器学习引领农作物管理新时代

机器学习引领农作物管理新时代 1. 引言1.1 农业的重要性和现代农作物管理的挑战1.2 机器学习技术在农业中的潜力和应用前景 2. 机器学习在农作物管理中的基础应用2.1 数据驱动的农业决策数据收集与处理示例代码&#xff1a;传感器数据采集决策支持系统 2.2 传感器技术与数据采集…

ArcGIS Enterprise 命令行组件创建配置

1. 创建ArcGIS Server站点 使用 createsite工具 命令行直接执行 createsite.sh [-u <arg>] [-p <arg>] [-d <arg>] [-c <arg>]执行文件 createsite.sh [-f <FILE>]安装目录下会有类似的创建站点文件&#xff1a; 修改其中的内容&#xff0c;…

python中的re模块--正则表达式

正则表达式&#xff0c;又称规则表达式。&#xff08;英语&#xff1a;Regular Expression&#xff0c;在代码中常简写为regex、regexp或RE&#xff09;&#xff0c;计算机科 学的一个概念。正则表达式通常被用来检索、替换那些符合某个模 式(规则)的文本 re模块作用 通过使用…

Android:创建自定义View

点击查看创建自定义view官网文档 一、简介 设计良好的自定义视图与任何其他精心设计的类一样。它通过一个简单的接口封装一组特定的功能&#xff0c;高效使用 CPU 和内存&#xff0c;诸如此类。除了是一个精心设计的类之外&#xff0c;自定义视图还必须执行以下操作&#xff1…

elementui 日历组件el-calendar使用总结

功能&#xff1a; 1.日历可以周视图、月视图切换&#xff1b; 2.点击月视图中日期可以切换到对应周视图&#xff1b; 3.点击周视图查看当日对应数据&#xff1b; 4.周、月视图状态下&#xff0c;点击前后按钮&#xff0c;分别切换对应上下的周、月&#xff1b; 5.点击回到…

MWA(Modern Web App)初学那些事-2-Basic HTML CSS

初学MWA(Modern Web App&#xff09;那些事-2-Basic HTML & CSS 目录 初学MWA(Modern Web App&#xff09;那些事-2-Basic HTML & CSS前言一、本节学习目标二、HTML基础内容2.1关键元素2.4 Scripts 三、CSS 基础内容3.1 级联样式表-用于设置网页样式和布局3.2 CSS规则语…

Docker的虚拟化安装、常用命令和使用案例

文章目录 一、Docker的虚拟机安装1、完成虚拟机的更新2、完成Docker安装3、配置镜像加速器 二、Docker常用命令三、Docker的容器创建四、理解虚拟机中的Docker容器 一、Docker的虚拟机安装 1、完成虚拟机的更新 详见我的文章。 2、完成Docker安装 yum list installed|grep …

筑梦未来,精准构建:Chief Architect Premier X10 for Mac,首席建筑师的专业之选

Chief Architect Premier X10 for Mac&#xff0c;是建筑设计领域的一款顶尖软件&#xff0c;专为追求卓越设计与精准构建的用户量身打造。它融合了先进的3D建模技术与直观的操作界面&#xff0c;让设计师能够轻松实现创意与现实的完美融合。 这款软件提供了丰富的设计工具与资…

axios 下载大文件时,展示下载进度的组件封装——js技能提升

之前面试的时候&#xff0c;有遇到一个问题&#xff1a;就是下载大文件的时候&#xff0c;如何得知下载进度&#xff0c;当时的回复是没有处理过。。。 现在想到了。axios中本身就有一个下载进度的方法&#xff0c;可以直接拿来使用。 下面记录一下处理步骤&#xff1a; 参考…

【C++之C++11特性知识】

C学习笔记---026 C之C11特性知识1、C11特性知识介绍2、auto关键字3、范围for4、列表初始化5、final 与 override关键字6、lambda表达式7、右值引用和移动语义8、智能指针9、类型推导&#xff08;decltype关键字&#xff09;10、参考文档 C之C11特性知识 前言&#xff1a; 前面…

LVS+Nginx高可用集群---keepalived原理与实战

1.高可用集群架构keepalived双机主备原理 高可用&#xff1a;(HA) 部署nginx存在两台nginx。当主节点的nginx宕机停止服务的时候&#xff0c;nginx备用机起到跟nginx(主) keepalived的概念&#xff1a;解决单点故障&#xff1b;组件免费&#xff1b;可以实现高可用HA机制&…

css-grid布局(栅格布局)

css新世界-auto-fit grid 一个比flex更强大的布局,适合做整体布局 grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); auto-fit的话有strech效果gap 不仅可以用于grid 也可用flex. 在grid-template-areas表示这个位置空着grid area 的 [a b]命名可重复命名 表示的…

AutoHotKey自动热键(十一)下载SciTE4AutoHotkey-Plus的中文增强版脚本编辑器

关于AutoHotkey的专用编辑器, SciTE4AutoHotkey是一个免费的基于 SciTE 的 AutoHotkey 脚本编辑器,除了 DBGp 支持, 它还为 AutoHotkey 提供了语法高亮, 调用提示, 参数信息和自动完成, 以及其他拥有的编辑特性和辅助工具.XDebugClient 是一个基于 .NET Framework 2.0 的简单开…

视频号矩阵系统,AI自动生成文案,实现批量上传视频和定时发布

在数字化浪潮席卷全球的今天&#xff0c;视频内容已成为信息传播的重要载体。然而&#xff0c;对于众多自媒体创作者和企业而言&#xff0c;如何高效、精准地发布视频内容&#xff0c;依然是一个不小的挑战。幸运的是&#xff0c;随着技术的不断进步&#xff0c;视频号矩阵系统…

SpringBatch文件读写ItemWriter,ItemReader使用详解

SpringBatch文件读写ItemWriter&#xff0c;ItemReader使用详解 1. ItemReaders 和 ItemWriters1.1. ItemReader1.2. ItemWriter1.3. ItemProcessor 2.FlatFileItemReader 和 FlatFileItemWriter2.1.平面文件2.1.1. FieldSet 2.2. FlatFileItemReader2.3. FlatFileItemWriter 3…

c++ primer plus 第16章string 类和标准模板库,16.1.3 使用字符串

c primer plus 第16章string 类和标准模板库,16.1.3 使用字符串 c primer plus 第16章string 类和标准模板库,16.1.3 使用字符串 文章目录 c primer plus 第16章string 类和标准模板库,16.1.3 使用字符串16.1.3 使用字符串程序清单16.3 hangman.cpp 16.1.3 使用字符串 现在&a…