【微服务】springboot 实现动态修改接口返回值

news2024/9/28 22:22:08

目录

一、前言

二、动态修改接口返回结果实现方案总结

2.1 使用反射动态修改返回结果参数

2.1.1 认识反射

2.1.2 反射的作用

2.1.3 反射相关的类

2.1.4 反射实现接口参数动态修改实现思路

2.2 使用@ControllerAdvice 注解动态修改返回结果参数​​​​​​​

2.2.1 注解作用

2.2.2 实现思路

2.3 使用AOP动态修改返回结果参数

三、动态修改接口返回结果操作实践

3.1 前置准备

3.2 使用反射实现结果集参数动态修改

3.2.1 自定义反射工具类

3.2.2 测试接口继承工具类

3.3 使用@ControllerAdvice实现结果集参数动态修改

3.3.1 @ControllerAdvice 简单介绍

3.3.2 @ControllerAdvice 主要作用

3.3.3 @ControllerAdvice 用法

3.3.4 @ControllerAdvice实现结果集参数动态修改

3.4 使用自定义注解+AOP实现接口参数动态修改

3.4.1 实现思路

3.4.2 自定义注解

3.4.3 自定义AOP类

3.4.4 测试接口一

3.4.5 测试接口二

四、插件化封装

4.1 操作过程

4.1.1 创建maven工程

4.1.2 导入依赖

4.1.3 代码迁移

4.1.4 配置自动装配文件

4.1.5 使用maven命令安装jar包

4.2 功能测试

4.2.1 导入上一步的依赖

4.2.2 接口改造

4.2.3 接口测试

五、写在文末


一、前言

在日常项目开发中,涉及到很多需要动态修改rest接口返回参数的场景,比如对接口中的字段统一脱敏,对接口中的某些字段进行二次加密处理,或者对某些特别的字段根据安全审计要求进行二次处理,甚至需要动态的在接口中增加额外的参数等,诸如此类的场景不胜枚举,本篇将介绍如何在springboot项目对接口返回结果进行动态修改。

二、动态修改接口返回结果实现方案总结

在springboot框架下,基于框架现有提供的技术组件,有很多种实现方式,下面分别展开来说。

2.1 使用反射动态修改返回结果参数

2.1.1 认识反射

Java的反射机制是在运行状态中,对于任意一个类,都能够获得这个类的所有属性和方法;对于任意一个对象,都能够调用它的任何方法和属性。这种动态获取类的信息以及动态调用方法的功能称为Java语言的反射(reflection)机制。

2.1.2 反射的作用

通过反射机制就能在程序运行时发现该对象和类的真实信息,利用这个机制,可以动态修改类对象中的参数信息,比如运行过程中对象参数的值。

2.1.3 反射相关的类

反射中常会涉及到下面几个概念

  • Class类

    • 代表类的实体,在运行的Java应用程序中表示类和接口

  • Field类

    • 代表类的成员变量/字段

  • Method类

    • 代表类的方法

  • Constructor类

    • 代表类的构造方法

2.1.4 反射实现接口参数动态修改实现思路

完整的实现思路如下:

  • 获取接口返回值;

  • 拿到上一步返回值中的结果集对象

    • 拆解结果集,通过反射,获取结果集中的对象实例,解析其中的字段

    • 获取字段的名称,字段的返回值

    • 根据业务需求,对指定的字段结果进行修改

伪代码如下:

public void modifyResult(List<T> result,String... params){
	1、解析结果集
	2、反射获取结果集实例
	3、获取并解析结果集实例中的字段信息
	4、结合入参,动态修改字段值,并重新设置到实例对象中
}

2.2 使用@ControllerAdvice 注解动态修改返回结果参数

2.2.1 注解作用

@ControllerAdvice 是 Spring Framework 提供的一个注解,它用于定义一个全局的异常处理器或跨切面行为(cross-cutting concern)。这个注解可以用来集中处理控制器中的一些公共关注点,如全局异常处理、数据绑定初始化等。主要作用如下:

全局异常处理

 @ControllerAdvice 可以用来定义一个全局的异常处理器。当你在控制器中抛出了一个未被捕获的异常时,你可以定义一个带有 @ExceptionHandler 注解的方法来处理这个异常。这样可以避免在每个控制器或方法中重复定义相同的异常处理逻辑。

统一数据绑定初始化

除了异常处理外,@ControllerAdvice 还可以用来初始化数据绑定,这可以通过使用 @ModelAttribute 注解来实现。这种方法常用于在所有控制器方法调用前预先设置一些模型属性。

统一前置或后置处理

@ControllerAdvice 结合 @InitBinder 注解还可以用来定义全局的绑定初始化器和数据格式化器。此外,还可以使用 @ModelAttribute 注解来定义在所有控制器方法之前执行的前置处理方法,或者使用 @ModelAttribute 注解的方法来填充模型属性。

2.2.2 实现思路

使用@ControllerAdvice 注解实现接口返回值参数动态修改的思路如下:

  • 解析返回结果;

  • 反射获取结果中的对象实例;

  • 修改对象参数;

补充说明:

如果仅仅是为了在返回的结果集增加参数,或者对某些固定参数进行处理,可以忽略反射这一步的操作

2.3 使用AOP动态修改返回结果参数

aop是一种很好的解决公共业务场景下通用问题的实现思路,像本次的需求,修改接口参数一般并不局限于某个具体接口,而是在很多场景下都可能用到,因此使用AOP来解决也是一种很好切入点,具体来说,主要实现思路如下:

  • 自定义注解;

    • 注解中的参数可根据实际需要添加,比如可以添加需要修改的参数名称,修改后的类型等;

  • 为需要修改结果集参数的接口添加上一步的自定义注解;

  • 自定义AOP实现类,解析接口的参数,解析返回结果,利用反射技术,将结果集中需要修改的参数重新赋值;

三、动态修改接口返回结果操作实践

基于上面探讨的几种实现方案,接下来通过实战案例代码分别演示说明。

3.1 前置准备

创建一个springboot工程,并导入如下必要的依赖

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.44</version>
        </dependency>

        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.3</version>
        </dependency>

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

3.2 使用反射实现结果集参数动态修改

参考下面的操作步骤。

3.2.1 自定义反射工具类

完整的代码如下,实现思路:

  • 方法接收一个泛型的对象T,和一组待修改的参数;

  • 使用反射技术实例化对象T,拿到实例对象的字段信息;

  • 循环遍历字段Field对象列表,然后进行参数值的重新赋值;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
public class ResultHandler {

    public <T> void setUserInfo(T t, String... params) {
        if (Objects.isNull(t)) {
            log.error("t 参数为空");
            return;
        }
        List<String> modifyParams = Arrays.stream(params).toList();
        Class<? extends Object> tClass = t.getClass();
        Field[] fields = tClass.getDeclaredFields();
        Arrays.stream(fields)
                .filter(item ->
                        modifyParams.contains(item.getName())
                )
                .collect(Collectors.toList()).
                forEach(field -> {
                    field.setAccessible(true);
                    String fieldName = field.getName();
                    Object value = null;
                    try {
                        value = field.get(t);
                        field.set(t, value + "_change");
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                });
    }

}

3.2.2 测试接口继承工具类

如果你的接口需要动态修改返回值参数,可以继承上述工具类,如下:

@RestController
public class AviatorController extends ResultHandler{

    //localhost:8081/aop/post/test
    @PostMapping("/aop/post/test")
    public UserRequest testPost(@RequestBody(required = false) UserRequest userRequest) {
        System.out.println("进入接口");
        setUserInfo(userRequest,"name");
        return new UserRequest(userRequest.getName(),userRequest.getAddress());
    }

}

在上面的接口中,在最终返回数据之前,调用工具类中的方法,传入返回值吗,剩下的交给工具类中的方法处理即可,启动工程之后,测试一下接口,可以看到,返回值中的name参数就被修改了

3.3 使用@ControllerAdvice实现结果集参数动态修改

3.3.1 @ControllerAdvice 简单介绍

@ControllerAdvice 是 Spring Framework 提供的一个注解,用于定义一个全局异常处理器或者跨切面的增强功能。它是一个特殊的切面(AOP Aspect),可以用于处理控制器(@Controller 或 @RestController)中的异常、数据绑定错误、模型属性预填充以及其他跨切面的关注点。@ControllerAdvice 注解通常用在需要对多个控制器进行统一处理的场景中,比如全局异常处理、数据验证失败处理、模型属性预填充等。

3.3.2 @ControllerAdvice 主要作用

@ControllerAdvice 主要有如下作用:

  • 全局异常处理

    • 可以捕获所有控制器中抛出的异常,并提供统一的处理逻辑。

    • 使开发者能够集中处理异常,而不是在每个控制器中重复编写相同的异常处理代码。

  • 数据绑定错误处理

    • 当数据绑定失败时,可以捕获 BindExceptionMethodArgumentNotValidException 等异常,并进行统一处理。

    • 便于对前端传来的数据进行统一的校验和错误提示。

  • 模型属性预填充

    • 可以在请求处理之前预先填充模型属性,比如当前时间、用户信息等。

    • 使得控制器方法更加简洁,减少重复代码。

  • 跨切面关注点

    • 可以用来处理一些横切关注点,比如日志记录、安全检查等。

    • 通过 @ModelAttribute 或者自定义注解来实现。

3.3.3 @ControllerAdvice 用法

全局异常处理

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(value = {NullPointerException.class})
    public ResponseEntity<Object> handleNullPointerException(NullPointerException ex) {
        // 处理空指针异常
        return ResponseEntity.status(400).body("Null pointer exception occurred: " + ex.getMessage());
    }

    @ExceptionHandler(value = {IllegalArgumentException.class})
    public ResponseEntity<Object> handleIllegalArgumentException(IllegalArgumentException ex) {
        // 处理非法参数异常
        return ResponseEntity.status(400).body("Illegal argument exception occurred: " + ex.getMessage());
    }
}

数据绑定错误处理

import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import java.util.HashMap;
import java.util.Map;

@ControllerAdvice
public class DataBindingExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Object> handleValidationExceptions(MethodArgumentNotValidException ex) {
        BindingResult result = ex.getBindingResult();
        Map<String, String> errors = new HashMap<>();
        result.getAllErrors().forEach((error) -> {
            String fieldName = ((FieldError) error).getField();
            String errorMessage = error.getDefaultMessage();
            errors.put(fieldName, errorMessage);
        });
        return ResponseEntity.badRequest().body(errors);
    }
}

模型属性预填充

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;

@ControllerAdvice
public class ModelAttributePrePopulator {

    @ModelAttribute("currentUser")
    public String getCurrentUser() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        return authentication.getName();
    }
}

3.3.4 @ControllerAdvice实现结果集参数动态修改

自定义一个类实现ResponseBodyAdvice接口,如下:

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.HashMap;
import java.util.Map;

@ControllerAdvice
public class DataChangeAdvice implements ResponseBodyAdvice {

    static ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType,
                                  MediaType selectedContentType, Class selectedConverterType,
                                  ServerHttpRequest request, ServerHttpResponse response) {

        Map res = new HashMap();
        res.put("code",200);
        //如果返回值是String,直接放到Result里
        if (body instanceof String) {
            res.put("data",(String) body);
            return res;
        }
        //如果返回值是标准返回格式,就不需要再次封装了
        //如果不加这个判断,异常的结果会被封装两次
        else if (body instanceof Map) {
            return body;
        }
        String dataStr = null;
        try {
            dataStr = objectMapper.writeValueAsString(body);
            res.put("data",dataStr);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return res;
    }

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }
}

运行工程之后,测试一下上面的接口,可以看到,原本的接口返回值根据业务的需要重新做了修改

基于上述的改造,还可以继续扩展,比如通过自定义注解,在接口上面添加自定义注解,然后再在返回值中解析自定义注解,并根据实际的需要重新对注解中的参数进行修改。

3.4 使用自定义注解+AOP实现接口参数动态修改

在之前分享的一篇文章中,我们使用AOP+自定义注解的方式实现了请求参数的动态修改,使用这个方式是否也可以对接口返回的参数进行修改呢?

3.4.1 实现思路

参考下面的实现思路进行实现

  • 自定义注解,

    • 属性主要包括:待修改的结果参数名称,修改的格式等;

  • 自定义AOP类,对于那些标注了上述自定义注解的接口进行拦截;

    • 使用环绕通知的方式;

  • 在AOP执行方法中调用point.proceed()获取目标方法的执行结果;

    • 在结果中,使用反射,结合解析到的自定义注解,从而动态修改接口的参数值;

3.4.2 自定义注解

自定义一个注解,用于接口中待修改的参数进行标注

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ModifyResponseParams {

    Param[] value() default {};

    String dataFormat() default "";

    @Retention(RetentionPolicy.RUNTIME)
    @Target({})
    public static @interface Param {
        String name();
        String value() default "";
    }

}

3.4.3 自定义AOP类

aop中的业务逻辑即可结合上面的实现思路进行理解,参考如下完整的示例代码,实现逻辑也是按照上述的实现思路进行构建

package com.congge.aop;

import com.congge.controller.R;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Aspect
@Component
@Order(1)
@Slf4j
public class ResponseParamModifierAspect {

    @Around("@annotation(modifyResponseParams)")
    public Object modifyRequestParams(ProceedingJoinPoint point, ModifyResponseParams modifyResponseParams) throws Throwable {

        List<String> modifyParams = new ArrayList<>();
        for (ModifyResponseParams.Param param : modifyResponseParams.value()) {
            modifyParams.add(param.name());
        }

        Object t = point.proceed();
        if (Objects.isNull(t)) {
            log.error("接口返回结果为空");
            return t;
        }
        //获取返回结果集并解析
        R<?> r = (R<?>) t;
        Object data = r.getData();
        if (data instanceof List<?>) {
            List<?> list = (List<?>) data;
            list.forEach(item ->{
                Class<?> tClass = item.getClass();
                Field[] fields = tClass.getDeclaredFields();
                for (Field field : fields) {
                    field.setAccessible(true);
                    String fieldName = field.getName();
                    if(modifyParams.contains(fieldName)){
                        try {
                            Object value = field.get(item);
                            field.set(item, value + "_change");
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            });
        }else {
            Object t1 = data;
            Class<? extends Object> tClass = t1.getClass();
            Field[] fields = tClass.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                String fieldName = field.getName();
                if(modifyParams.contains(fieldName)){
                    try {
                        Object value = field.get(t1);
                        field.set(t1, value + "_change");
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
        return t;
    }

}

3.4.4 测试接口一

在测试接口上面添加上述自定义注解,对需要修改的参数在注解中进行标注

    @ModifyResponseParams(value = {
            @ModifyResponseParams.Param(name = "address"),
            @ModifyResponseParams.Param(name = "name")
    })
    @PostMapping("/aop/modify/v1")
    public R testModifyV1(@RequestBody(required = false) UserRequest userRequest) {
        System.out.println("进入接口");
        return R.ok(new UserRequest(userRequest.getName(),userRequest.getAddress()));
    }

使用接口工具调用一下,可以看到接口返回值中的两个参数被修改了

3.4.5 测试接口二

这一次,返回一个集合

    @ModifyResponseParams(value = {
            @ModifyResponseParams.Param(name = "address"),
            @ModifyResponseParams.Param(name = "name")
    })
    @PostMapping("/aop/modify/v2")
    public R testModifyV2(@RequestBody(required = false) UserRequest userRequest) {
        System.out.println("进入接口");
        List<UserRequest> userRequests =
                Arrays.asList(new UserRequest(userRequest.getName(), userRequest.getAddress()));
        return R.ok(userRequests);
    }

使用接口工具调用一下,可以看到接口返回值中的两个参数被修改了

四、插件化封装

有了上面的实践之后,为了减少后续遇到类似的场景时的多次重复编码,可以考虑将上述AOP的实现方案使用springboot的starter机制进行插件化封装,参考如下操作步骤。

4.1 操作过程

4.1.1 创建maven工程

工程目录结构如下

4.1.2 导入依赖

主要包括下面几个核心依赖

    <dependencies>

        <!-- Spring框架基本的核心工具 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>

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

        <!--阿里 FastJson依赖-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.44</version>
        </dependency>

        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.3</version>
        </dependency>

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

    </dependencies>

4.1.3 代码迁移

将上一小节中的几个核心实现类拷贝过来到aop包下(略)

4.1.4 配置自动装配文件

在resources目录下,参考工程结构,创建配置文件 spring.factories ,将AOP的完整类路径名称配置进去

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.congge.aop.ResponseParamModifierAspect

4.1.5 使用maven命令安装jar包

执行mvn install 命令,将工程的jar安装到本地仓库中

4.2 功能测试

4.2.1 导入上一步的依赖

在需要的工程pom中导入上一步的依赖jar的maven坐标

        <dependency>
            <groupId>com.congge</groupId>
            <artifactId>aop_com</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

4.2.2 接口改造

原本的接口代码逻辑保持不变,只需要将自定义注解改为上一步的注解即可

4.2.3 接口测试

启动工程之后再次调用上述接口,接口返回值参数被修改了,说明插件包中的逻辑正常生效了

五、写在文末

本文通过案例和操作详细介绍了如何在微服务项目中实现对接口返回值的参数修改,在实际应用中,可以结合案例中的思路以及自身的需求场景进行深度的拓展,希望对看到的同学有用,本篇到此结束,感谢观看。

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

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

相关文章

【C++算法】4.双指针_快乐数

文章目录 题目链接&#xff1a;题目描述&#xff1a;解法C 算法代码&#xff1a;图解&#xff1a; 题目链接&#xff1a; 202.快乐数 题目描述&#xff1a; 解法 根据题目来看&#xff0c;可能是无限循环&#xff0c;也可能是快乐数。因为就相当于下图&#xff1a; 无限循环可…

QT--基础

将默认提供的程序都注释上意义 0101.pro QT core gui #QT表示要引入的类库 core&#xff1a;核心库 gui&#xff1a;图形化界面库 #如果要使用其他库类中的相关函数&#xff0c;则需要加对应的库类后&#xff0c;才能使用 greaterThan(QT_MAJOR_VERSION, 4): QT wid…

AMD 矩阵核心

AMD matrix cores — ROCm Blogs 注意&#xff1a; 本文博客之前是 AMD lab notes 博客系列的一部分。 矩阵乘法是线性代数的一个基本方面&#xff0c;它在高性能计算&#xff08;HPC&#xff09;应用中是一个普遍的计算。自从 AMD 推出 CDNA 架构以来&#xff0c;广义矩阵乘法…

基于SpringBoot+Vue+MySQL的甜品店管理系统

系统展示 用户前台界面 管理员后台界面 系统背景 在数字化浪潮的推动下&#xff0c;甜品店行业也面临着转型与升级的需求。传统的线下经营模式已难以满足现代消费者对于便捷、高效购物体验的追求。为了提升运营效率、优化顾客体验&#xff0c;我们设计了一款基于SpringBoot后端…

Django基础-创建新项目,各文件作用

学习Django的前置知识&#xff1a; python基本语法&#xff1a;需要掌握Python中的变量、循环、条件判断、函数等基本概念。面向对象编程&#xff08;OOP&#xff09;&#xff1a;Django的核心架构基于面向对象编程&#xff0c;许多功能&#xff08;如模型和视图&#xff09;依…

黑神话悟空小西天

游戏里我们一开始就出现一个很可爱的小和尚&#xff0c;当脚步声传来&#xff0c;小和尚化身为一尊弥勒佛&#xff0c;而这尊弥勒佛的大小和位置都在说&#xff0c;这里没有弥勒佛的位置。 随后天命人进入一片雪地&#xff0c;遇到了赤尻马猴&#xff0c;打跑赤尻马猴&#xff…

C++_unordered系列关联式容器(哈希)

unordered系列关联式容器&#xff0c;我们曾在C_map_set详解一文中浅浅的提了几句。今天我们来详细谈谈 本身在C11之前是没有unordered系列关联式容器的&#xff0c;unordered系列与普通的map、set的核心功能重叠度达到了90%&#xff0c;他们最大的不同就是底层结构的不同&…

AVL树(平衡二叉树)的介绍以及相关构建

欢迎光临 &#xff1a; 羑悻的小杀马特-CSDN博客 目录 一AVL树的介绍&#xff1a; 二AVL树的实现&#xff1a; 1结构框架&#xff1a; 2节点的插入&#xff1a; 旋转&#xff1a; 21左单旋&#xff1a; 2.1.1左单旋介绍及步骤&#xff1a; 2.1.2左单旋代码实…

【JavaSE系列】IO流

目录 前言 一、IO流概述 二、IO流体系结构 三、File相关的流 1. FileInputStream 2. FileOutputStream 3. FileReader 4. FileWriter 四、缓冲流 五、转换流 1. InputStreamReader 2. OutputStreamWriter 六、数据流 七、对象流 八、打印流 九、标准输入输出流…

C++学习9.28

1> 创建一个新项目&#xff0c;将默认提供的程序都注释上意义 por QT core gui #QT表示引入的类库 core:核心库例如IO操作在该库中 gui:图形化显示库 #如果要使用其他类库中的相关函数&#xff0c;就需要调用相关类库后&#xff0c;才能加以使用greaterThan(Q…

c++926

1.什么是虚函数&#xff1f;什么是纯虚函数&#xff1f; 虚函数&#xff1a;被virtual关键字修饰的成员函数&#xff0c;用于实现多态性&#xff0c;通过基类访问派生类的函数。纯虚函数&#xff1a;在虚函数后面添加0&#xff0c;只有声明而没有实现&#xff0c;需要派生类提…

天龙八部怀旧单机微改人面桃花+安装教程+GM工具+虚拟机一键端

今天给大家带来一款单机游戏的架设&#xff1a;天龙八部怀旧单机微改人面桃花。 另外&#xff1a;本人承接各种游戏架设&#xff08;单机联网&#xff09; 本人为了学习和研究软件内含的设计思想和原理&#xff0c;带了架设教程仅供娱乐。 教程是本人亲自搭建成功的&#xf…

图说数集相等定义表明“R各元x的对应x+0.0001的全体=R“是几百年重大错误

黄小宁 设集A&#xff5b;x&#xff5d;表A各元均由x代表&#xff0c;&#xff5b;x&#xff5d;中变量x的变域是A。其余类推。因各数x可是数轴上点的坐标故x∈R变为实数yx1的几何意义可是&#xff1a;一维空间“管道”g内R轴上的质点x∈R(x是点的坐标)沿“管道”g平移变为点y…

红队信息搜集扫描使用

红队信息搜集扫描使用 红队行动中需要工具化一些常用攻击&#xff0c;所以学习一下 nmap 等的常规使用&#xff0c;提供灵感 nmap 帮助 nmap --help主机扫描 Scan and no port scan&#xff08;扫描但不端口扫描&#xff09;。-sn 在老版本中是 -sP&#xff0c;P的含义是 P…

视频美颜SDK与直播美颜工具API是什么?计算机视觉技术详解

今天&#xff0c;小编将深入探讨视频美颜SDK与直播美颜工具API的概念及其背后的计算机视觉技术。 一、视频美颜SDK的概念 视频美颜SDK是一套用于开发实时美颜效果的工具集&#xff0c;开发者可以利用它在视频流中实现面部特征的优化。这些SDK通常提供了一系列功能&#xff0c…

.NET 红队武器库和资源集合 (第38期)

01阅读须知 此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等&#xff08;包括但不限于&#xff09;进行检测或维护参考&#xff0c;未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失&#xf…

计算机网络自顶向下(1)---网络基础

目录 1.网络的分类 2.网络协议 3.网络分层结构 1.OSI七层模型 2.TCP/IP四层模型 3.网络与OS的关系 4.网络传输基本流程 1.协议报头 5.网络中的地址管理 1.IP地址 2.端口号 6.传输层协议 1.TCP协议 2.UDP协议 3.网络字节序 7.socket 1.网络的分类 局域网&…

excel-VBA知识点记录

1、计算机硬件的组成部分 内存&#xff0c;一旦断电&#xff0c;存储在里面的数据就消失了&#xff0c;而硬盘是永久存储数据的&#xff0c;所以刚开始我们在文件里面编辑没有按保存的时候&#xff0c;数据是在内存里面的&#xff0c;一旦断电数据就没了&#xff0c;但我们点了…

大语言模型知识点分享

1 目前主流的开源模型体系有哪些&#xff1f; Prefix Decoder 系列模型 核心点&#xff1a; 输入采用双向注意力机制&#xff0c;输出为单向注意力。双向注意力意味着输入的每个部分都可以关注到输入的所有其他部分&#xff0c;这在理解上下文时具有很强的优势。 代表模型&a…

六级翻译 高分笔记

第一节 句子的拆分与重组 核心原则&#xff1a;拆主干&#xff0c;补修饰 一、句子的拆分与重组 1.青藏铁路是世界最高最长的高原铁路。&#xff08;“的”字前面所有去掉&#xff0c;就是句子主干&#xff09; The Qinghai-Tibet Railway is the highest and longest plate…