从开源项目中学习如何自定义 Spring Boot Starter 小组件

前言

今天参考的开源组件Graceful Response——Spring Boot接口优雅响应处理器。

具体用法可以参考github以及官方文档。

image-20231214090633021

基本使用

引入Graceful Response组件

项目中直接引入如下maven依赖,即可使用其相关功能。

        <dependency>
            <groupId>com.feiniaojin</groupId>
            <artifactId>graceful-response</artifactId>
            <version>{latest.version}</version>
        </dependency>

开始使用

配置文件可不配置,直接全部默认配置

graceful-response:
  response-style: 1  # 配置响应格式类型(0 or 1)后续介绍
  print-exception-in-global-advice: true # 在全局异常处理器中打印异常,默认不打印

一切之前需在主启动类添加该注解开启功能:@EnableGracefulResponse。(后续介绍)

Controller直接返回我们查询的对象即可,Graceful Response会帮我们封装响应格式(响应格式可配置

Controller

@RestController
public class HelloController {
    
    @Resource
    private UserMapper userMapper;
    
    @GetMapping("/userList")
    public List<User> userList() {
        return userMapper.selectList(null);
    }
}

调用接口:

image-20231214113454576

默认成功/失败状态码和提示消息也都是可以配置的,是不是感觉非常的神奇(一切皆可配)

image-20231214114109561

其他具体用法见官网。

废话不多说,接下来我们直接步入主题探究下自定义starter组件核心步骤

自定义starter组件核心步骤

首先明确一下,自定义starter组件的三个核心步骤:

  1. 自定义注解
  2. 结合Spring AOP实现注解逻辑
  3. 实现starter组件自动装配以及可配置

接下来看开源组件都是怎么做的吧~

看看源码目录结构(麻雀虽小但五脏俱全):

image-20231214103700335

0. 注解使用

@ExceptionMapper注解为例,介绍一下相关功能及实现原理。

Graceful Response引入@ExceptionMapper注解,通过该注解将异常和错误码关联起来,这样Service方法就不需要再维护Response的响应码了,直接抛出业务异常,由Graceful Response进行异常和响应码的关联。 @ExceptionMapper的用法如下:

自定义业务类异常

/**
 * NotFoundException的定义,使用@ExceptionMapper注解修饰
 * code:代表接口的异常码
 * msg:代表接口的异常提示
 */
@ExceptionMapper(code = "1404", msg = "找不到对象")
public class NotFoundException extends RuntimeException {

}

Service接口定义

public interface QueryService {
    UserInfoView queryOne(Query query);
}

Service接口实现

@Service
public class QueryServiceImpl implements QueryService {
    @Resource
    private UserInfoMapper mapper;

    public UserInfoView queryOne(Query query) {
        UserInfo userInfo = mapper.findOne(query.getId());
        if (Objects.isNull(userInfo)) {
            // 这里直接抛自定义异常
            throw new NotFoundException();
        }
        // ……后续业务操作
    }
}

当Service层的queryOne方法抛出NotFoundException时,Graceful Response会进行异常捕获,并将NotFoundException对应的异常码和异常信息封装到统一的响应对象中,最终接口返回以下JSON(默认响应格式

{
  "status": {
    "code": "1404",
    "msg": "找不到对象"
  },
  "payload": {}
}

使用起来十分方便,接下来我们看下具体实现原理。

1. 自定义注解

首先看下注解定义:

/**
 * 异常映射注解.
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ExceptionMapper {

    /**
     * 异常对应的错误码.
     *
     * @return 异常对应的错误码
     */
    String code() default "ERROR";

    /**
     * 异常信息.
     *
     * @return 异常对应的提示信息
     */
    String msg() default "Poor network quality!";

    /**
     * 异常信息是否支持替换
     * 仅当msgReplaceable==ture,且异常实例的message不为空时才能替换
     */
    boolean msgReplaceable() default false;
}

2. 结合Spring AOP实现注解逻辑(核心)

配置类

配置类GracefulResponseProperties跟可配置项相关,所有配置项都映射为该类

@ConfigurationProperties(prefix = "graceful-response") // 配置前缀
public class GracefulResponseProperties {

    /**
     * 在全局异常处理器中打印异常,默认不打印
     */
    private boolean printExceptionInGlobalAdvice = false;

    /**
     * 默认的Response实现类名称,配置了responseClassFullName,则responseStyle不生效
     */
    private String responseClassFullName;

    /**
     * responseStyle的风格,responseClassFullName为空时才会生效
     * responseStyle==null或者responseStyle==0,Response风格为 DefaultResponseImplStyle0
     * responseStyle=1,Response风格为 DefaultResponseImplStyle1
     */
    private Integer responseStyle;

    /**
     * 默认的成功返回码 默认0
     */
    private String defaultSuccessCode = DefaultConstants.DEFAULT_SUCCESS_CODE;

    /**
     * 默认的成功提示 默认OK
     */
    private String defaultSuccessMsg = DefaultConstants.DEFAULT_SUCCESS_MSG;

    /**
     * 默认的失败码 默认1
     */
    private String defaultErrorCode = DefaultConstants.DEFAULT_ERROR_CODE;

    /**
     * 默认的失败提示 默认error
     */
    private String defaultErrorMsg = DefaultConstants.DEFAULT_ERROR_MSG;

    /**
     * Validate异常码,不提供的话默认DefaultConstants.DEFAULT_ERROR_CODE
     */
    private String defaultValidateErrorCode = DefaultConstants.DEFAULT_ERROR_CODE;

    /**
     * 例外包路径
     */
    private List<String> excludePackages;

    /**
     * 不使用@ExceptionMapper和@ExceptionAliasFor修饰的原生异常
     * 是否使用异常信息Throwable类的detailMessage进行返回
     * originExceptionUsingDetailMessage=false,则msg=defaultErrorMsg
     */
    private Boolean originExceptionUsingDetailMessage = false;

    // getter / setter
}

设置响应状态

ResponseStatusFactory:响应状态工厂(仅定义code和msg),默认实现成功默认响应、失败默认响应以及自定义code、msg响应状态

默认实现类:

// 响应状态
public interface ResponseStatus {

    void setCode(String code);

    String getCode();

    void setMsg(String msg);

    String getMsg();
}

// 默认响应状态
public class DefaultResponseStatus implements ResponseStatus {

    private String code;

    private String msg;

    public DefaultResponseStatus() {
    }

    public DefaultResponseStatus(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    @Override
    public void setCode(String code) {
        this.code = code;
    }

    @Override
    public String getCode() {
        return code;
    }

    @Override
    public void setMsg(String msg) {
        this.msg = msg;
    }

    @Override
    public String getMsg() {
        return msg;
    }
}
// 核心实现类
public class DefaultResponseStatusFactoryImpl implements ResponseStatusFactory {

    @Resource
    private GracefulResponseProperties properties;

    // 默认成功响应转态
    @Override
    public ResponseStatus defaultSuccess() {

        DefaultResponseStatus defaultResponseStatus = new DefaultResponseStatus();
        defaultResponseStatus.setCode(properties.getDefaultSuccessCode());
        defaultResponseStatus.setMsg(properties.getDefaultSuccessMsg());
        return defaultResponseStatus;
    }

    // 默认失败响应转态
    @Override
    public ResponseStatus defaultError() {
        DefaultResponseStatus defaultResponseStatus = new DefaultResponseStatus();
        defaultResponseStatus.setCode(properties.getDefaultErrorCode());
        defaultResponseStatus.setMsg(properties.getDefaultErrorMsg());
        return defaultResponseStatus;
    }

    // 自定code、msg状态
    @Override
    public ResponseStatus newInstance(String code, String msg) {
        return new DefaultResponseStatus(code, msg);
    }
}

设置响应格式

ResponseFactory:根据配置项,设置响应格式

默认实现类:

public class DefaultResponseFactory implements ResponseFactory {

    private final Logger logger = LoggerFactory.getLogger(DefaultResponseFactory.class);

    private static final Integer RESPONSE_STYLE_0 = 0;

    private static final Integer RESPONSE_STYLE_1 = 1;

    @Resource
    private ResponseStatusFactory responseStatusFactory;

    @Resource
    private GracefulResponseProperties properties;

    @Override
    public Response newEmptyInstance() {
        try {
            String responseClassFullName = properties.getResponseClassFullName();

            // 配置了Response的全限定名,即实现Response接口,用配置的进行返回
            if (StringUtils.hasLength(responseClassFullName)) {
                Object newInstance = Class.forName(responseClassFullName).getConstructor().newInstance();
                return (Response) newInstance;
            } else {
                // 没有配Response的全限定名,则创建DefaultResponse
                return generateDefaultResponse();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // 响应格式判断
    private Response generateDefaultResponse() {
        Integer responseStyle = properties.getResponseStyle();
        // 未配置或者配置0
        if (Objects.isNull(responseStyle) || RESPONSE_STYLE_0.equals(responseStyle)) {
            return new DefaultResponseImplStyle0();
        } else if (RESPONSE_STYLE_1.equals(responseStyle)) {
            return new DefaultResponseImplStyle1();
        } else {
            logger.error("不支持的Response style类型,responseStyle={}", responseStyle);
            throw new IllegalArgumentException("不支持的Response style类型");
        }
    }

    @Override
    public Response newInstance(ResponseStatus responseStatus) {
        Response bean = this.newEmptyInstance();
        bean.setStatus(responseStatus);
        return bean;
    }

    @Override
    public Response newSuccessInstance() {
        Response emptyInstance = this.newEmptyInstance();
        emptyInstance.setStatus(responseStatusFactory.defaultSuccess());
        return emptyInstance;
    }

    @Override
    public Response newSuccessInstance(Object payload) {
        Response bean = this.newSuccessInstance();
        bean.setPayload(payload);
        return bean;
    }

    @Override
    public Response newFailInstance() {
        Response bean = this.newEmptyInstance();
        bean.setStatus(responseStatusFactory.defaultError());
        return bean;
    }

}

对应配置文件中两种响应格式:

image-20231214140225763

默认响应格式:

public interface Response {
    
    void setStatus(ResponseStatus statusLine);

    ResponseStatus getStatus();

    void setPayload(Object payload);

    Object getPayload();
}

image-20231214142333843

默认格式实现类:

{
  "status": {
    "code": "",
    "msg": ""
  },
  "payload": {}
}
public class DefaultResponseImplStyle0 implements Response {

    private ResponseStatus status;
    
    private Object payload = Collections.emptyMap();

    public DefaultResponseImplStyle0() {
    }

    public DefaultResponseImplStyle0(Object payload) {
        this.payload = payload;
    }

    @Override
    public void setStatus(ResponseStatus responseStatus) {
        this.status = responseStatus;
    }

    @Override
    public ResponseStatus getStatus() {
        return status;
    }

    @Override
    public void setPayload(Object obj) {
        this.payload = obj;
    }

    @Override
    public Object getPayload() {
        return payload;
    }
}

style设置为1,响应格式:

{

    "code": "1404",
    "msg": "找不到对象"
    "data": {}
}

另一种响应实现类

public class DefaultResponseImplStyle1 implements Response {

    private String code;

    private String msg;

    private Object data = Collections.emptyMap();

    @Override
    public void setStatus(ResponseStatus statusLine) {
        this.code = statusLine.getCode();
        this.msg = statusLine.getMsg();
    }

    @Override
    @JsonIgnore
    public ResponseStatus getStatus() {
        return null;
    }

    @Override
    public void setPayload(Object payload) {
        this.data = payload;
    }

    // 这里直接把 payload 忽略了(因此这种响应格式中没有payload字段)
    @Override
    @JsonIgnore
    public Object getPayload() {
        return null;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }
}

核心处理逻辑类

核心处理逻辑类:全局异常处理

/**
 * 全局异常处理.
 */
@ControllerAdvice
@Order(200)
public class GlobalExceptionAdvice implements ApplicationContextAware {

    private final Logger logger = LoggerFactory.getLogger(GlobalExceptionAdvice.class);

    @Resource
    private ResponseStatusFactory responseStatusFactory;

    @Resource
    private ResponseFactory responseFactory;

    private ExceptionAliasRegister exceptionAliasRegister;

    // 配置类
    @Resource
    private GracefulResponseProperties properties;

    /**
     * 异常处理逻辑. 【统一捕获所有异常】
     *
     * @param throwable 业务逻辑抛出的异常
     * @return 统一返回包装后的结果
     */
    @ExceptionHandler({Throwable.class})
    @ResponseBody 
    public Response exceptionHandler(Throwable throwable) {
        // 配置项是否打印异常信息(默认false)
        if (properties.isPrintExceptionInGlobalAdvice()) {
            logger.error("Graceful Response:GlobalExceptionAdvice捕获到异常,message=[{}]", throwable.getMessage(), throwable);
        }
        ResponseStatus statusLine;
        if (throwable instanceof GracefulResponseException) {
            statusLine = fromGracefulResponseExceptionInstance((GracefulResponseException) throwable);
        } else {
            // 校验异常转自定义异常
            statusLine = fromExceptionInstance(throwable);
        }
        // 设置完code、msg之后直接返回
        return responseFactory.newInstance(statusLine);
    }

    private ResponseStatus fromGracefulResponseExceptionInstance(GracefulResponseException exception) {
        String code = exception.getCode();
        if (code == null) {
            code = properties.getDefaultErrorCode();
        }
        return responseStatusFactory.newInstance(code,
                exception.getMsg());
    }

    private ResponseStatus fromExceptionInstance(Throwable throwable) {

        Class<? extends Throwable> clazz = throwable.getClass();

        // 【直接获取抛出异常上的注解】
        ExceptionMapper exceptionMapper = clazz.getAnnotation(ExceptionMapper.class);

        // 1.有@ExceptionMapper注解,直接设置结果的状态
        if (exceptionMapper != null) {
            boolean msgReplaceable = exceptionMapper.msgReplaceable();
            //异常提示可替换+抛出来的异常有自定义的异常信息
            if (msgReplaceable) {
                String throwableMessage = throwable.getMessage();
                if (throwableMessage != null) {
                    return responseStatusFactory.newInstance(exceptionMapper.code(), throwableMessage);
                }
            }
            return responseStatusFactory.newInstance(exceptionMapper.code(),
                    exceptionMapper.msg());
        }

        // 2.有@ExceptionAliasFor异常别名注解,获取已注册的别名信息
        if (exceptionAliasRegister != null) {
            ExceptionAliasFor exceptionAliasFor = exceptionAliasRegister.getExceptionAliasFor(clazz);
            if (exceptionAliasFor != null) {
                return responseStatusFactory.newInstance(exceptionAliasFor.code(),
                        exceptionAliasFor.msg());
            }
        }
        ResponseStatus defaultError = responseStatusFactory.defaultError();

        // 3. 原生异常 + originExceptionUsingDetailMessage=true
        // 如果有自定义的异常信息,原生异常将直接使用异常信息进行返回,不再返回默认错误提示
        if (properties.getOriginExceptionUsingDetailMessage()) {
            String throwableMessage = throwable.getMessage();
            if (throwableMessage != null) {
                defaultError.setMsg(throwableMessage);
            }
        }
        return defaultError;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.exceptionAliasRegister = applicationContext.getBean(ExceptionAliasRegister.class);
    }
}

非空返回值封装处理类(ResponseBodyAdvice)

ResponseBodyAdvice:泛型T为响应data类型,直接Object即可

@ControllerAdvice
@Order(value = 1000)
public class NotVoidResponseBodyAdvice implements ResponseBodyAdvice<Object> {

    private final Logger logger = LoggerFactory.getLogger(NotVoidResponseBodyAdvice.class);

    @Resource
    private ResponseFactory responseFactory;
    @Resource
    private GracefulResponseProperties properties;

    /**
     * 路径过滤器
     */
    private static final AntPathMatcher ANT_PATH_MATCHER = new AntPathMatcher();

    /**
     * 只处理不返回void的,并且MappingJackson2HttpMessageConverter支持的类型.
     *
     * @param methodParameter 方法参数
     * @param clazz           处理器
     * @return 是否支持
     */
    @Override
    public boolean supports(MethodParameter methodParameter,
                            Class<? extends HttpMessageConverter<?>> clazz) {
        Method method = methodParameter.getMethod();

        // method为空、返回值为void、非JSON,直接跳过
        if (Objects.isNull(method)
                || method.getReturnType().equals(Void.TYPE)
                || !MappingJackson2HttpMessageConverter.class.isAssignableFrom(clazz)) {
            logger.debug("Graceful Response:method为空、返回值为void、非JSON,跳过");
            return false;
        }

        // 有ExcludeFromGracefulResponse注解修饰的,也跳过
        if (method.isAnnotationPresent(ExcludeFromGracefulResponse.class)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Graceful Response:方法被@ExcludeFromGracefulResponse注解修饰,跳过:methodName={}", method.getName());
            }
            return false;
        }

        // 配置了例外包路径,则该路径下的controller都不再处理
        List<String> excludePackages = properties.getExcludePackages();
        if (!CollectionUtils.isEmpty(excludePackages)) {
            // 获取请求所在类的的包名
            String packageName = method.getDeclaringClass().getPackage().getName();
            if (excludePackages.stream().anyMatch(item -> ANT_PATH_MATCHER.match(item, packageName))) {
                logger.debug("Graceful Response:匹配到excludePackages例外配置,跳过:packageName={},", packageName);
                return false;
            }
        }
        logger.debug("Graceful Response:非空返回值,需要进行封装");
        return true;
    }

    // 满足上述条件,封装返回对象(只要是非Void返回值,不做任何配置都会封装返回结果)
    @Override
    public Object beforeBodyWrite(Object body,
                                  MethodParameter methodParameter,
                                  MediaType mediaType,
                                  Class<? extends HttpMessageConverter<?>> clazz,
                                  ServerHttpRequest serverHttpRequest,
                                  ServerHttpResponse serverHttpResponse) {
        if (body == null) {
            return responseFactory.newSuccessInstance();
        } else if (body instanceof Response) {
            return body;
        } else {
            // 设置data数据
            return responseFactory.newSuccessInstance(body);
        }
    }

}

3. 实现starter组件自动装配以及可配置

全局自动装配类

该类可以将我们自定义的所有类全部加载到Spring容器中。

/**
 * 全局返回值处理的自动配置.
 */
@Configuration
// 将外部配置文件中的属性注入到配置类中(将配置类加载到Spring容器中)
@EnableConfigurationProperties(GracefulResponseProperties.class)
public class AutoConfig {

    @Bean
    // 这个注解很是重要
    @ConditionalOnMissingBean(value = GlobalExceptionAdvice.class)
    public GlobalExceptionAdvice globalExceptionAdvice() {
        return new GlobalExceptionAdvice();
    }

    @Bean
    @ConditionalOnMissingBean(value = ValidationExceptionAdvice.class)
    public ValidationExceptionAdvice validationExceptionAdvice() {
        return new ValidationExceptionAdvice();
    }

    @Bean
    @ConditionalOnMissingBean(NotVoidResponseBodyAdvice.class)
    public NotVoidResponseBodyAdvice notVoidResponseBodyAdvice() {
        return new NotVoidResponseBodyAdvice();
    }

    @Bean
    @ConditionalOnMissingBean(VoidResponseBodyAdvice.class)
    public VoidResponseBodyAdvice voidResponseBodyAdvice() {
        return new VoidResponseBodyAdvice();
    }

    @Bean
    @ConditionalOnMissingBean(value = {ResponseFactory.class})
    public ResponseFactory responseBeanFactory() {
        return new DefaultResponseFactory();
    }

    @Bean
    @ConditionalOnMissingBean(value = {ResponseStatusFactory.class})
    public ResponseStatusFactory responseStatusFactory() {
        return new DefaultResponseStatusFactoryImpl();
    }

    @Bean
    public ExceptionAliasRegister exceptionAliasRegister() {
        return new ExceptionAliasRegister();
    }

    @Bean
    public Init init(){
        return new Init();
    }
}

注解启动全局结果处理入口

通过元注解 @Import(AutoConfig.class) 实际上将 AutoConfig 这个配置类引入到标注了 @EnableGracefulResponse 注解的类中。

引入该组件,只有在某个类上添加了 @EnableGracefulResponse 注解时,AutoConfig 中定义的相关 Bean 才会被注册到 Spring 容器中。可以方便地启用特定功能或配置。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfig.class)
public @interface EnableGracefulResponse {
}

SpringBoot主启动类,添加该注解开启功能:

image-20231214153605858

另一种方式META-INF/spring.factories

如果想引入该组件就直接开启所有功能,可以不用上述全局启动注解

直接在组件项目的 resources 中创建 META-INF 目录,并在此目录下创建一个 spring.factories 文件,将starter组件的自动配置类的类路径写在文件上即可。

resources
	META-INF
		spring.factories
// 直接将自动装配类全限定名放入该文件即可
com.feiniaojin.gracefulresponse.AutoConfig

Spring Boot项目启动时,会扫描外部引入的Jar中的META-INF/spring.factories文件,将文件中配置的类信息装配到Spring容器中)【用的是Java的SPI机制,还没研究后续再说吧】

这样只要引入该组件,组件功能也就集成进来了。【这种不够灵活,视情况用哪种方式】(建议:能用全局启动注解就用全局启动注解)

配置智能提示

组件中记得添加该依赖:

       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <version>${spring.boot.version}</version>
            <optional>true</optional>
        </dependency>

组件中添加上述依赖可以用于处理配置类(@ConfigurationProperties 注解标注的类)以及生成相关的元数据。

IDE 支持: 提供更好的集成开发环境(IDE)支持。通过生成的配置元数据,IDE 可以在配置文件中提供智能提示,我们可以更方便地编辑配置。

引入该组件,在修改配置类可以有智能提示

如下:

image-20231214114109561

该文章只介绍了该组件部分功能,有兴趣可以自行研究呀~

最后看一下组件的目录结构(学习一下):

image-20231214153359444

小结

自定义starter组件的三个核心步骤:

  1. 自定义注解
  2. 注解结合AOP实现逻辑
  3. 自动装配和可配置(配置类、自动装配类以及全局启动注解/spring.factories文件

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

电信网关配置管理系统后台 upload.php 文件上传漏洞复现

0x01 产品简介 中国电信集团有限公司(英文名称“China Telecom”、简称“中国电信”)成立于2000年9月,是中国特大型国有通信企业、上海世博会全球合作伙伴。 0x02 漏洞概述 电信网关配置管理系统后台 /manager/teletext/material/upload.php 接口存在文件上传漏洞,攻击者…

在IDEA中使用Git 、远程仓库克隆工程到本地

4.1 在IDEA中配置Git 安装好IntelliJ IDEA后&#xff0c;如果Git安装在默认路径下&#xff0c;那么idea会自动找到git的位置&#xff0c;如果更改了Git的安装位置则需要手动配置下Git的路径。 选择File→Settings打开设置窗口&#xff0c;找到Version Control下的git选项&…

分布式解决方案与实战

分布式多线程性能调优 使用多线程优化接口 //下单业务public Object order( long userId){long start System.currentTimeMillis();//方法的开始时间戳&#xff08;ms&#xff09;JSONObject orderInfo remoteService.createOrder(userId);Callable<JSONObject> calla…

IntelliJ IDEA2023学习教程

详细介绍idea开发工具及使用技巧 1. 2023版安装1.1删除老版本1.2 下载及安装 3.快捷技巧4. 创建各种model 1. 2023版安装 1.1删除老版本 如果以前装有idea需要先删除&#xff0c;以避免冲突&#xff0c;在idea安装目录/bin/Uninstall.exe双击1.2 下载及安装 最新版本 https:/…

SpringIOC之FilterType

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

实现进程间的通信

本例程是开发一款能实现进程通信的DLL。本例程以Visual Studio 2015为例。在Visual Studio 2013&#xff0c;Visual Studio 2017都是可以。 第一步&#xff1a;在Visual Studio 2015中&#xff0c;创建DLL工程。如何创建DL&#xff0c;在这里就不作具体说明了。百度都有许多创建…

【Python】—— 文本分析

文本分析 相关知识1. 文本数据处理2. 文本可视化3. Python编程4. 词频统计5. 人名提取6. 自然段划分7. 人物出现频率分布分析8. 词云生成 实验内容数据下载地址&#xff1a;1.对纯英文小说进行分析。2.对中文小说进行分析。 问题与解决附录1.对纯英文小说进行分析。2.对中文小说…

正则化实战( Lasso 套索回归,Ridge 岭回归)

Lasso 套索回归 导入包 import numpy as np from sklearn.linear_model import Lasso from sklearn.linear_model import SGDRegressor, LinearRegression原方程的计算结果 # 1. 创建数据集X&#xff0c;y X 2 * np.random.rand(100, 20) w np.random.rand(20, 1) b np.r…

PyGame字体详解

文章目录 字体初始化获取字体字体对象内置方法 字体初始化 在pygame程序中&#xff0c;第一步势必进行初始化&#xff0c;即调用pygame.init()函数&#xff0c;而此初始化过程&#xff0c;则顺便包含了字体初始化&#xff0c;即默不作声地调用了pygame.font.init()函数。下面通…

FreeModbus--学习函数指针

目录 函数指针 最简单的例子 稍作修改例子 引入协议栈的函数指针 引入协议栈第二处函数指针 函数指针 该协议栈中使用到函数指针&#xff0c;现开展一篇专门存放函数指针的文章。 C语言的函数指针是指向函数的指针变量&#xff0c;可以用来存储和调用函数的地址。在C语言中…

【亲测】获取百度智能云access_token并存储,百度智能云access_token有效期

百度智能云服务内置很多api接口&#xff08;文字识别&#xff0c;企业信息识别&#xff0c;等&#xff09;&#xff0c;所有百度智能云自带的接口都会用到百度的access_token 第一步&#xff1a;登录百度智能云管理中心 第二步&#xff1a;创建账户&#xff0c;完整身份认证 …

智能优化算法应用:基于乌燕鸥算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于乌燕鸥算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于乌燕鸥算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.乌燕鸥算法4.实验参数设定5.算法结果6.参考文…

jmeter判断’响应断言‘两个变量对象是否相等

1、首先需要设置变量&#xff0c;json、正则、csv文件等变量 2、然后在响应断言中 ①JMeter Variable Name to use —— 输入一个变量&#xff0c;变量名即可 ② 模式匹配规则 ——相等 ③测试模式 ——输入引用的变量命${变量名} &#xff08;注意这里是需要添加一个测试模式…

安卓开发显示加载中

private ProgressDialog loadobj; // 显示 ProgressDialog loadobj loadobj.show(MainActivity.this, "正在加载", "请稍后..."); // 取消 ProgressDialog loadobj.dismiss();或者 public ProgressDialog progressDialog;public void loading(){// …

流场寻路(Flow Field Path Finding)

简介 当场景中有成千上万个寻路游戏单位需要到达同一目标点时&#xff0c;通过常用的A*算法进行寻路不再是合适的选择&#xff0c;因为每个寻路游戏单位都需要依据自身所在的位置&#xff0c;根据算法获得一条从自身位置寻路到目标点的路径&#xff0c;n个游戏单位进行寻路就需…

人工智能在大型复杂机械产品装配状态检测自动化中的应用

尊敬的读者们&#xff0c;本文主要围绕“大型复杂机械产品装配状态检测自动化方案”开展讨论&#xff0c;从这个领域存在的问题和难度&#xff0c;以及基于人工智能、数字图像处理、机器人控制、装配机理等技术的自动化设计与实践方案。文章提出了数字化建模和智能识别模型构建…

[Springboot 源码系列] 浅析自动配置原理

文章目录 自动配置类原理AopAutoConfigurartion条件装配的底层原理ConditionalConditionalOnXxx 自动配置类原理 public class AutoConfApplication {public static void main(String[] args) {GenericApplicationContext context new GenericApplicationContext();context.re…

Git命令之本地分支与远程分支支关联/解除关联

目录 1.应用场景2.关联远程仓库中存在的分支3.解除本地分支与远程分支的关联 1.应用场景 在实际的工作生活中&#xff0c;往往需要将本地的分支和远程分支关联&#xff0c;这样我们就可以使用git pull命令来更新拉取最新的代码&#xff0c;并使用git push命令将自己本地的修改…

设备远程监控系统:实时对工厂设备状态进行监控

随着工业4.0时代的到来&#xff0c;工业物联网平台在工业领域的应用越来越广泛。设备远程监控系统作为工业物联网的重要组成部分&#xff0c;能够实时对工厂设备状态进行监控&#xff0c;提高生产效率和管理水平。本文将详细介绍工厂设备数据的采集方式以及设备远程监控系统的应…

企业寄件运费贵?企业如何将企业快递费用降至最低?

按照上述情况&#xff0c;如果使用闪侠惠递商家寄件服务&#xff0c;相较于原本的费用&#xff0c;会节省很大一部分的成本费用的&#xff0c;显而易见&#xff0c;这是非常便宜的。 闪侠惠递的寄件服务支持圆通、德邦、京东、顺丰等多家主流快递公司&#xff0c;并可以对比多…