【Spring Boot】Spring Boot 中的 Starter

news2024/11/27 0:16:34

Spring Boot 中的 Starter

  • 1.常用 Starter
  • 2.为什么要用 Starter
  • 3.Starter 有哪些要素

我们都知道,Spring 的功能非常强大,但也有些弊端。比如:我们需要手动去配置大量的参数,没有默认值,需要我们管理大量的 jar 包和它们的依赖。为了提升 Spring 项目的开发效率,简化一些配置,Spring 官方引入了 SpringBoot。当然,引入 SpringBoot 还有其他原因,在这里就不过多描述了。

而 Spring Boot 为了简化配置,提供了非常多的 Starter。它先打包好与常用模块相关的所有 JAR 包,并完成自动配置,然后组装成 Starter(如把 Web 相关的 Spring MVC、容器等打包好后组装成 spring-boot-starter-web)。这使得在开发业务代码时不需要过多关注框架的配置,只需关注业务逻辑即可。

1.常用 Starter

Spring Boot 提供了很多开箱即用的 Starter,大概有近 50 种,常见的如下表所示。

Starter
说明
spring-boot-starter-web用于构建 Web。包含 RESTful 风格框架、SpringMVC 和默认的嵌入式容器 Tomcat
spring-boot-starter-test用于测试
spring-boot-starter-data-jpa带有 Hibernate 的 Spring Data JPA
spring-boot-starter-jdbc传统的 JDBC。轻量级应用可以使用,学习成本低,但最好使用 JPA 或 MyBatis
spring-boot-starter-thymeleaf支持 Thymeleaf 模板
spring-boot-starter-mail支持 Java Mail、Spring Email 发送邮件
spring-boot-starter-integrationSpring 框架创建的一个 API,面向企业应用集成(EAI)
spring-boot-starter-mobileSpring MVC 的扩展,用来简化手机上的 Web 应用程序开发
spring-boot-starter-data-redis通过 Spring Data Redis、Redis Client 使用 Redis
spring-boot-starter-validationBean Validation 是一个数据验证的规范,Hibernate Validator 是一个数据验证框架
spring-boot-starter-websocket相对于非持久的协议 HTTP,Websocket 是一个持久化的协议
spring-boot-starter-web-servicesSOAP Web Services
spring-boot-starter-hateoas为服务添加 HATEOAS 功能
spring-boot-starter-security用 Spring Security 进行身份验证和授权
spring-boot-starter-data-rest用 Spring Data REST 公布简单的 REST 服务

如果想使用 Spring 的 JPA 操作数据库,则需要在项目中添加 spring-boot-starter-data-jpa 依赖,即在 pom.xml 文件中的 <dependencies> 和元素之间加入依赖,具体代如下:

<dependencies>
	...
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-jpa</artifactId>
	</dependency>
	...
</dependencies>

如果依赖项没有版本号,则 Spring Boot 会根据自己的版本号自动关联。如果需要特定的版本则需要加上 version 元素。

2.为什么要用 Starter

在 SpringBoot 还没有出来之前,我们使用 Spring 开发项目。如果程序需要连接数据库,我们一般会使用 Hibernate 或 Mybatis 等 ORM 框架,这里我以 Mybatis 为例,具体的操作步骤如下:

  • 到 Maven 仓库去找需要引入的 mybatisjar 包,选取合适的版本。
  • 到 Maven 仓库去找 mybatis-spring 整合的 jar 包,选取合适的版本。
  • 在 Spring 的 applicationContext.xml 文件中配置 dataSourcemybatis 相关信息。

当然有些朋友可能会指正,不是还需要引入数据库驱动包吗?

确实需要引入,但数据库驱动有很多,比如:mysqloraclesqlserver,这不属于 mybatis 的范畴,使用者可以根据项目的实际情况单独引入。

如果程序只是需要连接数据库这一个功能还好,按上面的步骤做基本可以满足需求。但是,连接数据库可能只是庞大的项目体系中一个环节,实际项目中往往更复杂,需要引入更多的功能,比如:连接 redis、连接 mongodb、使用 rocketmq、使用 excel 功能等等。

引入这些功能的话,需要再把上面的步骤再重复一次,工作量无形当中增加了不少,而且有很多重复的工作。

另外,还是有个问题,每次到要到 Maven 中找合适的版本,如果哪次找的 mybatis.jar 包和 mybatis-spring.jar 包版本不兼容,程序不是会出现问题?

SpringBoot 为了解决以上两个问题引入了 Starter 机制。

3.Starter 有哪些要素

我们首先一起看看 mybatis-spring-boot-starter.jar 是如何定义的。

在这里插入图片描述
可以看到它的 META-INF 目录下只包含了:

  • pom.protperties:配置 Maven 所需的项目 versiongroupIdartifactId
  • pom.xml:配置所依赖的 jar 包。
  • MANIFEST.MF:这个文件描述了该 jar 文件的很多信息。
  • spring.provides:配置所依赖的 artifactId,给 IDE 使用的,没有其他的作用。

注意一下,没有一行代码。

我们重点看一下 pom.xml,因为这个 jar 包里面除了这个没有啥重要的信息。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot</artifactId>
        <version>1.3.1</version>
    </parent>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <name>mybatis-spring-boot-starter</name>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
        </dependency>
    </dependencies>
</project>

从上面可以看出,pom.xml 文件中会引入一些 jar 包,其中除了引入 spring-boot-starter,之外重点看一下:mybatis-spring-boot-autoconfigure

我们找到 mybatis-spring-boot-autoconfigure.jar 文件,打开这个文件。

在这里插入图片描述

里面包含如下文件:

  • pom.properties:配置 Maven 所需的项目 versiongroupIdartifactId
  • pom.xml:配置所依赖的 jar
  • additional-spring-configuration-metadata.json:手动添加 IDE 提示功能
  • MANIFEST.MF:这个文件描述了该 jar 文件的很多信息
  • spring.factories:SPI(Service Provider Interface)会读取的文件
  • spring-configuration-metadata.json:系统自动生成的 IDE 提示功能
  • ConfigurationCustomizer:自定义 Configuration 回调接口
  • MybatisAutoConfigurationmybatis 配置类
  • MybatisPropertiesmybatis 属性类
  • SpringBootVFS:扫描嵌套的 jar 包中的类

spring-configuration-metadata.jsonadditional-spring-configuration-metadata.json 的功能差不多,我们在 applicationContext.properties 文件中输入 spring 时,会自动出现下面的配置信息可供选择,就是这个功能了。

在这里插入图片描述

这两个文件有什么区别?

如果 pom.xml 中引入了 spring-boot-configuration-processor 包,则会自动生成 spring-configuration-metadata.json

如果需要手动修改里面的元数据,则可以在 additional-spring-configuration-metadata.json 中编辑,最终两个文件中的元数据会合并到一起。

MybatisProperties 类是 属性实体类

@ConfigurationProperties(prefix = MybatisProperties.MYBATIS_PREFIX)
public class MybatisProperties {

    public static final String MYBATIS_PREFIX = "mybatis";
    private String configLocation;
    private String[] mapperLocations;
    private String typeAliasesPackage;
    private String typeHandlersPackage;
    private boolean checkConfigLocation = false;
    private ExecutorType executorType;
    private Properties configurationProperties;
    @NestedConfigurationProperty
    private Configuration configuration;

    public String getConfigLocation() {
        return this.configLocation;
    }

    public void setConfigLocation(String configLocation) {
        this.configLocation = configLocation;
    }

    @Deprecated
    public String getConfig() {
        return this.configLocation;
    }

    @Deprecated
    public void setConfig(String config) {
        this.configLocation = config;
    }

    public String[] getMapperLocations() {
        return this.mapperLocations;
    }

    public void setMapperLocations(String[] mapperLocations) {
        this.mapperLocations = mapperLocations;
    }

    public String getTypeHandlersPackage() {
        return this.typeHandlersPackage;
    }

    public void setTypeHandlersPackage(String typeHandlersPackage) {
        this.typeHandlersPackage = typeHandlersPackage;
    }

    public String getTypeAliasesPackage() {
        return this.typeAliasesPackage;
    }

    public void setTypeAliasesPackage(String typeAliasesPackage) {
        this.typeAliasesPackage = typeAliasesPackage;
    }

    public boolean isCheckConfigLocation() {
        return this.checkConfigLocation;
    }

    public void setCheckConfigLocation(boolean checkConfigLocation) {
        this.checkConfigLocation = checkConfigLocation;
    }

    public ExecutorType getExecutorType() {
        return this.executorType;
    }

    public void setExecutorType(ExecutorType executorType) {
        this.executorType = executorType;
    }

    public Properties getConfigurationProperties() {
        return configurationProperties;
    }

    public void setConfigurationProperties(Properties configurationProperties) {
        this.configurationProperties = configurationProperties;
    }

    public Configuration getConfiguration() {
        return configuration;
    }

    public void setConfiguration(Configuration configuration) {
        this.configuration = configuration;
    }

    public Resource[] resolveMapperLocations() {
        ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
        List<Resource> resources = new ArrayList<Resource>();
        if (this.mapperLocations != null) {
            for (String mapperLocation : this.mapperLocations) {
                try {
                    Resource[] mappers = resourceResolver.getResources(mapperLocation);
                    resources.addAll(Arrays.asList(mappers));
                } catch (IOException e) {
                    // ignore
                }
            }
        }
        return resources.toArray(new Resource[resources.size()]);
    }
}

可以看到 Mybatis 初始化所需要的很多属性都在这里,相当于一个 JavaBean。

下面重点看一下 MybatisAutoConfiguration 的代码:

@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnBean(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration {

    private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);
    private final MybatisProperties properties;
    private final Interceptor[] interceptors;
    private final ResourceLoader resourceLoader;
    private final DatabaseIdProvider databaseIdProvider;
    private final List<ConfigurationCustomizer> configurationCustomizers;
    public MybatisAutoConfiguration(MybatisProperties properties,
                                    ObjectProvider<Interceptor[]> interceptorsProvider,
                                    ResourceLoader resourceLoader,
                                    ObjectProvider<DatabaseIdProvider> databaseIdProvider,
                                    ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
        this.properties = properties;
        this.interceptors = interceptorsProvider.getIfAvailable();
        this.resourceLoader = resourceLoader;
        this.databaseIdProvider = databaseIdProvider.getIfAvailable();
        this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
    }

    @PostConstruct
    public void checkConfigFileExists() {
        if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {
            Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());
            Assert.state(resource.exists(), "Cannot find config location: " + resource
                    + " (please add config file or check your Mybatis configuration)");
        }
    }

    @Bean
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
        factory.setDataSource(dataSource);
        factory.setVfs(SpringBootVFS.class);
        if (StringUtils.hasText(this.properties.getConfigLocation())) {
            factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
        }
        Configuration configuration = this.properties.getConfiguration();
        if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
            configuration = new Configuration();
        }
        if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
            for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
                customizer.customize(configuration);
            }
        }
        factory.setConfiguration(configuration);
        if (this.properties.getConfigurationProperties() != null) {
            factory.setConfigurationProperties(this.properties.getConfigurationProperties());
        }
        if (!ObjectUtils.isEmpty(this.interceptors)) {
            factory.setPlugins(this.interceptors);
        }
        if (this.databaseIdProvider != null) {
            factory.setDatabaseIdProvider(this.databaseIdProvider);
        }
        if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
            factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
        }
        if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
            factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
        }
        if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
            factory.setMapperLocations(this.properties.resolveMapperLocations());
        }

        return factory.getObject();
    }

    @Bean
    @ConditionalOnMissingBean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        ExecutorType executorType = this.properties.getExecutorType();
        if (executorType != null) {
            return new SqlSessionTemplate(sqlSessionFactory, executorType);
        } else {
            return new SqlSessionTemplate(sqlSessionFactory);
        }
    }

    public static class AutoConfiguredMapperScannerRegistrar
            implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware {
        private BeanFactory beanFactory;
        private ResourceLoader resourceLoader;

        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

            ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
            try {
                if (this.resourceLoader != null) {
                    scanner.setResourceLoader(this.resourceLoader);
                }

                List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
                if (logger.isDebugEnabled()) {
                    for (String pkg : packages) {
                        logger.debug("Using auto-configuration base package '{}'", pkg);
                    }
                }

                scanner.setAnnotationClass(Mapper.class);
                scanner.registerFilters();
                scanner.doScan(StringUtils.toStringArray(packages));
            } catch (IllegalStateException ex) {
                logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", ex);
            }
        }

        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            this.beanFactory = beanFactory;
        }

        @Override
        public void setResourceLoader(ResourceLoader resourceLoader) {
            this.resourceLoader = resourceLoader;
        }
    }

    @org.springframework.context.annotation.Configuration
    @Import({ AutoConfiguredMapperScannerRegistrar.class })
    @ConditionalOnMissingBean(MapperFactoryBean.class)
    public static class MapperScannerRegistrarNotFoundConfiguration {

        @PostConstruct
        public void afterPropertiesSet() {
            logger.debug("No {} found.", MapperFactoryBean.class.getName());
        }
    }
}

这个类就是一个 Configuration(配置类),它里面定义很多 Bean,其中最重要的就是 SqlSessionFactory 的 Bean 实例,该实例是 Mybatis 的核心功能,用它创建 SqlSession,对数据库进行 CRUD 操作。

除此之外,MybatisAutoConfiguration 类还包含了:

  • @ConditionalOnClass 配置了只有包含 SqlSessionFactory.classSqlSessionFactoryBean.class,该配置类才生效。
  • @ConditionalOnBean 配置了只有包含 dataSource 实例时,该配置类才生效。
  • @EnableConfigurationProperties 该注解会自动填充 MybatisProperties 实例中的属性。
  • @AutoConfigureAfter 配置了该配置类在 DataSourceAutoConfiguration 类之后自动配置。

这些注解都是一些辅助功能,决定 Configuration 是否生效,当然这些注解不是必须的。

接下来,重点看看 spring.factories 文件有啥内容:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

里面只有一行配置,即 Key 为 EnableAutoConfiguration,Value 为 MybatisAutoConfiguration

好了,介绍了这么多东西,现在我们来总结一下,

Starter 几个要素如下图所示:

在这里插入图片描述
那么,编写 Starter 需要哪些步骤?

  • 需要定义一个名称为 xxx-spring-boot-starter 的空项目,里面不包含任何代码,可以有 pom.xmlpom.properties 文件。
  • pom.xml 文件中包含了名称为 xxx-spring-boot-autoconfigure 的项目。
  • xxx-spring-boot-autoconfigure 项目中包含了名称为 xxxAutoConfiguration 的类,该类可以定义一些 Bean 实例。当然,Configuration 类上可以打一些如:ConditionalOnClassConditionalOnBeanEnableConfigurationProperties 等注解。
  • 需要在 spring.factories 文件中增加 Key 为 EnableAutoConfiguration,Value 为 xxxAutoConfiguration

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

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

相关文章

SAP-CentralFinance - 学习心得2

过账总账中的交易 业务示例 创建大量日记账分录是会计日常工作的一部分。在SAP,会计可以使用不同的输入屏幕。使用所有方法,总账科目过账会自动列在损益表报表中(如果财务报表版本中包含科目)。查询已过账科目时还可显示对应的过账。 贵公司计划通过企业基金增加资本。在…

kafka安装配置及集成springboot

1. 安装 单机安装kafka Kafka对于zookeeper是强依赖&#xff0c;保存kafka相关的节点数据&#xff0c;所以安装Kafka之前必须先安装zookeeper dockerhub网址: https://hub.docker.com Docker安装zookeeper 下载镜像&#xff1a; docker pull zookeeper:3.4.14创建容器 doc…

《C++学习笔记---初阶篇6》---string类 上

目录 1. 为什么要学习string类 1.1 C语言中的字符串 2. 标准库中的string类 2.1 string类(了解) 2.2 string类的常用接口说明 2.2.1. string类对象的常见构造 2.2.2. string类对象的容量操作 2.2.3.再次探讨reserve与resize 2.2.4.string类对象的访问及遍历操作 2.2.5…

PPMP_char3

PMPP char3 – Multidimensional grids and data ​ 五一过后&#xff0c;有些工作要赶&#xff0c;抽出时间更新一下。这一章基本都熟练掌握&#xff0c;在做习题过程中有一些思考。这里涉及到了一点点GEMM&#xff08;矩阵乘&#xff09;&#xff0c;GEMM有太多可深挖的了&a…

Ubuntu24 文件目录结构——用户——权限 详解

目录 权限 用户 文件目录结构 一个目录可以有程序&#xff0c;目录&#xff0c;文件&#xff0c;以及这三者的链接。可以看到还分别有使用者和权限信息。 每个文件和目录都有与之关联的三个主要属性&#xff1a;所有者&#xff08;owner&#xff09;、组&#xff08;group&a…

【ESP32接入ATK-MO1218 GPS模块】

【ESP32接入ATK-MO1218 GPS模块】 1. 引言2. ATK-MO1218 GPS模块概述3. 接入ATK-MO1218 GPS模块的步骤4. 示例代码5. 结论1. 引言 在现代的嵌入式系统和物联网项目中,精确的位置信息是至关重要的。ATK-MO1218 GPS模块作为一款高性能的GPS/北斗双模定位模块,为开发者提供了强…

【Qt 学习笔记】Qt常用控件 | 容器类控件 | Tab Widget的使用及说明

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt常用控件 | 容器类控件 | Tab Widget的使用及说明 文章编号&#xf…

实现红黑树

目录 红黑树的概念 红黑树的节点结构定义 红黑树的插入 红黑树的验证 实现红黑树完整代码 红黑树的概念 红黑树 &#xff0c;是一种 二叉搜索树 &#xff0c;但 在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是 Red 或 Black 。 通过对 任何一条从根到叶子的…

You Only Cache Once:YOCO 基于Decoder-Decoder 的一个新的大语言模型架构

这是微软再5月刚刚发布的一篇论文提出了一种解码器-解码器架构YOCO&#xff0c;因为只缓存一次KV对&#xff0c;所以可以大量的节省内存。 以前的模型都是通过缓存先前计算的键/值向量&#xff0c;可以在当前生成步骤中重用它们。键值(KV)缓存避免了对每个词元再次编码的过程&…

基于SSM的“网约车用户服务平台”的设计与实现(源码+数据库+文档)

基于SSM的“网约车用户服务平台”的设计与实现&#xff08;源码数据库文档) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SSM 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统功能 首页 站内新闻浏览 打车信息查询功能 在线打车功能…

Linux 服务器配置共享文件夹(NFS)

一、准备三台 linux 服务器 三台服务器: manger:172.16.11.178 ap1:172.16.11.179 ap2:172.16.11.180 /root/serverfiles/ 为共享目录 二、配置步骤 1、在服务端01的机器上安装nfs和rpcbind程序 yum -y install nfs* yum -y install rpcbind* 2、在安装完nfs以及rpcb…

MySQL查询篇-聚合函数-窗口函数

文章目录 distinct 关键字聚合函数常见的聚合函数group by和having 分组过滤 窗口函数with as窗口聚合函数排名窗口函数值窗口函数 distinct 关键字 distinct 去重数据&#xff0c;ps:null值也会查出来 select distinct column from table;聚合函数 常见的聚合函数 select …

【保姆级教程】VMware Workstation Pro的虚拟机导入vritualbox详细教程

解决方案 1、OVF格式2、VMX格式 1、OVF格式 选定需要导出的虚拟机&#xff08;关闭或者挂起状态下&#xff09;依次选择文件-导出为ovf 在Vritualbox导入刚刚导出的.ovf文件 更改路径&#xff0c;按实际需要修改 成功导入 2、VMX格式 如果在VMware Workstation Pro导出的…

rs6(vmp)瑞某,药某局,商某局,专某局,维某网,cookie + 后缀 的分析解析

文章目录 说在前面rs vmp 特征 介绍解决方法算法补环境运行报错 代码联调补环境框架 补环境导出结果导出cookie导出后缀 效果展示 vx lyj_txd qq 1416279170 # 加我备注来意说在前面 免责声明&#xff1a; 本篇文章只做学习讨论&#xff0c;无商务用途&#xff0c; 未对目标…

APP反抓包 - 服务端证书验证

案例引入: app:泡泡聊天 版本:v1.7.4 发送登录请求,抓包发现提示:403 Forbidden 这里就是使用了服务端证书校验,因为charles没有安装证书,所以到达服务器的响应没有通过验证,返回异常。 美之图: 一,校验逻辑 在安卓开发时,在客户端预设证书(p12/bks),客户端…

C++基础与深度解析 | C++初探 | Hello World | 系统I/O | 控制流 | 结构体与自定义数据类型

文章目录 一、从Hello World谈起二、系统I/O三、控制流四、结构体与自定义数据类型 一、从Hello World谈起 #include <iostream>void fun(const char *pInfo) {std::cout << pInfo << std::endl; }int main() {fun("Hello World!");fun("Hel…

从 Oracle 到 TiDB,国有大行打造本地生活 APP 新体验

导读 本文介绍了某国有大行推出的本地生活服务类 APP 在数字时代的创新应用实践。该 APP 利用金融科技和互联网平台模式&#xff0c;打造“金融非金融”的线上生态服务平台&#xff0c;满足了用户多样化的生活需求。为应对用户增长和数据量增加带来的挑战&#xff0c;该 APP 决…

【网络编程】Servlet的前后端练习 | 表白墙 | 前后端交互 | 提交消息 | 获取消息

文章目录 一、Servlet的前后端练习1.表白墙服务器要实现的逻辑&#xff1a;1.获取消息 &#xff1a;2.提交消息&#xff1a;完整前端代码&#xff1a;完整后端代码&#xff1a; 一、Servlet的前后端练习 1.表白墙 服务器要实现的逻辑&#xff1a; 1.页面加载时&#xff0c;网…

47-Qt控件详解:Buttons Containers1

一 QPushButton (命令按钮) #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <QPushButton>//引入QPushButton类对应的头文件class MainWindow : public QMainWindow {Q_OBJECTpublic:MainWindow(QWidget *parent nullptr);~MainWind…

YOLOv8独家原创改进: AKConv(可改变核卷积)

1.AKConv原理介绍 地址:2311.11587 (arxiv.org) 摘要:基于卷积运算的神经网络在深度学习领域取得了令人瞩目的成果,但标准卷积运算存在两个固有的缺陷。一方面,卷积运算仅限于局部窗口,无法捕获其他位置的信息, 并且它的采样形状是固定的。 另一方面,卷积核的大小固定为…