{"id":13694796,"url":"https://github.com/liuyueyi/quick-fix","last_synced_at":"2025-06-24T23:05:27.332Z","repository":{"id":37172195,"uuid":"163576235","full_name":"liuyueyi/quick-fix","owner":"liuyueyi","description":"应用内存服务访问, 应用内数据访问订正工具","archived":false,"fork":false,"pushed_at":"2022-12-10T05:19:13.000Z","size":170,"stargazers_count":44,"open_issues_count":7,"forks_count":13,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-06-24T23:04:52.433Z","etag":null,"topics":["jar","java","ognl","ognl-expression","reflect","servlet","socket","spi","spring","springmvc"],"latest_commit_sha":null,"homepage":"http://liuyueyi.github.io/hexblog/","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/liuyueyi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-12-30T09:33:44.000Z","updated_at":"2025-06-11T07:37:53.000Z","dependencies_parsed_at":"2023-01-26T03:01:05.912Z","dependency_job_id":null,"html_url":"https://github.com/liuyueyi/quick-fix","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/liuyueyi/quick-fix","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liuyueyi%2Fquick-fix","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liuyueyi%2Fquick-fix/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liuyueyi%2Fquick-fix/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liuyueyi%2Fquick-fix/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/liuyueyi","download_url":"https://codeload.github.com/liuyueyi/quick-fix/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liuyueyi%2Fquick-fix/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261771109,"owners_count":23207217,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["jar","java","ognl","ognl-expression","reflect","servlet","socket","spi","spring","springmvc"],"created_at":"2024-08-02T17:01:42.617Z","updated_at":"2025-06-24T23:05:27.305Z","avatar_url":"https://github.com/liuyueyi.png","language":"Java","funding_links":[],"categories":["Java"],"sub_categories":[],"readme":"## Quick-Fix\n\n[![Builder](https://travis-ci.org/liuyueyi/quick-fix.svg?branch=master)](https://travis-ci.org/liuyueyi/quick-fix)\n[![mvn-repository](https://jitpack.io/v/liuyueyi/quick-fix.svg)](https://jitpack.io/#liuyueyi/quick-fix)\n[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/liuyueyi/quick-fix.svg)](http://isitmaintained.com/project/liuyueyi/quick-fix \"Average time to resolve an issue\")\n[![Percentage of issues still open](http://isitmaintained.com/badge/open/liuyueyi/quick-fix.svg)](http://isitmaintained.com/project/liuyueyi/quick-fix \"Percentage of issues still open\")\n\n## I. 背景说明\n\n### case1: 程序出bug了\n\n在我们的实际工作中，当我们遇到别人反馈代码出问题了吧，怎么返回的数据不对？\n\n当应用持续跑了一段时间之后，这个时候我们的第一个反应基本是确认能复现么？如果能复现，那么调用的姿势是不是对的？如果确认姿势没问题，那么就是请求参数不对了!!! 如果请求参数还没有问题，卧槽，这下完了，真可能有bug了，这下怎么办？\n\n接下来，一般的讨论是在测试环境复现一下，如果能复现，那么开启debug（或者远程debug），一行行调试，相信很快就能搞定了；\n\n但是，最怕的就是但是，测试环境没法复现，至于线上环境才有问题，这下怎么搞？\n\n### case2: 缓存数据有问题\n\n另外一个场景就是为了提升服务性能，缓存基本上会被大量的使用在各个系统之间；有缓存，那么就会有缓存不一致的问题，如果缓存用的是外部的如(redis/memcache)之类的，那么缓存数据的查询和订正，就相对简单了；但是，如果我们使用了内存作为数据的缓存，比如（hashmap, guava)，这种时候，我想知道这个内存中的数据怎么办？我想修改这个内存的中的数据怎么办？\n\n### 3. 小结\n\n上面两个场景，归纳一下主要是两个问题\n\n- 如何知道线上应用中，某个服务的方法的执行结果；\n- 如何知道线上应用中，某些内存数据的结果\n\n## II. 方案设计\n\n为了解决上面抛出的两个问题，我们要怎么做呢？\n\n### 1. 设计\n\n如何访问应用中的方法、数据，首先想到的就是反射；通过反射来执行某个实例的方法，或者获取实例的属性值，并没有太多的难度，有问题的是如何做到无侵入，如何与外部通信，如何做到通用\n\n首先我们需要注入一个EndPoint，用于实现应用于外界的通信，这个是一切开始的基本条件，Fixer的Endpoint负责接收外部请求，并将请求转发给内部的解析器，执行应用内服务访问，并将结果输出给外部使用者\n\n![IMAGE](http://blog.hhui.top/hexblog/imgs/190102/00.jpg)\n\n上图给出了EndPoint的结构设计，因为目前的java应用，直接以jar方式跑的不太多了，更常见的是将服务跑在其他的容器中，比如我们常见的tomcat应用，Spring应用等；不同的容器，对外暴露的方式不一样，怎么样才可以做到在不同的容器中，进行优雅的支持呢？\n\n接下来请求到应用内之后，首先定位到访问的服务，其次则进行服务调用执行，其实现流程如下\n\n![IMAGE](https://blog.hhui.top/hexblog/imgs/190102/01.jpg)\n\n### 2. 技术\n\n从上面的结构设计出发，找到这个项目实现的关键点，然后看下可以怎么实现\n\n#### a. 服务定位\n\n如何通过传入的请求参数来定位需要执行的服务方法，一般来将，应用中提供的服务可以分为两种情况\n\n- 以实例的形式提供服务\n  - 如Spring中以Bean的形式，一个Service就是一个Bean；我们可以借助Spring的ApplicationContext获取对应的bean\n  - 这种类型的服务，要求应用本身持有所有服务，然后我们可以通过这个ServiceHolder来定位具体的服务\n- 一个类对应一个服务\n  - 这种常见的是静态类或者单例，这个是以ClassLoader维度进行区分的；\n  - 因此我们可以直接通过ClassLoader方式来加载对应的类\n\n然后我们主要目标需要集中在第一种方式，不同的应用方式，获取ServiceHolder不一样，让我们自己去全部支持，显然是不太现实的，因此我们需要设计一个方案，让使用者，自己来根据应用中的ServiceHolder，来选择具体的Service方法\n\n这种，就可以通过SPI机制来支持\n\n#### b. EndPoint支持\n\n提供与外部的交互，最常见的方案就是暴露一个http接口，然后接收外部的请求；非web服务怎么办？也可以开一个socket走tcp通信，那么问题来了\n\n- 对于web服务\n  - 直接在已有的web服务上新增一个端点，并加上权限控制？\n  - 另开一个端口提供服务\n- 对于非web服务\n  - 新开端口提供服务\n  \n所以再具体的实现中，我们需要考虑以下几点\n\n- 如何复用已有的web服务？\n- 没有web服务时，自己怎么支持web服务？\n- 如何支持绑定端口的配置？\n- 当项目中引入了多种EndPoint支持方式时，怎么保证只有一个生效？\n\n针对上面的问题，具体实现时，会用到下面一些机制\n\n- 引入优先级\n- 通过SPI来实现自定义扩展\n- 解析JVM参数，来获取对应的配置\n\n## III. 相关博文\n\n从设计到实现，下面博文分别进行详细介绍说明\n\n**技术文档**\n\n- [190102-Quick-Fix 从0到1构建一个应用内服务/数据访问订正工具包](https://liuyueyi.github.io/hexblog/2019/01/02/190102-Quick-Fix-%E4%BB%8E0%E5%88%B01%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AA%E5%BA%94%E7%94%A8%E5%86%85%E6%9C%8D%E5%8A%A1-%E6%95%B0%E6%8D%AE%E8%AE%BF%E9%97%AE%E8%AE%A2%E6%AD%A3%E5%B7%A5%E5%85%B7%E5%8C%85/)\n- [190108-Quick-Fix 如何优雅的实现应用内外交互之接口设计篇](https://liuyueyi.github.io/hexblog/2019/01/08/190108-Quick-Fix-%E5%A6%82%E4%BD%95%E4%BC%98%E9%9B%85%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%BA%94%E7%94%A8%E5%86%85%E5%A4%96%E4%BA%A4%E4%BA%92%E4%B9%8B%E6%8E%A5%E5%8F%A3%E8%AE%BE%E8%AE%A1%E7%AF%87/)\n- [190311-Quick-Fix 通过反射执行任意类目标方法的实现全程实录（上篇）](https://liuyueyi.github.io/hexblog/2019/03/11/190311-Quick-Fix-%E9%80%9A%E8%BF%87%E5%8F%8D%E5%B0%84%E6%89%A7%E8%A1%8C%E4%BB%BB%E6%84%8F%E7%B1%BB%E7%9B%AE%E6%A0%87%E6%96%B9%E6%B3%95%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%85%A8%E7%A8%8B%E5%AE%9E%E5%BD%95%EF%BC%88%E4%B8%8A%E7%AF%87%EF%BC%89/)\n- [2019/03/15 190315-Quick-Fix 通过反射执行任意类目标方法的实现全程实录（中篇）](https://liuyueyi.github.io/hexblog/2019/03/15/190315-Quick-Fix-%E9%80%9A%E8%BF%87%E5%8F%8D%E5%B0%84%E6%89%A7%E8%A1%8C%E4%BB%BB%E6%84%8F%E7%B1%BB%E7%9B%AE%E6%A0%87%E6%96%B9%E6%B3%95%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%85%A8%E7%A8%8B%E5%AE%9E%E5%BD%95%EF%BC%88%E4%B8%AD%E7%AF%87%EF%BC%89/)\n- [2019/03/17 190317-Quick-Fix 通过反射执行任意类目标方法的实现全程实录（下篇）](https://liuyueyi.github.io/hexblog/2019/03/17/190317-Quick-Fix-%E9%80%9A%E8%BF%87%E5%8F%8D%E5%B0%84%E6%89%A7%E8%A1%8C%E4%BB%BB%E6%84%8F%E7%B1%BB%E7%9B%AE%E6%A0%87%E6%96%B9%E6%B3%95%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%85%A8%E7%A8%8B%E5%AE%9E%E5%BD%95%EF%BC%88%E4%B8%8B%E7%AF%87%EF%BC%89/)\n\n**使用文档**\n\n- [190104-Quick-Fix 纯Jar应用及扩展手册](https://liuyueyi.github.io/hexblog/2019/01/04/190104-Quick-Fix-%E7%BA%AFJar%E5%BA%94%E7%94%A8%E5%8F%8A%E6%89%A9%E5%B1%95%E6%89%8B%E5%86%8C/)\n\n## IV. 使用说明\n\n### 1. 依赖管理\n\n首先添加仓库，两种方式，一个是github的release版本的引入，优势是稳定；确定是更新及时问题；\n\n```xml\n\u003crepositories\u003e\n    \u003crepository\u003e\n        \u003cid\u003ejitpack.io\u003c/id\u003e\n        \u003curl\u003ehttps://jitpack.io\u003c/url\u003e\n    \u003c/repository\u003e\n\u003c/repositories\u003e\n```\n\n另一个是我个人的仓库\n\n```xml\n\u003crepositories\u003e\n    \u003crepository\u003e\n        \u003cid\u003eyihui-maven-repo\u003c/id\u003e\n        \u003curl\u003ehttps://raw.githubusercontent.com/liuyueyi/maven-repository/master/repository\u003c/url\u003e\n    \u003c/repository\u003e\n\u003c/repositories\u003e\n```\n\n### 2. 引入依赖包\n\n根据实际的应用场景，引入对应的依赖包，\n\n#### a. 纯jar应用\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.git.hui.fix\u003c/groupId\u003e\n    \u003cartifactId\u003efix-core\u003c/artifactId\u003e\n    \u003cversion\u003e1.4.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n注意事项：\n\n- fix-core 内置了一个http服务器，默认绑定端口号 9999, 可以通过jvm参数 `-Dquick.fix.port` 来覆盖\n- 在应用的入口出，需要主动执行 `FixEngine.instance();` 进行初始化\n- 因为fix-core 只提供了静态类的ServerLoader, 对于实例的加载，需要业务方自己来实现\n\n\n使用姿势如下：\n\n```bash\ncurl -X POST -H \"Content-Type:application/json\" http://127.0.0.1:9999/fixer/call -d '{\"service\": \"CalculateServer\", \"method\": \"getCache\", \"params\": [\"init\"], \"type\":\"static\"}'\n```\n\n实例demo:\n\n[jar-example](examples/jar-example)\n\n#### b. 纯Spring应用\n\n如我是一个纯Spring应用，没有使用SpringMVC，可以引入\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.git.hui.fix\u003c/groupId\u003e\n    \u003cartifactId\u003espring-fixer\u003c/artifactId\u003e\n    \u003cversion\u003e1.4.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n注意事项：\n\nspring-fixer提供了访问Spring容器内bean的服务方式，因此除了获取默认提供的静态类之外，还可以访问bean；\n\n- 使用默认的http服务器，端口号为 9999, 通过jvm参数 `-Dquick.fix.port` 来覆盖\n- 与前面不同，不需要主动调用`FixEngine.instance()`\n- 内部提供bean的加载ServerLoader，可以直接通过beanName或者Bean的完整类名访问其内部方法/数据\n\n使用姿势如下:\n\n\n```bash\n# 执行bean的某个方法\ncurl -X POST -H \"Content-Type:application/json\" http://127.0.0.1:8080/inject-fixer-endpoint/call -d '{\"service\": \"demoBean\", \"method\": \"randName\"}'\n# 查看bean的属性值\ncurl -X POST -H \"Content-Type:application/json\" http://127.0.0.1:8080/inject-fixer-endpoint/call -d '{\"service\": \"demoBean\", \"field\": \"name\"}'\n# 执行bean的属性的某个方法\ncurl -X POST -H \"Content-Type:application/json\" http://127.0.0.1:8080/inject-fixer-endpoint/call -d '{\"service\": \"demoBean\", \"field\": \"values\", \"method\":\"add\", \"params\": [\"autoInsertByQuickFixer!\"]}'\n\n# 测试静态类的静态成员的方法调用\ncurl -X POST -H \"Content-Type:application/json\" http://127.0.0.1:9999/fixer/call -d '{\"service\": \"com.git.hui.fix.example.spring.server.StaticBean\", \"method\": \"getCache\", \"params\": [\"init\"], \"type\":\"static\"}'\n\n# 单例类的使用姿势\ncurl -X POST -H \"Content-Type:application/json\" http://127.0.0.1:8080/inject-fixer-endpoint/call -d '{\"service\": \"SingletonBean\", \"method\": \"getInstance\", \"secondMethod\": \"sayHello\", \"secondParams\": [\"init\"], \"type\":\"static\"}'\n```\n\n实例demo:\n\n\n- [spring-example](examples/spring-example)\n\n#### c. SpringMVC应用\n\n如是一个SpringMVC应用，可以引入\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.git.hui.fix\u003c/groupId\u003e\n    \u003cartifactId\u003espring-mvc-fixer\u003c/artifactId\u003e\n    \u003cversion\u003e1.4.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n使用说明：\n\n- 利用mvc本身提供的http服务，访问路径为`/inject-fixer-endpoint/call`； 因此需要做好安全校验\n\n\n使用姿势\u0026实例：\n\n- 使用和前面的类似\n- [spring-mvc-example](examples/spring-mvc-example)\n\n\n#### d. SpringCloud应用\n\n如果是一个SpringCloud服务，且开启了 actuator 应用监测，可以引入\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.git.hui.fix\u003c/groupId\u003e\n    \u003cartifactId\u003espring-cloud-fixer\u003c/artifactId\u003e\n    \u003cversion\u003e1.4.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n使用说明：\n\n- 将FixEndPoint端口集成在SpringCloud的Actuator中，因此在实际使用时，需要在配置中开启，设置参数 `management.endpoints.web.exposure.include`\n- 访问路径为：`/actuator/inject-fixer-endpoint/call`， 前面的 `actuator`路径与应用监测配置的路径一致 \n\n\n使用姿势\u0026实例:\n\n- [spring-cloud-example](examples/spring-cloud-example)\n\n## V. End\n\n### 版本说明\n\n**v1.1**\n\n- 完成quick-fix 基础功能，实现应用内服务\\数据访问\n- 集成基于socket的http服务器，作为默认的应用内外交互通道\n- 支持Spring Jar 应用直接使用\n- 支持Spring MVC 应用直接使用\n- 支持Spring Cloud 应用直接使用\n\n**v1.2**\n\n- 支持ServerLoader优先级指定\n- 使用gson替换fastjson, 解决序列化对应的的key没有双引号导致json格式非法的问题\n\n**v1.3**\n\n- 新增单例反射调用支持；支持二级链式方法调用\n\n**v1.4**\n\n- [issues #4](https://github.com/liuyueyi/quick-fix/issues/4) 支持参数解析的扩展\n    - 默认提供八种基本数据类型，BigInteger, BigDecimal, Class, Enum, Json格式POJO对象的参数转换\n    - 通过JDK SPI方式，加载自定义的继承自`IArgParser`的参数解析器 \n- [issues #5](https://github.com/liuyueyi/quick-fix/issues/5) 解决传参为null的场景支持\n- 项目[spring-mvc-example](examples/spring-mvc-example) 新增枚举类传参的示例demo\n\n**v1.4.1**\n\n- 修复`HttpMessageParser`请求头在不同环境下可能大小写不一致的问题\n\n**v1.4.2**\n\n- 添加日志埋点\n\n \n### 其他\n\n拒绝单机，欢迎start或者加好友支持\n\n### 声明\n\n尽信书则不如，已上内容，一家之言，因个人能力有限，难免有疏漏和错误之处，如发现bug或者有更好的建议，欢迎批评指正，不吝感激\n\n- 微博地址: 小灰灰Blog\n- QQ： 一灰灰/3302797840； 交流群： 864706093\n- WeChat: 一灰/liuyueyi25\n\n### 扫描关注\n\n公众号\u0026博客\n\n![QrCode](https://gitee.com/liuyueyi/Source/raw/master/img/info/blogInfoV2.png)\n\n\n打赏码\n\n![pay](https://gitee.com/liuyueyi/Source/raw/master/img/pay/pay.png)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliuyueyi%2Fquick-fix","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fliuyueyi%2Fquick-fix","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliuyueyi%2Fquick-fix/lists"}