【Spring】统一事件的处理(拦截器、统一异常处理、统一数据格式返回)

文章目录

  • 前言
  • 一、Spring 拦截器
    • 1.1 用户登录权限校验案例
      • 1.1.1 最初的用户登录验证
      • 1.1.2 使用 Spring AOP 实现登录验证的问题
    • 1.2 Spring 拦截器的使用
      • 1.2.1 Spring 拦截器概念与使用步骤
      • 1.2.2 使用拦截器实现对用户登录权限的校验
    • 1.3 拦截器实现原理
    • 1.4 Spring 拦截器和 Spring AOP 的区别
  • 二、统一异常处理
    • 2.1 为什么要统一异常处理
    • 2.2 统一异常处理的使用
  • 三、统一数据返回格式
    • 3.1 为什么需要统一数据返回格式
    • 3.2 统一数据返回格式的实现
    • 3.3 针对 body 为 String 类型报错的问题


前言

在现代的 Web 应用程序开发中,往往需要处理用户权限、异常情况以及数据返回格式等诸多方面的问题。Spring 框架为我们提供了强大的工具和机制来应对这些挑战。本文将重点介绍 Spring 框架中的拦截器、统一异常处理以及统一数据返回格式等相关内容。

一、Spring 拦截器

在 Web 应用开发中,拦截器是一种非常有用的机制,它允许我们在请求处理的不同阶段执行一些特定的操作。这些操作可以是权限校验、日志记录、数据预处理等。下面将详细探讨 Spring 框架中的拦截器相关内容。

1.1 用户登录权限校验案例

首先介绍一个常见的应用案例:

  • 用户登录权限校验。通常,在Web应用中,某些资源或功能需要登录后才能访问,因此需要对用户进行登录状态的验证。在没有拦截器的情况下,我们可能会使用传统的方式来实现这个验证逻辑。让我们来看一下最初的用户登录验证方法

1.1.1 最初的用户登录验证

在使用拦截器之间,我们先来回顾一下最开始使用用户登录验证的方法:

@RestController
public class UserController {// 最开始实现用户登录权限验证的方式@RequestMapping("/method1")public Object method1(HttpServletRequest request){// 获取Session,没有不创建HttpSession session = request.getSession(false);if(session == null || session.getAttribute("userinfo") == null){// 没有获取到 session, 此时说明用户未登录return false;}// 此时则说明已经登录了,执行后续业务逻辑// ...return true;}@RequestMapping("/method2")public Object method2(HttpServletRequest request){// 获取Session,没有不创建HttpSession session = request.getSession(false);if(session == null || session.getAttribute("userinfo") == null){// 没有获取到 session, 此时说明用户未登录return false;}// 此时则说明已经登录了,执行后续业务逻辑// ...return true;}// 其他方法...
}

当使用最初的方式在每个需要用户登录权限验证的方法中加入相同的登录验证逻辑时,会导致一些问题:

  1. 代码重复性: 需要在每个需要验证的方法中复制粘贴相同的验证代码,增加了代码冗余,不利于维护和修改。

  2. 可读性差: 多个方法中都包含相同的验证逻辑,使得代码可读性降低,难以快速理解每个方法的实际功能。

  3. 维护困难: 如果需要修改验证逻辑或者增加新的验证条件,需要在多个地方进行修改,容易出错。

  4. 代码耦合: 将验证逻辑直接嵌入到每个方法中,导致业务逻辑和验证逻辑紧密耦合在一起,不利于代码的解耦和单元测试。

  5. 扩展性差: 如果未来需要添加更多的验证逻辑,或者对验证逻辑进行定制化,需要修改多个方法,增加了工作量和风险。

1.1.2 使用 Spring AOP 实现登录验证的问题

面对上述的问题,就需要考虑使用统一的用户登录验证来解决了。一提到统一登录验证,我们可以会想到使用 Spring AOP 的面向切面编程来实现,但是其他却存在很大的问题,首先来回顾一下 Spring AOP 代码的实现:

// 创建一个切面(类)
@Aspect
@Component
public class UserAspect{// 创建切点(方法)定义拦截规则@Pointcut("execution(public * com.example.demo.controller.UserController.*(..))")public void pointcut() {}// 前置通知@Before("pointcut()")public void doBefore() {System.out.println("执行了前置通知:" + LocalDateTime.now());}// 后置通知@After("pointcut()")public void doAfter() {System.out.println("执行了后置通知:" + LocalDateTime.now());}// 返回后通知@AfterReturning("pointcut()")public void doAfterReturning() {System.out.println("执行了返回后通知:" + LocalDateTime.now());}// 抛异常后通知@AfterThrowing("pointcut()")public void doAfterThrowing() {System.out.println("抛异常后通知:" + LocalDateTime.now());}// 环绕通知@Around("pointcut()")public Object doAround(ProceedingJoinPoint joinPoint) {Object proceed = null;System.out.println("Around 方法开始执行:" + LocalDateTime.now());try {// 执行拦截的方法proceed = joinPoint.proceed();} catch (Throwable e) {e.printStackTrace();}System.out.println("Around 方法结束执行: " + LocalDateTime.now());return proceed;}
}

如果要使用上面 Spring AOP 的通知方法来实现用户登录验证,主要存在两个问题:

1. 获取 HttpSession 对象问题

  • 在 AOP 中,通知(Advice)是在切点(Pointcut)匹配的连接点(Join Point)上执行的,然而,这些连接点都是方法级别的,不能直接提供对HttpServletRequestHttpSession等相关对象的访问。因此,在 AOP 切面中无法直接访问 HttpSession 对象。

2. 选择性拦截问题

  • 在实际的应用程序中,我们可能只需要对部分功能进行登录权限验证,而需要排除一些其他方法,比如用户的注册和登录
  • 使用基于方法名的切点表达式则难以精确地实现这一目标,因为这可能会涉及到复杂的正则表达式匹配,而且如果有新的方法需要排除,就需要手动更新切点表达式。

为了解决这些问题,Spring 的拦截器机制更适合处理这类情况。拦截器可以访问 HttpServletRequestHttpSession 等请求相关对象,也可以更方便地配置哪些路径需要被拦截,哪些不需要。

1.2 Spring 拦截器的使用

1.2.1 Spring 拦截器概念与使用步骤

Spring 拦截器是 Spring 框架中的一种拦截机制,用于在请求进入控制器前后执行的特定的操作。拦截器可以用于实现登录权限校验、日志记录、数据预处理等功能。拦截器使用面向切向编程(AOP)的思想,可以在请求的不同阶段插入自己的逻辑,从而实现各种功能

下面是使用 Spring 拦截器的一般步骤:

1. 创建拦截器类

首先,创建一个类来实现 Spring 的 HandlerInterceptor接口,并重写其中的方法。通常包括下面这三种方法:

  • preHandle:在请求处理之前执行,可以用于权限校验等操作。如果返回false则中断请求处理。
  • postHandle:在请求处理之后执行,但在视图渲染之前执行。可以进行日志记录等操作。
  • afterCompletion:在视图渲染之后执行,可以进行一些资源清理等操作。

2. 添加并配置拦截器

在 Spring Boot 中配置添加拦截器大致可以分为以下两步:

1)将创建的拦截器类添加到 Spring 容器中: 在 Spring Boot 中,可以使用 @Component 注解来标记创建的拦截器类,将其交由 Spring 容器管理。这样,Spring Boot 会自动扫描并将这个拦截器类纳入到应用的上下文中。

例如,在拦截器类上添加 @Component 注解,如下所示:

import org.springframework.stereotype.Component;@Component
public class AuthInterceptor implements HandlerInterceptor {// 拦截器的具体实现
}

2)实现 WebMvcConfigurer 接口并重写 addInterceptors 方法来添加拦截器到系统配置: 在 Spring Boot 中,可以通过实现 WebMvcConfigurer 接口,重写其中的 addInterceptors 方法来添加拦截器。这个方法会将创建的拦截器添加到系统中,使其在请求被处理时生效。

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate AuthInterceptor authInterceptor; // 注入你的拦截器类@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 添加拦截器,并指定拦截的路径registry.addInterceptor(authInterceptor).addPathPatterns("/**").excludePathPatterns("/user/login").excludePathPatterns("/user/reg");}
}

上述代码中,addInterceptors 方法会将拦截器类 authInterceptor 添加到系统配置中,并通过 addPathPatterns 指定要拦截的路径,然后可以使用excludePathPatterns方法来指定放弃拦截的指定接口,如登录、注册功能。这样,当程序运行时,配置好的拦截器就会自动生效。

3. 配置拦截器顺序
如果同时使用了多个拦截器,可以通过配置来定义它们的执行顺序。在 Spring MVC 中,拦截器的执行顺序与它们在配置文件中声明的顺序一致。
4. 使用拦截器
配置好拦截器后,它们会在请求被处理之前、之后或之后的渲染阶段执行相应的逻辑。这样就可以在拦截器中实现所需的功能,如权限校验、日志记录等。

1.2.2 使用拦截器实现对用户登录权限的校验

1. 创建用户登录验证的拦截器:

/*** Spring MVC拦截器(LoginInterceptor),用于检查用户是否已登录。* 如果用户未登录,则重定向到登录页面。*/
@Component
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 判断用户登录HttpSession session = request.getSession(false); // 默认值是trueif (session != null && session.getAttribute(ApplicationVariable.SESSION_KEY_USERINFO) != null) {// 用户已经登录了return true;}// 当代码执行到此次,表示用户未登录response.sendRedirect("/login.html");return false;}
}

2. 添加并配置拦截规则

/*** 配置拦截规则*/
@Configuration
public class MyConfig implements WebMvcConfigurer {@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor).addPathPatterns("/**") // 拦截所有的 url// 放开拦截的内容.excludePathPatterns("/**/*.js").excludePathPatterns("/**/*.css").excludePathPatterns("/**/*.jpg").excludePathPatterns("/login.html").excludePathPatterns("/user/reg").excludePathPatterns("/user/login")// ...;}
}

1.3 拦截器实现原理

拦截器的实现原理涉及到 Spring 框架的核心概念:面向切面编程(AOP)。在 AOP 的帮助下,拦截器可以被织入到方法调用链中,从而在请求处理的不同阶段执行相应的操作。下面将介绍拦截器的实现原理和工作流程。

1. AOP 概念回顾

AOP 是一种编程范式,旨在解决横切关注点(Cross-cutting Concerns)的问题。它通过将横切关注点与主要业务逻辑分离,使得我们能够更好地管理和维护代码。在 Spring 中,AOP 是通过代理模式和动态代理来实现的。

在 AOP 中,有两个重要的概念:切点(Pointcut)和通知(Advice)。

  • 切点(Pointcut): 切点定义了在哪些连接点(Join Point)上应用通知。连接点可以是方法调用、方法执行、字段访问等等。切点使用表达式来匹配连接点,以确定哪些连接点会被通知所影响。

  • 通知(Advice): 通知定义了在切点处执行的代码。在 Spring 中,有多种类型的通知,包括前置通知(在方法调用之前执行)、后置通知(在方法调用之后执行)、环绕通知(在方法调用前后执行)、异常通知(在方法抛出异常时执行)、最终通知(在方法调用结束后执行)。

2. 拦截器的工作流程

拦截器实际上是一种 AOP 的应用,通过 AOP 的方式实现对请求处理过程的干预。以下是拦截器的工作流程:

  1. 当请求到达 DispatcherServlet(前端控制器)时,拦截器首先会根据配置的拦截规则(切点)来判断是否要拦截该请求。

  2. 如果拦截器决定要拦截该请求,它会在切点前执行通知的逻辑,比如前置通知。这样,拦截器可以在请求处理之前执行一些预处理操作,如权限校验、日志记录等。

  3. 接下来,请求会继续被传递到相应的控制器进行处理。拦截器不会中断请求的流程,只是在处理前插入了自己的逻辑。

  4. 当控制器处理完请求后,拦截器会再次执行通知的逻辑,比如后置通知。这样,拦截器可以在请求处理之后执行一些后续操作,如数据封装、日志记录等。

  5. 最终,拦截器会在切点后执行通知的逻辑,比如最终通知。这样,拦截器可以在请求处理结束后执行一些清理操作。

通过拦截器,我们可以实现对请求处理过程中不同阶段的干预,从而达到一些横切关注点的处理。这种机制使得我们能够更好地管理和维护代码,提高了代码的可维护性和重用性。

1.4 Spring 拦截器和 Spring AOP 的区别

Spring 拦截器(Interceptor)和 Spring AOP(面向切面编程)是两种在 Spring 框架中用于处理横切关注点的机制,它们有些相似,但也有一些关键的区别。以下是它们之间的主要区别:

1. 目标领域不同

  • Spring 拦截器: 主要用于处理 Web 请求。它在请求处理的不同阶段执行特定的操作,如权限校验、日志记录等。拦截器主要关注于对 Web 请求的处理流程进行干预,以及在请求前后插入自定义的逻辑。

  • Spring AOP: 用于处理应用程序中的横切关注点,不仅限于 Web 请求。AOP 可以在方法调用、方法执行、字段访问等不同连接点上执行通知,从而实现一些与主要业务逻辑解耦的功能,如事务管理、性能监控等。

2. 应用范围不同

  • Spring 拦截器: 主要应用于 Web 层,处理 Web 请求。它可以控制请求的处理流程,但只作用于 Web 层,不会影响到业务层的方法调用。

  • Spring AOP: 可以应用于多个层面,包括业务层、持久层、Web 层等。AOP 可以跨足不同的层面,通过拦截连接点实现对不同层之间的通用关注点的处理。

3. 处理方式不同

  • Spring 拦截器: 拦截器是基于 Java 动态代理的机制,它通过织入在请求处理流程中的方式来实现对请求的干预。拦截器主要关注于请求处理的前后环节,可以进行预处理、后处理、资源释放等操作。

  • Spring AOP: AOP 是通过代理模式来实现的,可以使用 JDK 动态代理或者 CGLIB 来生成代理对象。AOP 可以在方法调用前后、异常抛出时、方法执行结束等连接点上执行通知,从而实现不同类型的横切关注点。

4. 原理和灵活性

  • Spring 拦截器: 拦截器是一种 Spring MVC 提供的机制,更加针对 Web 层的处理。它具有一定的灵活性,但在处理跨层面的通用关注点时相对较为有限。

  • Spring AOP: AOP 是 Spring 框架的核心特性之一,可以在多个层面应用,包括对方法调用、字段访问等各种连接点的处理。AOP 更具通用性,适用于处理各种横切关注点。

总之,Spring 拦截器和 Spring AOP 都是用于处理横切关注点的重要机制,但在应用范围、处理方式和灵活性等方面存在一些区别。选择合适的机制取决于实际需求以及在哪个层面需要处理横切关注点。

二、统一异常处理

2.1 为什么要统一异常处理

在开发过程中,应用程序难免会遇到各种异常情况,如数据库连接失败、空指针异常等。为了提供更好的用户体验和更好的错误信息管理,采用统一异常处理机制是很有必要的。以下是为什么要采用统一异常处理的一些原因:

  1. 友好的用户体验: 统一异常处理可以捕获各种异常,并返回统一的错误响应。这样,用户可以获得更友好、更易于理解的错误提示,而不会直接暴露应用程序内部的错误细节。

  2. 减少代码重复性: 在应用程序中,同一类型的异常可能会在多个地方发生。通过统一异常处理,可以将相同的错误处理逻辑抽取到一个地方,减少代码的重复性,提高代码的可维护性。

  3. 集中式错误管理: 统一异常处理可以将错误信息集中管理,方便进行日志记录、监控和错误分析。这有助于快速定位问题,提高应用程序的稳定性。

2.2 统一异常处理的使用

在 Spring 框架中,可以使用 @ControllerAdvice@ExceptionHandler 注解来实现统一异常处理。

步骤如下:

  1. 创建一个异常处理类,并使用 @ControllerAdvice 注解标记该类,表示它是一个控制器通知类,用于处理全局的异常情况。

  2. 在异常处理类中,使用 @ExceptionHandler 注解定义异常处理方法。可以针对不同类型的异常编写不同的处理方法,或者使用一个通用的方法处理所有异常。

  3. 在异常处理方法中,可以自定义错误信息,构建错误响应,并返回适当的视图或 JSON 响应。

以下是一个示例代码,展示了如何使用统一异常处理:


/*** 统一异常处理*/
@ControllerAdvice
@ResponseBody
public class MyExceptionAdvice {/*** 处理空指针异常* @param e* @return*/@ExceptionHandler(NullPointerException.class)public HashMap<String, Object> doNullPointerException(NullPointerException e){HashMap<String, Object> result = new HashMap<>();result.put("code", -300);result.put("msg", "空指针: " + e.getMessage());result.put("data", null);return result;}/*** 默认异常处理(当具体的异常处匹配不到时,会执行此方法)* @param e* @return*/@ExceptionHandler(Exception.class)public HashMap<String, Object> doException(Exception e){HashMap<String, Object> result = new HashMap<>();result.put("code", -300);result.put("msg", "Exception: " + e.getMessage());result.put("data", null);return result;}}

在这个代码示例中,创建了一个名为 MyExceptionAdvice 的全局异常处理类,通过 @ControllerAdvice 注解来标记它。这个类中包含了两个异常处理方法:

  1. doNullPointerException 方法,用于处理空指针异常(NullPointerException)。当应用程序抛出空指针异常时,这个方法会被调用,然后构建一个包含错误信息的 HashMap 并返回。

  2. doException 方法,用于处理其他类型的异常。如果应用程序抛出的异常类型不匹配前面定义的处理方法,就会执行这个默认异常处理方法。它也会构建一个错误响应并返回。

通过这样的方式,实现了针对不同类型异常的定制处理,提供了统一的错误响应格式,从而提升了用户体验和代码的可维护性。

三、统一数据返回格式

3.1 为什么需要统一数据返回格式

在开发中,不同接口可能返回不同的数据格式,这可能导致前端处理数据时需要根据不同接口的返回格式进行不同的处理逻辑,增加了代码的复杂性和维护成本。为了简化前端处理逻辑,提高代码的可维护性,可以考虑统一数据返回格式。

统一数据返回格式的优点包括:

  1. 减少前端逻辑复杂性: 前端不需要针对不同接口编写不同的处理逻辑,可以统一处理数据格式,降低代码复杂性。

  2. 提高前后端协作效率: 前后端通过明确的数据格式约定,可以更快地进行接口开发和联调,减少沟通成本。

  3. 规范错误处理: 统一的数据返回格式可以规范错误处理方式,使得前端可以更方便地判断请求是否成功,以及获取错误信息。

3.2 统一数据返回格式的实现

在实现统一数据格式返回的时候,主要借助 Spring 框架提供的 ResponseBodyAdvice 接口和@ControllerAdvice注解,并且需要重写接口中的supportsbeforeBodyWrite方法,其中supports方法表示是否要使用统一数据格式返回,而beforeBodyWrite则是实现对数据格式的统一。

以下是一个示例,展示了如何实现统一数据返回格式,其中规定返回的数据格式为:

{"cede":code,"msg":msg,"data":data
}

在返回的时候可以使用HashMap来组织。下面是具体的实现代码:

/*** 统一数据格式处理*/
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {@Autowiredprivate ObjectMapper objectMapper;@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}/*** 返回数据之前进行处理*/@SneakyThrows@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {//规定标准格式为:HashMap<String, Object> -> code,msg.dataif (body instanceof HashMap) {// 如果已经是标准格式return body;}// 重写返回结果,让其返回一个统一的数据格式HashMap<String, Object> result = new HashMap<>();result.put("code", 200);result.put("data", body);result.put("msg", "");return result;}
}

上述示例代码很清晰地演示了如何使用 @ControllerAdvice 注解和 ResponseBodyAdvice 接口来实现统一数据返回格式。通过重写 supportsbeforeBodyWrite 方法,实现了对返回数据格式的统一处理。

在这个示例中,规定了统一的数据格式,即返回的 JSON 包含了 “code”、“msg” 和 “data” 字段。如果返回的数据已经是标准格式(HashMap<String, Object>),则直接返回;否则,就构建一个标准格式的返回对象,并将原始数据放入其中。

3.3 针对 body 为 String 类型报错的问题

当针对处理基本数据类型的时候,例如:

@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/login")public int login() {return 1;}
}

浏览器访问结果:

此时发现没有任何问题,并且成功实现了对数据格式的统一返回。但是如果返回的是 String 类型呢?


@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/hello")public String hello(){return "hello world";}
}

再次通过浏览器访问:

1. 返回 String 类型出错原因分析:

其报错的内容大致是HashMap类型不能够转换成String类型。为什么会这样呢?首先需要了解数据在返回过程中的执行流程

  1. 控制器方法返回 String 类型;
  2. 针对于 String 类型进行统一数据格式处理,即把为 String 类型的 body 作为 value 设置到 keydataHashMap 中;
  3. HashMap 转换成 application/json 字符串并通过网络传输给前端。

值得注意的是:

  • 其中,如果 body 的类型如果是 String 类型,则会使用 StringHttpMessageConverter 进行类型的转换;
  • 否则使用 HttpMessageConverter进行类型转换。

但是使用针对于第三步,由于 body 是 String 类型的,因此使用的是StringHttpMessageConverter类,它只能够将 String 类型数据转换为 JSON 字符串,如果转换 HashMap 就会出错。

2. 解决方法:

解决方法大致可以分为两种:

  1. 解决方法一: 如果body类型是 String,就直接返回 String 类型,其中可以使用字符串拼接,也可以使用 jackson 中的ObjectMapper进行转换成 JSON 格式。
    1)拼接形成 String 进行返回:
@SneakyThrows
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {if (body instanceof String) {// 返回一个 String 字符串return "{\"code\" : 200, \"msg\": \"\", \"data\":\"" + body + "\"}";}if (body instanceof HashMap) {// 如果已经是标准格式return body;}// 重写返回结果,让其返回一个统一的数据格式HashMap<String, Object> result = new HashMap<>();result.put("code", 200);result.put("data", body);result.put("msg", "");return result;
}

2)使用 jackson 中的 ObjectMapper

@SneakyThrows
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {if (body instanceof HashMap) {// 如果已经是标准格式return body;}// 重写返回结果,让其返回一个统一的数据格式HashMap<String, Object> result = new HashMap<>();result.put("code", 200);result.put("data", body);result.put("msg", "");if(body instanceof String){// 返回一个 String 字符串return objectMapper.writeValueAsString(result);}return result;
}

2. 解决方法二: 直接禁用 StringHttpMessageConverter 类。

可以在配置类中通过重写 configureMessageConverters 方法来实现禁用 StringHttpMessageConverter

@Configuration
public class MyConfig implements WebMvcConfigurer {/*** 移除 StringHttpMessageConverter* @param converters*/@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) {converters.removeIf(converter -> converter instanceof StringHttpMessageConverter);}
}

其中,converter -> converter instanceof StringHttpMessageConverter 是一个 Lambda 表达式,用于检查 converter 是否是 StringHttpMessageConverter 的实例。如果是,则移除该实例。

当选择任意一个解决方法之后,再次访问返回 String 类型的控制器方法,其结果如下:


此时便能够正确返回统一的数据格式了。

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

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

相关文章

响应式设计是什么?怎么学习? - 易智编译EaseEditing

响应式设计是一种用于创建能够适应不同设备和屏幕尺寸的网站和应用程序的设计方法。它的目标是确保网站在各种设备上都能提供良好的用户体验&#xff0c;无论是在大屏幕的桌面电脑上还是在小屏幕的移动设备上。 在响应式设计中&#xff0c;页面的布局、字体、图像和其他元素会…

读《芯片浪潮》,学习台积电张忠谋的管理之道

大家知道&#xff0c;台积电一个公司就占据了全球晶圆代工市场一半的份额。 5纳米及以下最先进工艺的芯片&#xff0c;台积电可占到惊人的90%以上的市场。全球最新最强的智能手机、笔记本电脑的核心计算芯片都必须仰仗台积电一个企业的供应。 换一个说法&#xff0c;如果没有…

每天一道leetcode:剑指 Offer 12. 矩阵中的路径(中等DFS深度优先遍历)

今日份题目&#xff1a; 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 单词必须按照字母顺序&#xff0c;通过相邻的单元格内的字母构成&#xff0c;其中“相邻”单元…

62、华为昇腾开发板Atlas 200I DK A2配置mmpose的hrnet模型推理python/c++

基本思想&#xff1a;适配mmpose模型&#xff0c;记录一下流水帐&#xff0c;环境配置和模型来自&#xff0c;请查看参考链接。 链接: https://pan.baidu.com/s/1IkiwuZf1anyKX1sZkYmD1g?pwdi51s 提取码: i51s 一、转模型 (base) rootdavinci-mini:~/sxj731533730# atc --mo…

docker pull 设置代理 centos

On CentOS the configuration file for Docker is at: /etc/sysconfig/docker 用 root 权限打开 text editor sudo gedit 注意 加引号 Adding the below line helped me to get the Docker daemon working behind a proxy server: HTTP_PROXY“http://<proxy_host>:&…

C++ 动态规划经典案例解析之最长公共子序列(LCS)_窥探递归和动态规划的一致性

1. 前言 动态规划处理字符相关案例中&#xff0c;求最长公共子序列以及求最短编辑距离&#xff0c;算是经典中的经典案例。 讲解此类问题的算法在网上一抓应用一大把&#xff0c;即便如此&#xff0c;还是忍不住有写此文的想法。毕竟理解、看懂都不算是真正掌握&#xff0c;唯…

浅谈统一权限管理服务的设计与开发

作者 | 天地练心 导读 本文详细探讨了统一权限管理服务&#xff08;MPS&#xff09;的设计与开发&#xff0c;针对企业内部多平台权限管理混乱的问题&#xff0c;提出了一套综合RBAC、ACL、DAC权限模型的解决方案。文章从需求分析、技术选型、功能设计等方面全面介绍了MPS的构建…

阿里云ACP知识点

前言&#xff1a;记录ACP错题 1、在创建阿里云ECS时&#xff0c;每台服务器必须要包含_______用来存储操作系统和核心配置。 系统盘&#xff08;不是实例&#xff0c;实例是一个虚拟的计算环境&#xff0c;由CPU、内存、系统盘和运行的操作系统组成&#xff1b;ESC实例作为云…

2023国赛数学建模E题思路分析

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

纯js点击按钮切换首页部分页面

像我这种大数据的&#xff0c;不会前端的&#xff0c;懒得学框架&#xff0c;现在有gpt了&#xff0c;前端对于我来说&#xff0c;用原生的更加友好&#xff0c;毕竟算法gpt都能优化。 首页我有个页面&#xff0c;然后我现在想点击gm替换上面的统计&#xff0c;点击用户替换回…

Flask Web开发实战(狼书)| 笔记第1、2章

前言 2023-8-11 以前对网站开发萌生了想法&#xff0c;又有些急于求成&#xff0c;在B站照着视频敲了一个基于flask的博客系统。但对于程序的代码难免有些囫囵吞枣&#xff0c;存在许多模糊或不太理解的地方&#xff0c;只会照葫芦画瓢。 而当自己想开发一个什么网站的时&…

SpringCloud微服务之间如何进行用户信息传递(涉及:Gateway、OpenFeign组件)

目录 1、想达到的效果2、用户信息在微服务之间传递的两种途径3、用RuoYi-Cloud为例进行演示说明&#xff08;1&#xff09;网关将用户信息写在请求头中&#xff08;2&#xff09;业务微服务之间通过OpenFeign进行调用&#xff0c;并且将用户信息写在OpenFeign准备的请求头中&am…

Qt+C++自定义控件仪表盘动画仿真

程序示例精选 QtC自定义控件仪表盘动画仿真 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对<<QtC自定义控件仪表盘动画仿真>>编写代码&#xff0c;代码整洁&#xff0c;规则&…

浅谈SMT行业MES系统生产管理的特点

一、SMT生产车间在电子制造中起重要作用的部分&#xff0c;主要具备以下生产特点&#xff1a; 1.高密度和高速度&#xff1a; SMT生产车间中的电子元器件一般来说较为精小&#xff0c;且被紧密地排列在PCB上。这就要求SMT生产车间的机械设备具备高精度和高速度&#xff0c;确保…

怎么对视频进行压缩?

怎么对视频进行压缩&#xff1f;视频压缩&#xff0c;我们都知道是将视频文件进行压缩变小的过程&#xff0c;是我们日常办公中较为常用的手段。现如今&#xff0c;在视频技术不断发展与创新的基础上&#xff0c;视频分辨率也在不断提高&#xff0c;进而导致文件占有量也非常大…

前后端分离------后端创建笔记(05)用户列表查询接口(下)

本文章转载于【SpringBootVue】全网最简单但实用的前后端分离项目实战笔记 - 前端_大菜007的博客-CSDN博客 仅用于学习和讨论&#xff0c;如有侵权请联系 源码&#xff1a;https://gitee.com/green_vegetables/x-admin-project.git 素材&#xff1a;https://pan.baidu.com/s/…

设计HTML5图像和多媒体

在网页中的文本信息直观、明了&#xff0c;而多媒体信息更富内涵和视觉冲击力。恰当使用不同类型的多媒体可以展示个性&#xff0c;突出重点&#xff0c;吸引用户。在HTML5之前&#xff0c;需要借助插件为网页添加多媒体&#xff0c;如Adobe Flash Player、苹果的QuickTime等。…

DoIP学习笔记系列:(六)满足AES128-CMAC算法的“安全认证”.dll生成实践

文章目录 1. 算法Demo2. 算法实现传送门 DoIP学习笔记系列:导航篇 AES128-CMAC算法在汽车电子控制单元的软件开发中涉及到安全相关的需求经经常用到,具体的算法原理请各位小伙伴自行百度,本篇主要向大家分享该算法如何集成到.dll文件中,在OTA、刷写等场景作为$27服务的安全…

python -m参数的作用(python3 -m)

文章目录 Python -m 参数的作用直接执行模块代码模块自测试环境隔离避免名称冲突 其他&#xff1a;python3 --help Python -m 参数的作用 在Python中&#xff0c;使用-m参数可以执行一个模块作为脚本。它是用于从命令行直接运行一个Python模块的标志。这种方式具有以下几个方面…

RocketMQ消息轨迹产生的背景以及使用方式

这里是weihubeats,觉得文章不错可以关注公众号小奏技术&#xff0c;文章首发。拒绝营销号&#xff0c;拒绝标题党 背景 最近在维护RocketMQ经常会出现这种问题 消息发送方和接收方出现扯皮&#xff0c;消息发送方说我的消息已经发送成功了&#xff0c;消费方说我没接收到消息。…