当前位置: 首页 > news >正文

编写SpringBoot的自定义starter包

starter项目

先来看一下Starter的官方解释: 

Spring Boot Starter 是一种方便的依赖管理方式,它封装了特定功能或技术栈的所有必要依赖项和配置,使得开发者可以快速地将这些功能集成到Spring Boot项目中。Spring Boot官方提供了一系列的Starters,涵盖了从Web开发到安全、数据访问等多个方面

其实Starter是一种SDK思想,基于SDK高度抽象快速构建功能块或技术块是当下企业技术部门实现技术资产快速开发的利器,基于Spring的starter自然就是最高效的方法。

starter项目结构分析

我们先来看一下mybatis-spring-boot-starter的项目结构

抛开mybatis的jdbc等逐步演化的核心功能依赖,只专注我们想要参观的starter部分可以看到,mybatis-spring-boot-starter被引入后是以两个jar的形式存在,即

  • mybatis-spring-boot-autoconfigure
  • mybatis-spring-boot-starter 

其中,mybatis-spring-boot-starter 项目中并无代码,仅仅一个pom文件指明依赖

<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>2.1.4</version></parent><artifactId>mybatis-spring-boot-starter</artifactId><name>mybatis-spring-boot-starter</name><properties><module.name>org.mybatis.spring.boot.starter</module.name></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><!--mybatis-starter的代码项目--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-autoconfigure</artifactId></dependency><!-- 支撑mybatis-starter的其他依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId></dependency></dependencies>
</project>

也就是说, mybatis-starter与SpringBoot对接的主要逻辑集中在mybatis-spring-boot-autoconfigure 中,mybatis-spring-boot-starter  仅仅是个壳子,它统一管理mybatis在适配SpringBoot中的资源依赖统一管理。

根据mybatis-starter以及其他标准的start开发、SpringBoot的官方资料,我们进行自定义一个starter的形式应该是以maven的多module形式创建一个项目,该项目下包含两个module,一个是XXX-spring-boot-starter,另一个是XXX-spring-boot-autoconfigure,且XXX-spring-boot-starter 仅仅是一个只包含pom依赖的的空白项目。

编写一个自定义的starter包的流程

在之前的SpringBoot中通过自定义注解使用AOP里,我们使用AOP实现了日志监听,这里还是以这个为例子,将它改造为一个开箱即用的starter。

第一步:创建module

根据SpringBoot的官方结构,创建module项目lognote-spring-boot

<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><groupId>org.example</groupId><artifactId>lognote-spring-boot</artifactId><version>1.0-SNAPSHOT</version><packaging>pom</packaging><modules><module>lognot-spring-boot-starter</module><module>lognote-spring-boot-autoconfgure</module></modules><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties></project>

lognote-spring-boot下分别包含两个module,作为公共对外的lognote-spring-boot-starter和与SpringBoot框架完成自动注入对接的lognote-spring-boot-autoconfigure 

 

第二步:管理依赖

根据上述分析mybatis的例子,对starter子module的pom进行设置,让其依赖autoconfigure,以下是lognote-spring-boot-starter的pom文件依赖

<dependencies><dependency><groupId>org.example</groupId><artifactId>lognote-spring-boot-autoconfgure</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies>

对autoconfigure项目进行所需依赖引入,引入对接SpringBoot的核心依赖autoconfigure和一些常用的依赖,以下是lognote-spring-boot-autoconfigure的pom文件 :

<dependencies><!-- starter包必须引入该依赖,完成对SpringBoot的适配 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId><version>2.3.6.RELEASE</version></dependency><!--其他相关依赖--><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.7.30</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.8</version></dependency></dependencies>

第三步:autoconfigure项目中增加配置文件

剩下的相关功能及相关配置代码均在lognote-spring-boot-autoconfigure项目中完成

使用新建基础配置启动类:

@Configuration
@ComponentScan(basePackages = "org.example.lognote.*")
public class ApplicationConfiguration {
}

配置spring.factories文件

在resources目录下新建META-INFO目录,新建spring.factories文件,文件中指定lognote-spring-boot-autoconfigure项目的启动类路径即可:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.example.lognote.ApplicationConfiguration

配置yml文件及相关配置内容(可选)

可根据组件情况是否进行yml相关配置,这里进行是否启用日志记录的开关配置,需要在yml中增加配置参数:

#yml文件下新增配置,默认为关闭
lognote:enable: false

当被其他SpringBoot项目引用时,可在其yml文件中进行配置即可。

创建参数的实体类

@ConfigurationProperties(prefix = "lognote")
@Data
@Configuration
public class LogNoteProperties {private Boolean enable = false;
}

 创建判定条件逻辑类,可基于配置内容,决定是否注入或者使用内部相关功能,(在下面具体代码中会使用该条件判定)

public class LogNoteCondition implements Condition {@Overridepublic boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {String able= conditionContext.getEnvironment().getProperty("lognote.enable");if(null!= able && Boolean.parseBoolean(able) ){return true;}return false;}

这里提供的是基于实现条件接口进行自定义代码的逻辑处理方式来判定条件,也可以使用:@ConditionalOnProperty等注解来实现这个诉求 

第四步:  封装功能编写代码

上述内容完成后,对于lognote-spring-boot-autoconfigure来说,直接进行相关代码的编写,就像在SpringBoot项目中一样,可以直接使用SpringBoot中的相关注解等信息,我们在starter中创建的bean以及一些对象,在starter被SpringBoot的项目引用后,会一并交由引用方的Spring上下文去管理。

 相关AOP及注解内容、逻辑细节可在 SpringBoot中通过自定义注解使用AOP中了解,这里仅仅放一下核心代码部分。

首先定义一个注解:

@Target(ElementType.METHOD) // 指定注解的适用范围
@Retention(RetentionPolicy.RUNTIME) //指定运行时
//根据条件确定该注解是否生效
@Conditional(LognoteCondition.class)
public @interface LogNote {//方法描述String desc() default "";//是否记录方法执行耗时boolean timeSpan() default true;
}

进行切面逻辑开发


@Component
@Aspect
@Slf4j(topic = "LogNote")
public class LogNoteAspect {@Around("@annotation(org.example.LogNote)")public Object aroundAdvice(ProceedingJoinPoint point) throws Throwable {MethodSignature signature = (MethodSignature) point.getSignature();//获取被调用方法Method method = signature.getMethod();//取出被调用方法的注解,方便后续使用注解中的属性ApiLog loglinstener = method.getAnnotation(ApiLog.class);log.info("----------------------method[{}]start--------------------",method.getName());log.info("方法描述:{}",loglinstener.desc());log.info("参数 :{}",point.getArgs());long startTime = System.currentTimeMillis();Object proceed = point.proceed();long endTime = System.currentTimeMillis();log.info("耗时:{}ss",endTime-startTime);log.info("----------------------method[{}] end--------------------\n",method.getName())return proceed;}
}

第五步:测试&打包

使用maven将整个lognote-spring-boot-starter项目进行打包,将该依赖引入到自己项目中,在yml中进行相关配置开启,然后进行使用:

<!-- 在相关项目中引入自定义的lognote-spring-boot-starter依赖 -->        
<dependency><groupId>org.example</groupId><artifactId>lognote-spring-boot-starter</artifactId><version>1.0-SNAPSHOT</version>
</dependency>

使用:

    @LogNote(desc = "执行Es查询")public JSONObject seachEsData(String indexName, SearchSourceBuilder searchSourceBuilder) {JSONObject resultMap = new JSONObject();.......return resultMap;}

运行效果:

2023-06-05 20:00:00 [LogNote] :--------------------method[searchESData]start---------------
2023-06-05 20:00:00 [LogNote] :方法描述:执行Es查询
2023-06-05 20:00:00 [LogNote] :参数    :{"query":{"match_all:{}","size":1,"from":0}},log
2023-06-05 20:00:00 [LogNote] :耗时    : 58ss
2023-06-05 20:00:00 [LogNote] :--------------------method[searchESData]  end---------------

补充:starter项目的规范结构

结合上面的例子,其实不难发现,starter的开发核心是与SpringBoot进行连接,包括Spring上下文的串通、SpringBoot的自动化配置串通等,其核心是在于lognote-spring-boot-autoconfigure,甚至直接进行lognote-spring-boot-configure开发,第三方直接引用lognote-spring-boot-configure都可以。亦或者只创建一个lognote-spring-boot-starter项目,把原本写在autoconfigure的代码和spring.factories配置放到里面都行,这样甚至更简便,那么官方为什么还要引导使用module的形式呢。此种方式可以参考这个项目

 其实官方的推荐是站在组件的角度去考虑,starter是以组件级为基础单位,所以采用module的方式进行开发具有更好的解耦性,可以明确层次的组件边界的概念。

使用该结构的优势主要有两点

组件开发管理规范化

使用module可以使组件的开发更加规范化,同一组件相关逻辑和内容可以集中在一个module中而不是四散的各个独立的项目。

另外,对于开发中的依赖管理也可以更加友好,例如原本在lognote-spring-boot-autoconfigure中的一堆依赖,目前仅仅是单独在autoconfigure中,如果再有新的组件扩展需要,新项目可能还要再额外进行引入依赖,这中间各个项目的版本管理以及组件本身的版本管理, 如果使用module集中管理的方式,组件相关的扩展以及核心代码都在XXX-spring-boot这个父项目中,各子包、组件所需的依赖也可以统一在一处管理,对于组件的版本、发布都是十分规范的。

例如上述例子中,可以将autoconfigure的通用依赖统一放到lognote-spring-boot这个父类的pom中统一引入管理

<modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>lognote-spring-boot</artifactId><version>1.0-SNAPSHOT</version><packaging>pom</packaging><modules><module>lognot-spring-boot-starter</module><module>lognote-spring-boot-autoconfgure</module></modules><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><!-- 公共依赖 --><dependencyManagement><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.7.30</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.8</version></dependency></dependencyManagement>

组件的扩展和演进更友好

XXX-spring-boot-starter作为整个组件的唯一出口,是对外交流的,所以只需要是“接口”、“封面"性质即可,autoconfigure是作为与SpringBoot的Spring上下文和自动化配置对接的一个衔接逻辑,本质也是在对接层,而核心的组件则可以以jar进行灵活开发。例如mybatis,它的核心jdbc以及mybtais相关逻辑组件,都可以独立进行开发,最后统一通过autoconfigure进行与SpringBoot进行对接即可兼容SpringBoot,日后再有其他框架,也可以在autoconfigure层进行适配。其核心部分 

相关文章:

编写SpringBoot的自定义starter包

starter项目 先来看一下Starter的官方解释&#xff1a; Spring Boot Starter 是一种方便的依赖管理方式&#xff0c;它封装了特定功能或技术栈的所有必要依赖项和配置&#xff0c;使得开发者可以快速地将这些功能集成到Spring Boot项目中。Spring Boot官方提供了一系列的Star…...

【LeetCode:3106. 满足距离约束且字典序最小的字符串 + 贪心】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…...

25 Python常用函数——reduce()

在 Python 3.x 中&#xff0c;reduce() 不是内置函数&#xff0c;而是放到了标准库 functools 中&#xff0c;需要先导入再使用。 标准库 functools 中的函数 reduce() 可以将一个接受两个参数的函数以迭代累积的方式从左到右依次作用到一个序列或迭代器对象的所有元素上&#…...

oracle登录报“ORA-27101: shared memory realm does not exist”

oracle登录报“ORA-27101: shared memory realm does not exist” 问题&#xff1a; 1、使用ip:1521/服务名方式连库报错" ORA-27101: shared memory realm does not exist Linux-x86_64 Error: 2: No such file or directory" 2、sqlplus XX/密码 可以登录数据库 …...

界面控件Telerik UI for WPF 2024 Q2亮点 - 全新的AIPrompt组件

Telerik UI for WPF拥有超过100个控件来创建美观、高性能的桌面应用程序&#xff0c;同时还能快速构建企业级办公WPF应用程序。UI for WPF支持MVVM、触摸等&#xff0c;创建的应用程序可靠且结构良好&#xff0c;非常容易维护&#xff0c;其直观的API将无缝地集成Visual Studio…...

IT服务运营过程中的资源要素管理(至简)

在IT服务运营管理过程中&#xff0c;所有资源要投入正式、连续、稳定运行&#xff0c;要保持规范化的管理和标准化的操作&#xff0c;具体包括工具管理、知识管理、服务台管理与评价、备件库管理等内容。 一、工具管理 1、工具的基本运营。见下表&#xff1a; 工具的基本运营…...

wodpress设置固定链接的方式和好处【SEO优化】

设置固定链接的好处 提高用户体验&#xff1a;固定链接使得网址更加直观和易于记忆&#xff0c;用户可以更容易地分享和访问文章。 优化SEO&#xff1a;搜索引擎更倾向于索引具有清晰结构的网址&#xff0c;固定链接有助于提高网站的SEO表现。 避免URL重复&#xff1a;固定链…...

【C#】 CancellationTokenSource 与Thread的启动、取消的区别?

1.Thread的使用 Thread的使用参考&#xff1a;【C#】Thread的使用 2.CancellationTokenSource 的使用 CancellationTokenSource在C#中用于取消长时间运行的操作&#xff0c;如异步或后台任务。它允许你从外部请求一个操作的取消&#xff0c;并且被取消的操作可以通过检查Ca…...

基于 HTML+ECharts 实现智慧运维数据可视化大屏(含源码)

智慧运维数据可视化大屏&#xff1a;基于 HTML 和 ECharts 的实现 在现代企业中&#xff0c;运维管理是确保系统稳定运行的关键环节。随着数据量的激增&#xff0c;如何高效地监控和分析运维数据成为了一个重要课题。本文将介绍如何利用 HTML 和 ECharts 实现一个智慧运维数据可…...

AIGC(Artificial Intelligence Generated Content)

随着人工智能技术的飞速发展&#xff0c;AIGC&#xff08;Artificial Intelligence Generated Content&#xff09;在各个领域的应用日益广泛&#xff0c;其中也包括前端开发的重要部分——CSS&#xff08;层叠样式表&#xff09;的优化。CSS作为网页设计中控制布局和样式的关键…...

02 MySQL数据库管理

目录 1.数据库的结构 sql语言主要由以下几部分组成 2. 数据库与表的创建和管理 1&#xff0c;创建数据库 2&#xff0c;创建表并添加数据 3&#xff0c;添加一条数据 4&#xff0c;查询数据 5&#xff0c;更新数据 6&#xff0c;删除数据 3.用户权限管理 1.创建用户 …...

C++编程: 使用 Nanomsg 进行 PUB-SUB 模式基准测试

文章目录 0. 引言1. Nanomsg简介1.1 可扩展性协议类型1.2 支持的传输机制1.3 NanoMsg 架构与实现 2. PUB-SUB 模式基准测试 0. 引言 Nanomsg 作为一款高性能的通信库&#xff0c;支持多种消息传递模式&#xff0c;其中包括 PUB-SUB&#xff08;发布-订阅&#xff09;。 本篇文…...

【Unity2D 2022:Data】读取csv格式文件的数据

一、创建csv文件 1. 打开Excel&#xff0c;创建xlsx格式文件 2. 编辑卡牌数据&#xff1a;这里共写了两类卡牌&#xff0c;第一类是灵物卡&#xff0c;具有编号、卡名、生命、攻击四个属性&#xff1b;第二类是法术卡&#xff0c;具有编号、卡名、效果三个属性。每类卡的第一…...

美团测开面经整理大汇总!!

大厂测开面经&#xff0c;加油加油&#xff0c;一周看一篇 美团测开面经美团测开暑期实习面经第二弹美团-地图服务部测开一面面经&#xff08;70min&#xff09;美团-优选事业部测开一面面经美团-优选事业部测开二面面经&#xff08;82min&#xff09;美团第一次测开笔试美团测…...

微信公众号获取用户openid(PHP版,snsapi_base模式)

微信公众号获取用户openid的接口有2个&#xff1a;snsapi_base、snsapi_userinfo 详情见微信公众号开发文档&#xff1a;https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html 本文介绍用PHP方式调用snsapi_base接口获取微信用户…...

DuckDB核心模块揭秘 | 第1期 | 向量化执行引擎之Pipeline

DuckDB核心模块揭秘 | 第1期 | 向量化执行引擎之Pipeline DuckDB是一款非常火的OLAP嵌入式数据库&#xff0c;性能超级棒。它分为多个组件&#xff1a;解析器、逻辑规划器、优化器、物理规划器、执行器以及事务和存储管理层。其中解析器原语PgSQL的解析器&#xff1b;逻辑规划器…...

Vue如何让用户通过a链接点击下载一个excel文档

在Vue中&#xff0c;通过<a>标签让用户点击下载Excel文档&#xff0c;通常需要确保服务器支持直接下载该文件&#xff0c;并且你有一个可以直接访问该文件的URL。以下是一些步骤和示例&#xff0c;展示如何在Vue应用中实现这一功能。 1. 服务器端支持 首先&#xff0c;…...

美摄科技企业级视频拍摄与编辑SDK解决方案

在数字化浪潮汹涌的今天&#xff0c;视频已成为企业传递信息、塑造品牌、连接用户不可或缺的强大媒介。为了帮助企业轻松驾驭这一视觉盛宴的制作过程&#xff0c;美摄科技凭借其在影视级非编技术领域的深厚积累&#xff0c;推出了面向企业的专业视频拍摄与编辑SDK解决方案&…...

MySQL:增删改查、临时表、授权相关示例

目录 概念 数据完整性 主键 数据类型 精确数字 近似数字 字符串 二进制字符串 日期和时间 MySQL常用语句示例 SQL结构化查询语言 显示所有数据库 显示所有表 查看指定表的结构 查询指定表的所有列 创建一个数据库 创建表和列 插入数据记录 查询数据记录 修…...

初识git工具~~上传代码到gitee仓库的方法

目录 1.背景~~其安装 2.gitee介绍 2.1新建仓库 2.2进行相关配置 3.拉取仓库 4.服务器操作 4.1克隆操作 4.2查看本地仓库 4.3代码拖到本地仓库 4.4关于git三板斧介绍 4.4.1add操作 4.4.2commit操作 4.4.3push操作 5.一些其他说明 5.1.ignore说明 5.2git log命令 …...

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

.Net框架,除了EF还有很多很多......

文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器

——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的​​一体化测试平台​​&#xff0c;覆盖应用全生命周期测试需求&#xff0c;主要提供五大核心能力&#xff1a; ​​测试类型​​​​检测目标​​​​关键指标​​功能体验基…...

DAY 47

三、通道注意力 3.1 通道注意力的定义 # 新增&#xff1a;通道注意力模块&#xff08;SE模块&#xff09; class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面

代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http…...

04-初识css

一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题

分区配置 (ptab.json) img 属性介绍&#xff1a; img 属性指定分区存放的 image 名称&#xff0c;指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件&#xff0c;则以 proj_name:binary_name 格式指定文件名&#xff0c; proj_name 为工程 名&…...

uniapp 开发ios, xcode 提交app store connect 和 testflight内测

uniapp 中配置 配置manifest 文档&#xff1a;manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号&#xff1a;4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...

[ACTF2020 新生赛]Include 1(php://filter伪协议)

题目 做法 启动靶机&#xff0c;点进去 点进去 查看URL&#xff0c;有 ?fileflag.php说明存在文件包含&#xff0c;原理是php://filter 协议 当它与包含函数结合时&#xff0c;php://filter流会被当作php文件执行。 用php://filter加编码&#xff0c;能让PHP把文件内容…...