项目4-图书管理系统2+统一功能处理

news2025/6/17 22:37:00

1. 拦截器(Interceptor)

我们完成了强制登录的功能, 后端程序根据Session来判断用户是否登录, 但是实现⽅法是比较麻烦的。

所需要处理的内容:

• 需要修改每个接⼝的处理逻辑
• 需要修改每个接⼝的返回结果
• 接⼝定义修改, 前端代码也需要跟着修改

很麻烦!!!

1.1 什么是拦截器

拦截器是Spring框架提供的核心功能之⼀, 主要用来拦截用户的请求, 在指定方法前后, 根据业务需要执行预先设定的代码

也就是说, 允许开发⼈员提前预定义⼀些逻辑, 在⽤⼾的请求响应前后执⾏. 也可以在⽤⼾请求前阻止其执行.
在拦截器当中,开发⼈员可以在应⽤程序中做⼀些通⽤性的操作, ⽐如通过拦截器来拦截前端发来的请求,判断Session中是否有登录用户的信息.如果有就可以放行,如果没有就进行拦截

1.2 拦截器的基本使用 

下⾯我们先来学习下拦截器的基本使⽤.
拦截器的使⽤步骤分为两步:
1. 定义拦截器
2. 注册配置拦截器

1.2.1 ⾃定义拦截器

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

package com.example.demo.component;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("LoginInterceptor ⽬标⽅法执⾏前执⾏..");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("LoginInterceptor ⽬标⽅法执⾏后执⾏");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("LoginInterceptor 视图渲染完毕后执⾏,最后执⾏");
    }
}

• preHandle()⽅法:⽬标⽅法执⾏前执⾏. 返回true: 继续执⾏后续操作; 返回false: 中断后续操作.
• postHandle()⽅法:⽬标⽅法执⾏后执⾏
• afterCompletion()⽅法:视图渲染完毕后执⾏,最后执⾏(后端开发现在⼏乎不涉及视图, 暂不了解)

1.2.2 注册配置拦截器

实现WebMvcConfigurer接⼝,并重写addInterceptors⽅法 

package com.example.demo.configuration;

import com.example.demo.component.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**");//设置拦截器拦截的请求路径( /** 表⽰拦截所有请求)
    }
}

启动服务, 试试访问任意请求, 观察后端⽇志

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

我们把拦截器中preHandle⽅法的返回值改为false, 再观察运⾏结果

 可以看到, 拦截器拦截了请求, 没有进⾏响应.

1.3 拦截器详解

拦截器的⼊⻔程序完成之后,接下来我们来介绍拦截器的使⽤细节。

拦截器的使⽤细节我们主要介绍两个部分:
1. 拦截器的拦截路径配置
2. 拦截器实现原理

1.3.1 拦截路径

拦截路径是指我们定义的这个拦截器, 对哪些请求⽣效.

我们在注册配置拦截器的时候,

通过 addPathPatterns() ⽅法指定要拦截哪些请求.(即就是让哪些地方的拦截操作生效)【上述代码中, 我们配置的是 /** , 表⽰拦截所有的请求】

也可以通过excludePathPatterns() 指定不拦截哪些请求

⽐如⽤⼾登录校验, 我们希望可以对除了登录之外所有的路径⽣效

在拦截器中除了可以设置 /** 拦截所有资源外,还有⼀些常⻅拦截路径设置:

拦截路径含义举例
/*⼀级路径能匹配/user,/book,/login,不能匹配 /user/login
/**任意级路径能匹配/user,/user/login,/user/reg
/book/*/book下的⼀级路径能匹配/book/addBook,不能匹配/book/addBook/1,/book
/book/**/book下的任意级路径能匹配/book,/book/addBook,/book/addBook/2,不能匹
配/user/login

//后缀名被拦截:*.html,访问后缀名为html资源时,过滤器都会被执行

 以上拦截规则可以拦截此项⽬中的使⽤ URL,包括静态⽂件(图⽚⽂件, JS 和 CSS 等⽂件)

1.3.2 拦截器执行流程

正常的调⽤顺序:

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

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

2.登录校验

学习拦截器的基本操作之后,接下来我们需要完成最后⼀步操作:通过拦截器来完成图书管理系统中 的登录校验功能

2.1 定义拦截器

从session中获取⽤⼾信息, 如果session中不存在, 则返回false,并设置http状态码为401, 否则返回true.
http状态码401: Unauthorized
Indicates that authentication is required and was either not provided or has failed. If the
request already included authorization credentials, then the 401 status code indicates that
those credentials were not accepted.
中⽂解释: 未经过认证. 指⽰⾝份验证是必需的, 没有提供⾝份验证或⾝份验证失败. 如果请求已经包含授权凭据,那么401状态码表⽰不接受这些凭据。

2.2 注册配置拦截器 

同时,我们调用方法时发现可以传递List<>

 故上述代码也可以改成

//asList->将数组转成list
//使用该方法 可以将一个变长参数或者数组转换成List

删除之前的登录校验代码  

运行程序, 通过Postman进⾏测试:
1. 查看图书列表

 2. 登录之后再次进行图书列表的查看

 

 3.DispatcherServlet 源码分析(dispatch派遣)

当Tomcat启动之后, 有⼀个核⼼的类DispatcherServlet, 它来控制程序的执⾏顺序.

所有请求都会先进到DispatcherServlet,执⾏doDispatch 调度⽅法.

如果有拦截器, 会先执⾏拦截器preHandle() ⽅法的代码, 如果 preHandle() 返回true, 继续访问controller中的⽅法. controller当中的⽅法执⾏完毕后,再回过来执⾏ postHandle() 和 afterCompletion() ,返回给DispatcherServlet,最终给浏览器响应数据.

3.1 初始化

DispatcherServlet的初始化⽅法 init() 在其⽗类 HttpServletBean 中实现的.
主要作⽤是加载 web.xml 中 DispatcherServlet 的 配置, 并调⽤⼦类的初始化.
web.xml是web项⽬的配置⽂件,⼀般的web⼯程都会⽤到web.xml来配置,主要⽤来配置
Listener,Filter,Servlet等, Spring框架从3.1版本开始⽀持Servlet3.0, 并且从3.2版本开始通过配置DispatcherServlet, 实现不再使⽤web.xml

3.2 处理请求

DispatcherServlet 接收到请求后, 执⾏doDispatch 调度⽅法, 再将请求转给Controller.
我们来看doDispatch ⽅法的具体实现

查看源码方式:

ctrl+N全局搜索类,ctrl+R在一个类中搜索方法

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            try {
                ModelAndView mv = null;
                Object dispatchException = null;

                try {
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }

                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = HttpMethod.GET.matches(method);
                    if (isGet || HttpMethod.HEAD.matches(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }

                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }

                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }

                    this.applyDefaultViewName(processedRequest, mv);
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var20) {
                    dispatchException = var20;
                } catch (Throwable var21) {
                    dispatchException = new NestedServletException("Handler dispatch failed", var21);
                }

                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } catch (Exception var22) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
            } catch (Throwable var23) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
            }

        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }

        }
    }

HandlerAdapter 在 Spring MVC 中使⽤了适配器模式
适配器模式, 也叫包装器模式. 简单来说就是⽬标类不能直接使⽤, 通过⼀个新类进⾏包装⼀下, 适配调⽤⽅使⽤.
把两个不兼容的接⼝通过⼀定的⽅式使之兼容.
HandlerAdapter 主要⽤于⽀持不同类型的处理器(如 Controller、HttpRequestHandler 或者
Servlet 等),让它们能够适配统⼀的请求处理流程。这样,Spring MVC 可以通过⼀个统⼀的接⼝来处理来⾃各种处理器的请求

4.3 适配器模式

HandlerAdapter 在 Spring MVC 中使⽤了适配器模式
适配器模式定义
适配器模式, 也叫包装器模式.

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

不兼容的两个接口

通过适配器的⽅式, 兼容的A,B接口
适配器模式⻆⾊
Target: ⽬标接⼝ (可以是抽象类或接⼝), 客⼾希望直接⽤的接⼝
Adaptee: 适配者, 但是与Target不兼容
Adapter: 适配器类, 此模式的核⼼. 通过继承或者引⽤适配者的对象, 把适配者转为⽬标接⼝
client: 需要使⽤适配器的对象
适配器模式应⽤场景
⼀般来说,适配器模式可以看作⼀种"补偿模式",⽤来补救设计上的缺陷. 应⽤这种模式算是"⽆奈之 举", 如果在设计初期,我们就能协调规避接⼝不兼容的问题, 就不需要使⽤适配器模式了所以适配器模式更多的应⽤场景主要是对正在运⾏的代码进⾏改造, 并且希望可以复⽤原有代码实现新 的功能. ⽐如版本升级等.

4. 统⼀数据返回格式

强制登录案例中, 我们共做了两部分⼯作
1. 通过Session来判断⽤⼾是否登录
2. 对后端返回数据进⾏封装, 告知前端处理的结果
拦截器帮我们实现了第⼀个功能, 接下来看SpringBoot对第⼆个功能如何⽀持
统⼀的数据返回格式使⽤ @ControllerAdvice ResponseBodyAdvice 的⽅式实现
@ControllerAdvice 表⽰控制器通知类
添加类 ResponseAdvice , 实现 ResponseBodyAdvice 接⼝, 并在类上添加@ControllerAdvice 注解
public class ResponseAdvice implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType,
                                  MediaType selectedContentType, Class selectedConverterType,
                                  ServerHttpRequest request, ServerHttpResponse response) {
        return Result.success(body);
    }
}
supports⽅法:
判断是否要执⾏beforeBodyWrite⽅法. true为执⾏, false不执⾏. 通过该⽅法可以选择哪些类或哪些⽅法的response要进⾏处理, 其他的不进⾏处理.
从returnType获取类名和⽅法名
// 获取执⾏的类
Class<?> declaringClass = returnType.getMethod().getDeclaringClass();
// 获取执⾏的⽅法
Method method = returnType.getMethod();

beforeBodyWrite⽅法: 对response⽅法进⾏具体操作处理

4.1 测试

测试

添加统⼀数据返回格式之前:

添加统⼀数据返回格式之后:

加此注解

 4.2 存在问题

结果显⽰(500), 发⽣内部错误
查看数据库, 发现数据操作成功

 

查看⽇志, ⽇志报错

4.2.1 测试方法 

多测试⼏种不同的返回结果
测试代码:
测试结果

 发现只有返回结果为String类型时才有这种错误发⽣!!!

4.2.2 解决⽅案

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    private static ObjectMapper mapper=new ObjectMapper();
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType,
                                  MediaType selectedContentType, Class selectedConverterType,
                                  ServerHttpRequest request, ServerHttpResponse response) {
        if(body instanceof String){//如果返回结果为String类型, 使⽤SpringBoot内置提供的Jackson来实现信息的序列化
            try {
                return mapper.writeValueAsString(Result.success(body));
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        return Result.success(body);
    }
}

成功!!!

4.2.3 原因分析

  • SpringMVC默认会注册⼀些⾃带的 HttpMessageConverter (从先后顺序排列分别为
  • ByteArrayHttpMessageConverter , StringHttpMessageConverter , SourceHttpMessageConverter , SourceHttpMessageConverter , AllEncompassingFormHttpMessageConverter )
  • 其中AllEncompassingFormHttpMessageConverter 会根据项⽬依赖情况 添加对应的
  • HttpMessageConverter
  • 在依赖中引⼊jackson包后,容器会把 MappingJackson2HttpMessageConverter ⾃动注册到messageConverters 链的末尾.
  • Spring会根据返回的数据类型, 从 messageConverters 链选择合适的 HttpMessageConverter .
  • 当返回的数据是⾮字符串时, 使⽤的 MappingJackson2HttpMessageConverter 写⼊返回对象.
  • 当返回的数据是字符串时, StringHttpMessageConverter 会先被遍历到,这时会认为 StringHttpMessageConverter 可以使⽤.
  • ((HttpMessageConverter) converter).write(body, selectedMediaType,
    outputMessage) 的处理中, 调⽤⽗类的write⽅法
  • 由于 StringHttpMessageConverter 重写了addDefaultHeaders⽅法, 所以会执⾏⼦类的⽅法
  • 然⽽⼦类 StringHttpMessageConverter 的addDefaultHeaders⽅法定义接收参数为String, 此时t为Result类型, 所以出现类型不匹配"Result cannot be cast to java.lang.String"的异常

 4.2.4 案例代码修改

如果⼀些⽅法返回的结果已经是Result类型了, 那就直接返回Result类型的结果即可

5.统⼀异常处理 

统⼀异常处理使⽤的是 @ControllerAdvice + @ExceptionHandler 来实现的, @ControllerAdvice 表⽰控制器通知类, @ExceptionHandler 是异常处理器,两个结合表⽰当出现异常的时候执⾏某个通知,也就是执⾏某个⽅法事件
类名, ⽅法名和返回值可以⾃定义, 重要的是注解
接⼝返回为数据时, 需要加 @ResponseBody 注解
以上代码表⽰,如果代码出现Exception异常(包括Exception的⼦类), 就返回⼀个 Result的对象, Result 对象的设置参考 Result.fail(e.getMessage()

 我们可以针对不同的异常, 返回不同的结果

测试结果

 

当有多个异常通知时,匹配顺序为当前类及其⼦类向上依次匹配

6.@ControllerAdvice 源码分析

统⼀数据返回和统⼀异常都是基于 @ControllerAdvice 注解来实现的, 通过分析@ControllerAdvice 的源码, 可以知道他们的执⾏流程.

  • 从上述源码可以看出 @ControllerAdvice 派⽣于 @Component 组件, 这也就是为什么没有五 ⼤注解, ControllerAdvice 就⽣效的原因.
  • 下⾯我们看看Spring是怎么实现的, 还是从 DispatcherServlet 的代码开始分析.
  • DispatcherServlet 对象在创建时会初始化⼀系列的对象
public class DispatcherServlet extends FrameworkServlet {
 //...
 @Override
 protected void onRefresh(ApplicationContext context) {
 initStrategies(context);
 }
 
 /**
 * Initialize the strategy objects that this servlet uses.
 * <p>May be overridden in subclasses in order to initialize further 
strategy objects.
 */
 protected void initStrategies(ApplicationContext context) {
 initMultipartResolver(context);
 initLocaleResolver(context);
 initThemeResolver(context);
 initHandlerMappings(context);
 initHandlerAdapters(context);
 initHandlerExceptionResolvers(context);
 initRequestToViewNameTranslator(context);
 initViewResolvers(context);
 initFlashMapManager(context);
 }
 //...
}
对于 @ControllerAdvice 注解,我们重点关注 initHandlerAdapters(context)
initHandlerExceptionResolvers(context) 这两个⽅法.

6.1. initHandlerAdapters(context)

initHandlerAdapters(context) ⽅法会取得所有实现了 HandlerAdapter 接⼝的bean并
保存起来,其中有⼀个类型为 RequestMappingHandlerAdapter 的bean,这个bean就是
@RequestMapping 注解能起作⽤的关键,这个bean在应⽤启动过程中会获取所有被@ControllerAdvice 注解标注的bean对象, 并做进⼀步处理。

6.2. initHandlerExceptionResolvers(context)

接下来看 DispatcherServlet initHandlerExceptionResolvers(context) ⽅法,
这个⽅法会取得所有实现了 HandlerExceptionResolver 接⼝的bean并保存起来,其中就有⼀
个类型为 ExceptionHandlerExceptionResolver 的bean,这个bean在应⽤启动过程中会获
取所有被 @ControllerAdvice 注解标注的bean对象做进⼀步处理

7. 案例代码

通过上⾯统⼀功能的添加, 我们后端的接⼝已经发⽣了变化(后端返回的数据格式统⼀变成了Result类型), 所以我们需要对前端代码进⾏修改

7.1 登录页面

登录界⾯没有拦截, 只是返回结果发⽣了变化, 所以只需要根据返回结果修改对应代码即可

登录结果代码修改
tips:可以根据返回的东西更新前端返回代码

 

7.2图书列表

针对图书列表⻚有两处变化
1. 拦截器进⾏了强制登录校验, 如果校验失败, 则http状态码返回401, 此时会⾛ajax的error逻辑处理
2. 接⼝返回结果发⽣了变化
图书列表代码修改

7.3 其他

参考图书列表, 对删除图书, 批量删除图书,添加图书, 修改图书接⼝添加⽤⼾强制登录以及统⼀格式返回的逻辑处理

7.4 测试

先后端接口测试后前端测试

//修改时遇到的问题

//统一返回String类型出现错误

//改正1.前端处理

//改正2.接口设置返回类型

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

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

相关文章

vue简单使用一(vue的声明)

首先引入vue的js文件&#xff1a; <script src"js/vue.js" type"application/javascript"></script> vue.js文件去vue官网下载即可 html代码 <div class"vuePro"><div> vue的属性信息都得放在这个标签下面哪怕是同级也是…

轻松玩转TikTok:TK防关联引流系统实现一键式多账号管理

TK防关联引流系统是为TikTok平台量身打造的全方位引流解决方案&#xff0c;不仅解决了传统跨境电商在TikTok运营中遇到的诸多难题&#xff0c;还通过创新技术实现了自动化、高效化的全球引流。以下是该系统的主要优势及功能特点&#xff1a; 系统核心优势 1.全球真实环境模拟…

论文| Convolutional Neural Network-based Place Recognition - 2014

2014-Convolutional Neural Network-based Place Recognition

推荐!6个实用工具类微信小程序,提升效率的神器,工作生活中一定用得到!

亲爱的时间管理小能手们&#xff0c;你们是否经常感叹一天24小时不够用&#xff1f;是不是也经历过在杂乱无章的app海洋中寻找那一个刚好满足需求的小众工具&#xff1f;别急&#xff0c;今天我要给你揭秘六款实用到哭的小程序神器&#xff0c;它们就像你口袋里的多功能瑞士军刀…

Qt——示波器/图表 QCustomPlot

一、介绍 QCustomPlot是一个用于绘图和数据可视化的Qt C小部件。它没有进一步的依赖关系&#xff0c;提供友好的文档帮助。这个绘图库专注于制作好看的&#xff0c;出版质量的2D绘图&#xff0c;图形和图表&#xff0c;以及为实时可视化应用程序提供高性能。QCustomPlot可以导出…

Hackthebox IClean

靶机信息IP/难度Medium网址https://app.hackthebox.com/machines/IClean状态Active系统Linux Python XSS, SSTI 端口扫描 PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.6 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 256 2cf9077…

48V转15V,48V转12V,48V转24V高效率降压恒压芯片SL3041电路简单

在现代电子设备中&#xff0c;电源转换是一个关键且常见的技术。特别是对于那些需要将48V电压转换为更低电压&#xff08;如15V、12V或24V&#xff09;的设备&#xff0c;一个高效、可靠的降压恒压芯片至关重要。本文将详细介绍一款名为SL3041的高效率降压恒压芯片&#xff0c;…

Ant Design Vue 表单验证手机号的正则

代码&#xff1a; pattern: /^1[3456789]\d{9}$/ 1. <a-form-item label"原手机号" v-bind"validateInfos.contactTel"><a-inputstyle"width: 600px"allow-clear:maxlength"20"placeholder"请输入原手机号"v-mo…

JavaWeb中的Servlet是什么?怎么使用?

文章目录 一、什么是Servlet二、Servlet的基本内容1、Servlet的作用2、Servlet接口3、Servlet接口实现类4、Servlet接口实现类开发步骤5、Servlet对象生命周期6、HttpServletResquest接口7、HttpServletResponse接口8、请求对象和响应对象流程图9、请求对象和响应对象生命周期1…

centos7部署zabbix6.4.9

文章目录 [toc]一、环境准备1&#xff09;部署lnmp2&#xff09;修改配置文件3&#xff09;安装数据库 二、部署zabbix1&#xff09;下载zabbix2&#xff09;安装zabbix服务端3&#xff09;修改配置4&#xff09;开机启动5&#xff09;安装客户端 三、登录配置1&#xff09;访问…

面试算法-170-二叉树的最大深度

题目 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3 解 class Solution {public int maxDepth(TreeNod…

C语言 | Leetcode C语言题解之第16题最接近的三数之和

题目&#xff1a; 题解&#xff1a; int comp(const void *a, const void *b) { return *(int *)a - *(int *)b; } int threeSumClosest(int *nums, int numsSize, int target) {int n numsSize;qsort(nums, n, sizeof(int), comp);int best 1e7;// 根据差值的绝对值来更新答…

常见安全漏洞及其解决方案

1、 SQL注入漏洞 漏洞描述&#xff1a; SQL注入被广泛用于非法入侵网站服务器&#xff0c;获取网站控制权。它是应用层上的一种安全漏洞。通常在设计存在缺陷的程序中&#xff0c;对用户输入的数据没有做好过滤&#xff0c;导致恶意用户可以构造一些SQL语句让服务器去执行&…

2024第十九届中国(温州)机械装备展9月20-22日举行

第十九届中国&#xff08;温州&#xff09;机械装备展览会 THE 19TH CHINA (WENZHOU) MACHINERY EQUIPENT EXHIBITION 第十九届中国&#xff08;温州&#xff09;国际紧固件博览会 THE 19TH CHINA (WENZHOU) NTERNATIONAL FASTENER EXPOSITION 时间&#xff1a;2024年9月…

APP软件的运营方法

APP软件的运营是一个复杂的过程&#xff0c;涉及到多个方面的策略和方法。以下是一些有效的APP运营方法&#xff0c;通过这些方法&#xff0c;APP运营者可以有效地吸引和留住用户&#xff0c;提高用户活跃度和忠诚度&#xff0c;最终实现商业成功。北京木奇移动技术有限公司&am…

C语言 函数——断言与防御式编程

目录 如何确定假设的真假&#xff1f; 断言 防御式编程&#xff08;Defensive programming&#xff09; 如何确定假设的真假&#xff1f; 程序中的假设 *某个特定点的某个表达式的值一定为真 *某个特定点的某个表达式的值一定位于某个区间等 问题&#xff1a;如何确定这些…

K8s拉取habor镜像

目录 在daemon.json中添加仓库地址 重新加载daemon.json并重启docker 在目标node节点添加域名 验证目标node是否能正常登录镜像仓库 创建pod资源 加载yml文件 验证 查看pod的ip与端口号 在daemon.json中添加仓库地址 此处需要在创建资源对象所在的节点进行添加 路径&a…

AcWing 1027. 方格取数

解题思路 如果使用了先走和后走的方式&#xff0c;这种方式利用了贪心的思想&#xff0c;会产生局部最优解&#xff0c; 但局部最优解毕竟不是全局最优解。所以要采用同时走的方式。 相关代码 import java.util.Scanner;public class Main {static int w[][] new int[15][15]…

建筑设计全过程碳排放计算与案例分析

“30/60双碳目标”已成为我国绿色发展的新国策&#xff0c;建筑业对碳排放量化分析和减排也越来越重视。特别是全文强制性国标标准《建筑节能与可再生能源利用通用规范》GB55015-2021的实施&#xff0c;对建设项目可行性研究报告、建设方案和初步设计要求进行建筑碳排放分析&am…

不可错过的3D建模素材网站,让设计更加丰富多彩!

3D建模已经渗透到了各个行业和领域&#xff0c;从游戏设计、室内设计到建筑设计&#xff0c;3D模型的使用越来越广泛。然而&#xff0c;创建出优秀的3D模型就需要大量的优质3D建模素材。幸运的是&#xff0c;有一些网站提供了大量的高质量3D建模素材&#xff0c;这些都是我们设…