《重构:改善既有代码的设计》读书笔记(持续更新中)

2023-11-01

背景(我也不知道这个算不算读书笔记,书本知识点整理和个人理解内容可跳至下面正文):

最近过年加找工作一直没想起来整理学习内容, 时间都很零碎。回想一下, 的确一直以来都是为了完成项目去看知识点。 除了刚开始想转行做IT的时候跟着慕课网的两条前后端路线系统地学习了一下, 但是终究囫囵吞枣。

前几天刚好有被问到《重构》这本书的内容。这两天临时抱佛脚看一下。 有了一定量的代码量积累的现在, 带着些许疑惑和朦胧的概念, 刚好到了看这本书的时候。

其实再上一家公司, 很早以前就被推荐这本书了, 不过不知道为什么后来忘了。 现在稍微看了下, 突然发现, 之前被要求我做到的一些代码习惯和公司内部的代码风格很多都能在书中找到。有些写代码的时候遇到的疑惑在书中也有提到(经常一边写新代码一边看之前的代码,尽量优化,奈何水平太菜, 经常迷茫于如何取舍,甚至最开始有段时间为了代码的简短和重复使用用力过头),感谢同事一直以来的帮助和容忍(我写的屎山和比蜗牛快不了多少的代码速度)。

在这个迷迷糊糊但是有点头绪的时候看这本书, 是最好的。

===========================================================================================

正文:

关于面向对象的五大原则:

  1. 单一职责: 一个类只负责一个职责。 目的在于解耦增强内聚。
  2. 开闭原则:对于扩展是开放的,对于修改是关闭的。可利用多态、抽象类等实现扩展,超类不应该做改变。
  3. 里氏替换原则:子类必能代替父类。
  4. 依赖倒转原则:高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象;抽象不应该依赖于具体实现,具体实现应该依赖于抽象
  5. 接口隔离原则:一个类对另一个类的依赖应该建立在最小的接口上,使用多个专门的接口比使用单一的总接口要好
  6. 迪米特原则(最小知识原则):一个对象应当对其他对象有尽可能少的了解。

所以, 在开发过程中, 我们的目标是:复用性、重构性、维护性。这本书很多地方都是以这三个目标为中心, 以求养成良好的代码风格和代码习惯。

----------------------------------------------------------------------------------------------------------

第一章:

这一章主要是用一个案例简单引出了一个重构的大概形象。

读这一章的时候我反思对比了一下以前开发过程中经历。如果有时间, 可以尝试下自己如何重构再对比下作者的做法

第一点我注意到的是, 重构需要以微小的步伐进行修改程序, 可以多次修改, 但是切忌不能一次性把一大段修改掉, 很容易出错。刚开始的时候我会犯这种错误, 心太急, 直接把整段代码拷贝出来, 有时候会把中间有些引入的参数或者被修改的参数(特别是Java传对象时引用传递,原对象会被不小心改掉)遗漏引发不必要的bug。所以在每一步的修改后都阶段性进行测试都是必要的。

所在在看到第一章的内容时如觉醍醐灌顶:在提取代码前, 首先我们必须 找出函数内局部变量和参数,未被修改的作为传入值, 被修改的作为返回值。 但是要注意的是被修改的参数尽量要少。

有一点之前我没想到过的是:只涉及某个类的方法, 写到该类下, 使用return返回该方法。 这个一般在第一遍写的时候就决定了代码放在哪个类, 但是在后期多次修改后的确会出现这个方法可能和其它类关系密切点的情况, 但是我不会去注意到需要移动他(也有一部分怕出错吧, 毕竟要是换了地方会引起其它地方拿不到, 越是到后期越是难以动这些代码, 涉及的东西太多, 修改非常容易出现遗漏, 工作量也很大)。

然后第一章的示例中还提到了变量的问题,感谢老大, 这方面之前没有问题,我们的代码中:

1. 引入参数以a开头,方法内变量名以类型首字母小写开头, 但是成员变量不加类型首字母, 自动生成get/set的方法时会出问题。

2. 类名采用名词,方法名采用动词加名词的格式,且不能以is/get/set开头,这些会和部分框架之类的冲突, 尽量避免。 类似增删改查固定功能的方法名会以特定单词和格式,所有代码统一。

3. 工具类的构造方法时静态的, 以防被实例化, 下面的方法采用静态方法。不同功能的工具类都归类整理,比如文件上传下载, world、excel文档处理放在一个工具类中,各类格式的转换(包括金额,英文时间处理)是一个工具类,文件和数据加解密是一个工具类, 这点符合书中说到的一个类只做一件事。 

4. 一些不便的参数等使用常量存储在单独的class中, 主要分两个class, 一个放配置方面的, 比如Ip地址等, 一个是放项目中用法到的, 比如不同交易方式对应的状态码。 统一采用大写, 使用下划线分割, 相同功能的多个参数开头部分有相同单词, 放在一起。

--------------------------------------------------------------------

第二章

重构含义:目的是不改变软件可观察行为的前提下,提高其可理解性,降低修改成本。

作者重复强调的是:重构之后软件功能一如既往, 其他程序员或用户是不会发现有些东西已经改变了。

开发时需要遵守的准则:

  • 添加新功能时,不应该修改既有代码,只管添加新功能并通过测试。
  • 重构时不再添加新功能,只管改进程序结构,并通过已有测试。

为何重构:

  • 重构改进软件设计(Design)
  • 重构使软件更容易理解(Maintain)
  • 重构帮助找到BUG(Debug)
  • 重构提高编程速度(Efficiency)

何时重构:

  • 三次法则:事不过三,三则重构
  • 添加功能时重构(New Feature)
  • 修补错误时重构(Bug Fix)
  • 复审代码时重构(Code Review)

何时不该重构:

  • 既有代码太混乱,且不能正常工作,需要重写而不是重构。
  • 项目接近最后期限时,应该避免重构。

重构的目标:

情况描述

目标

难以阅读的程序,难以修改

容易阅读

逻辑重复的程序,难以修改

所有逻辑都只在唯一地点指定

添加新行为时需要修改已有代码的程序,难以修改

新的改动不会危及现有行为

带复杂条件逻辑的程序,难以修改

尽可能简单表达条件逻辑

重构中的间接层:

间接层的作用(这里的几点达到的优化其实可以对应下一章的坏代码归纳):

允许逻辑共享(避免重复代码)
分开解释意图和实现(方法越短小,越容易起好名字揭示意图,单一职责)
隔离变化(软件需要根据需求的变化不断修改,隔离缩小修改的范围)
封装条件逻辑(多态消息)

正如作者所言, 间接层的使用需要注意层次多少处于恰当的范围。个人觉得之前有段时间有点过分追求避免重复代码而做不必要甚至负面的工作, 写完时候回发现不恰当的地方, 又改回去, 很浪费时间, 这大概就是我速度过慢的一部分原因。

-----------------------------------------------------------------------------------

第三章 代码的坏味道

这一章总结归纳了典型的需要重构的情况:

  1. Duplicated Code(重复代码):抽取方法到父类或者第三方类
  2. Long Method(过长函数):分解提炼函数, 以用途命名函数名
  3. Large Class(过大的类):遵循单一职责原则(SRP)。
  4. Long Parameter List(过长参数列表):常见的是将相关的参数组织成一个对象来替换掉这些参数。

这一点, 在之前的工作中, 比如前后端传输查询条件(一般我们的软件查询条件会很多)会采用类名是一个特定单词结尾的类的对象来存放。在后端方法中我可能会构造采用HashMap来存放, 如果有现成类可以用会放到一个对象中, 3个以下的就直接传多个参数。

  1. Divergent Change(发散式变化):因为不同的原因,在不同的方向上,修改同一个类。应该分解成更小的类,每个类只因一种原因而修改。

         这点回想下, 我的逻辑基本放在Service中了, SSM结构中Controller只做接受参数, 调用Service然后向前端返回, Mapper中只有sql语句, 很少涉及到逻辑, 有也只是一些简单的if或者日期之类格式转换。总共结构基本只有简单的这三部。

  1. Shotgun Surgery(霰弹式修改):每遇到变化,需要修改多个类,容易遗漏。把需要修改的部分放到一个类里。
  2. Feature Envy(依恋情结):函数大量地使用了另外类的数据。这种情况下最好将此函数移动到那个类中
  3. Data Clumps(数据泥团):两个类中相同的字段、函数签名中相同的参数等,都适合提取成一个单独的数据类。

             这点虽然看着简单易懂且容易入门, 但是在特定情况下可能迷惑性而且需要一定的技巧,在做的时候需要有条理, 详见第一章。

  1. Primitive Obsession(基本类型偏执):不要动不动就来个对象, 如果有一组经常被放在一起的数据, 可以让他们组成一个extract class ;如果在参数列中看到基本型数据, 尝试intoduce Parameter Object;如果发现自己正从数组中挑选数据, 可使用运用Replace Array with Object。
  2. Switch Statements(switch惊悚现身):少用switch,考虑用多态替换它。

这让我想到另一个, 有很多个简单if(根据条件返回一个值)连在一起的时候, 我会用HashMap, key为条件对应的状态值, value为结果。

  1. Parallel Inheritance(平行继承体系):如果发现每当为某个类增加一个子类,必须也为另外一个类相应增加一个子类, 这时需要考虑消除这种重复性。方法是让一个继承体系的实例引用另一个继承体系的实例。
  2. PLazy Class(冗赘类):如果一个类不值得存在,那么它就应该消失。
  3. Speculative Generality(夸夸其谈未来性):如果你的抽象类、委托、方法的参数没有实际的作用,那么就应当被移除掉。
  4. Temporary Field(令人迷惑的暂时字段):类中某个字段只为某些特殊情况而设置。把这些变量提炼到一个独立的类中。提炼后的新对象是个函数对象
  5. Message Chains(过度耦合的消息链):常常是因为数据结构的层次很深,需要层层调用getter获取内层数据。可以考虑这个字段是否应该移到较外层的类,或者把调用链封装在较外层类的方法。
  6. Middle Man(中间人):如果一个类的很多功能都通过委托给其他类来完成,那么就不如去掉这些中间人直接和真正负责的对象打交道。
  7. Inappropriate Intimacy(两个类过度亲密):
  8. Alternative Classes(异曲同工的类):根据用途重新命名,将某些行为移入类,或者使用超类类。
  9. Incomplete Lib Class(不完美的类库):修改类库的一两个函数 - 引入外部函数(Introduce Foreign Method);添加一大堆额外行为 - 添加本地扩展(Introduce Local Extension)
  10. Data Class(纯稚的数据类):数据类不应该把全部字段单纯的通过getter/setter暴露出来(我们在多层结构系统开发时经常这么做),而应该暴露抽象接口,封装内部结构。
  11. Refused Bequest(被拒绝的遗赠):a)子类继承父类的所有函数和数据,子类只挑选几样来使用:为子类新建一个兄弟类,再运用下移方法(Push Down Method)和下移字段(Push Down Field)把用不到的函数下推个兄弟类。b)子类只复用了父类的行为,却不想支持父类的接口:运用委托替代继承(Replace Inheritance with Delegation)来达到目的。
  12. Comments(过多的注释):适当注释, 不要太多, 代码中很明显的就不要注释了。但也不要一点都没有。

以上内容很多, 文中只有总结, 没有案例, 这个需要在不断加大代码阅读量和思考以及动手实践中不断去体会。

-------------------------------------------------------------------------

第四章 建立测试体系

// 之后再看, 之前测试只是简单地每个小功能模块完成后测试, 再拼装后整个流程测试。

----------------------------------------------------------------------------

接下来的章节都是在详细说明各类重构方法

第五章 重构列表

参数按照列表形式传递,检测重构每一个节点。

这一章, 作者先介绍了重构的记录格式:名称-->概要-->动机-->做法-->范例

工作中用Eclipse, 在开发过程中比较方便, 这一点可以得到控制,但是书中提到编译器可能存在的缺点, 总结如下:

1. 被删除的部分在继承体系中声明不止一次,那么编译器会被迷惑。

     这点不是很理解。 我想的可能有点问题:以前在子类重写父类方法的时候没有加@override注解, 父类删除方法后, 子类没删除,eclipse是不会提示的。当然那时候让我意识到@override注解还是需要写的。

2. 编译器查找会拖慢速度, 这点没有明显感觉到, 可能我还没到境界, 或者时代不同, 工具不同。

3. 编译器无法找到通过反射机制而得到的引用点。 实际项目没遇到过orz

作者推荐的是结合菜单选项或者文本查找方式。

----------------------------------------------------------------------

第六章 重新组织函数

函数不应过长, 传递的参数尽可能少而完整。 不要把传入的参数修改后返回。

1. Extract Method(提炼函数)

   把过长的函数中一部分提取出来, 写成一个单独的方法, 这个比较容易理解。

  但是需要注意的是:长度不是问题, 关键在于函数名称和函数本体之间的语义距离。个人理解是需要让每个方法所做的事都属于它名称所表示的内容。

  在提取过程中, 仅在提取方法中使用的临时变量和原方法中需要用到的局部变量的定义位置需要注意, 前者, 需要从原方法迁移进来; 后者需要作为传入参数,根据在此之后, 改参数是够被其它地方使用决定是否再作为返回值。

   具体做法见原文

2. Inline Method(内联函数)

动机:

  1) 对于内部代码和函数名同样清晰易读的函数, 可以把它去掉, 直接使用其中的代码。

  2) 假如手中有一群组织不合理的函数, 可以先把他们内联到一个大型函数中, 然后再从中提炼出组织合理的小函数。

  3) 面对过多的间接层, 可以使用内联手法, 去除多余间接层。

需要注意: 找出所有这个函数的被调用点, 对于具有多态,递归调用, 多返回点, 内联至另一个对象而该对象并无提供访问点, 则尽量不要使用该重构手法。

3. Inline Temp(内联临时变量)

动机:某个临时变量被赋予某个函数调用的返回值, 且妨碍其它重构则可以将它内联化。
书中提到了一个小窍门:如果这个变量未被声明为final, 则将它声明为final, ide如果报错, 从而判断该值在后面是否被改变过, 如果有过改变, 就不太适合将其内联化。

书中还提到, 在修改完所有引用点后再删除该变量的声明和赋值语句, 在实际开发过程中, 有时候我会由于之前设计不合理会在之后造成需要内联化得变量的产生, 但是我都是直接先删除, 然后按照eclipse的错误提示去第一遍查找引用点的, 所以在想是不是应该按作者这么来, 然后在删除之后再通过eclipse是否有错误提示来判断是否还有疏漏。

有时候也有疑惑, 这方面讲, IDE是让我们过于依赖它了还是简化了开发过程

4. Replace Temp with Query

以查询取代临时变量

个人感觉就是当某个临时变量的获取在多出会被用到时,且该临时变量只被赋值一次, 且这个赋值过程不受其它条件影响,  可以把获取这个临时变量的过程单独提炼出来作为一个方法。

这样的好处在于一方面可以提高代码的复用率, 在以后需要修改时只要修改一个地方;且临时变量的减少可以使当前函数的优化变得简单。

步骤: 因为需要缺点该临时变量只被赋值一次, 所以可以先给这个变量设置为final, 如果未报错, 即可进行下面步骤。

5. Introduce Explaining Variable

将较长或者复杂的条件子句提炼出来, 赋值给一个临时命名清晰的变量。

这一条前段时间在阿里巴巴Java开发手册上刚好看到过。

本书作者相对这个优化方法,更加倾向于上一条的 单独提炼成一个private函数, 如果这个private函数被多次调用, 则改为project或者public。但是假如, 提炼到外部函数时会传递很多参数的时候, 则使用这一条。

6. Splite Temporary  Variable

 分解临时变量

对于被多次赋值, 且赋值后含义不同的临时变量, 应该按照其含义拆分成多个不同的临时变量。

看到这点, 好像有点明白了自己为什么每次写代码总有种手忙脚乱的感觉23333

修改过程: 在每个临时变量赋值处对临时变量进行重命名, 并修改下次赋值之前的引用点, 在这里我们依旧可以使用final和编译器来判断下个赋值点在哪里。

7. Remove Assignments to Parameters

移除对参数的赋值

感觉特别是写一些公共方法的时候, 如果把别人入参改了, 会一不小心给人一个surprise, 而且让代码可读性变差。 以前自己写的时候感觉改变入参很奇怪, 后来做了几个项目后, emmm. 现在也是习惯对一些值传递的进来参数, 如果要修改, 都会先给个临时变量, 传对象进来的时候就需要特别注意点

这里作者提到了, Java传递对象的时候指向的是该对象的引用, 所以把对象作为参数传递的时候, 需要特别注意.

有时候会有传一个对象进去, 然后修改对象中的变量来操作. 这种在实际中也是常有的, 需要区别对待. 但是直接使变量指向性的对象还是不要做的好.

题外话, 这个让我想到了“面试题整理”之类的内容出镜率很高的题:Java的包装类, 特别是String和Integer,常量池让他们在不同情况下表现不一样。 但是我在日常中, 还是把他们直接当对象来用, 不怎么考虑常量池的情况, 以免出错。 可能是我的代码量不大, 做的项目都比较简单。

8. Replace Method With Method Object

以函数对象取代函数.

当我们的某个函数比较巨大的时候, 我们会把这个函数按更加细致的功能进行拆解, 但是假如在拆解过程中我们发现这些小函数之间会有大量共用的变量, 这时候把这些小函数继续放在原先的类中就会看上去比较混乱(要么会有大量入参, 要么有大量浪费的填装Map, 要么把这些共用变量混在原类的成员变量中把水搅混). 这时候就可以考虑将这个大函数放到一个单独的类里面, 然后再拆解.

操作中需要注意的是:

1. 入参的赋值是在新类的构造方法上, 如果需要灵活配置可以考虑Builder的方式

2. 新类中又一个private final类型的变量, 用来存放源对象, 这个对象也会在构造方法中作为入参被传入并赋值. 这样就可以在新类中方便使用源对象中的值了.

3. 计算以及返回在新类的compute方法中.(名字使用约定俗成的compute以便其他人阅读和使用)

9.  Substitute Algorithm(替换算法)

适当重构一些方法.

历史遗留代码, 如果写得很"迷", 看情况, 少动为妙

----------------------------------------------------------------------

第七章 在对象之间搬移特性

类变得臃肿的原因很多时候往往是由于承担过多的责任. 所以当我们发现类过于臃肿的时候就需要考虑将不属于这个类的职责的内容迁移出去. 防止出现一个类几千行代码的情况出现.

7.1 Move Method(搬迁函数)

判断依据:

某个函数与其所在类之外的另一个类有更多交流(调用或者被调用)

执行:

在目标类中建立一个类似功能的方法, 将源类中的该方法变为单纯的委托函数或者直接将其移除.

步骤依旧是按照 先在目标类中放入新方法并确保能正常运行后再修改源类中的方法或者删除.

在迁移过程中也需要注意灵活, 可以对方法进行适当再次拆分, 部分进行迁移.

 

7.2 MoveField(搬移字段)

某个字段与其所在类之外的另一个类有更多交流(调用或者被调用)

在这一情况下, 可以考虑迁移字段或者字段的使用者(方法)

7.3 (ExtractE Class)提炼类

使用7.1和7.2对臃肿的类进行分解. 

需要注意的是, 新类以及新类的方法是否需要公开

这一过程中也存在一定的危险性, 事务问题. 这在后面会讲到

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

《重构:改善既有代码的设计》读书笔记(持续更新中) 的相关文章

  • 使用 objectGUID 进行查询 - Spring LDAP 模板

    我正在尝试获取 存储并依次使用 objectGUID 来查询 Active Directory 为了获取用户属性我正在使用以下 public static class MyDnKeyValueAttMapper implements Att
  • 使用 xuggle 将 mp3 转换为 wav 出现异常

    我正在尝试将 mp3 转换为 wav 代码在这里 String mp3 F work pic2talk38512 mp3 String wav F work pic2talk38512 wav TranscodeAudioAndVideo
  • 从 Bitmap 类创建 .bmp 图像文件

    我创建了一个使用套接字的应用程序 客户端在其中接收图像并将图像数据存储在 Bitmap 类中 谁能告诉我如何创建一个名为我的图像 png or 我的图像 bmp来自此 Bitmap 对象 String base64Code dataInpu
  • Java,将 null 分配给对象和仅声明之间有什么区别

    之间有什么区别 Object o null and Object o 仅声明 有人可以回答我吗 这取决于您声明变量的范围 例如 局部变量没有default values在这种情况下你将不得不分配null手动 在这种情况下实例变量分配 nul
  • Eclipse 自动完成更改变量名称

    只是一个愚蠢的问题 但很难搜索 因为有很多关于 Eclipse 自动完成的主题 而且很难找到与我的问题匹配的内容 所以问题是 如果我写 MyClass MyVarName 然后按空格键 添加 new MyClass Eclipse 自动添加
  • 使用 java 的 RAR 档案 [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • Java 9 中可以使用提前编译吗?

    As per JEP 295 http openjdk java net jeps 295 任何 JDK 模块 类或用户代码的 AOT 编译都是实验性的 JDK 9 中不支持 要使用 AOT 化的 java base 模块 用户必须编译该模
  • 在 Java 中填充布尔数组

    作为一名相当新手的 Java 程序员 我给自己设定了一个艰巨的挑战 尝试编写一个简单的文本冒险 不出所料 我已经遇到了困难 我试图为我的 Location 类提供一个属性来存储它包含的退出 我为此使用了一个布尔数组 本质上保存代表每个出口的
  • 获取TextView的高度

    我有一些文字将被放入TextView 我这样做是使用setText 现在我需要找到文本在文本中占据的行数或高度TextView 我尝试使用getHeight 但它总是返回 0 无论如何 有没有办法获得文本中存在的文本的高度TextView
  • 适用于 Solaris 的 Java 8 中缺少 javaws

    看起来 Oracle 从 Java 8 for Solaris 中删除了 Java Web Start javaws 在 Java 8u51 中不再可用 来自兼容性指南 http www oracle com technetwork jav
  • Jodatime 日期格式

    是否可以格式化 JodaTime 日期 这是代码 private static LocalDate priorDay LocalDate date1 do date1 date1 plusDays 1 while date1 getDayO
  • Spring 术语中命令、表单、业务和实体对象之间的区别?

    我试图理解这些对象在松散耦合系统方面的差异 业务对象与实体对象相同吗 我可以使用 MVC 中的业务或实体对象作为我的命令对象吗 命令对象与表单对象相同吗 只是寻找 Spring 术语和用法中对象类型的说明 我在 stackoverflow
  • Java 套接字:可以从一个线程发送并在另一个线程上接收吗?

    这可能是一个非常基本的问题 但我很难找到答案 让一个线程写入 Socket 的输出流 而另一个线程从 Socket 的输入流读取数据 这样可以吗 编辑 这是一个与外部服务器通信的客户端应用程序 我并不是想让两个线程互相交谈 很抱歉含糊不清
  • 在java中是否可以使用反射创建没有无参数构造函数的“空白”类实例?

    我有一个没有默认构造函数的类 我需要一种方法来获取此类的 空白 实例 空白 意味着实例化后所有类字段都应具有默认值 如 null 0 等 我问这个问题是因为我需要能够序列化 反序列化大对象树 而且我无法访问该对象类的源 并且类既没有默认构造
  • 我们必须将 .class 文件放在 Tomcat 目录中的位置

    我必须把我的 class文件在 Tomcat 目录中 在我的 Java Complete Reference 书中 他们告诉将其放入C Program Files Apache Tomcat 4 0 webapps examples WEB
  • activemq 的优先级

    我们目前正在使用 JMS 和 activemq 5 5 1 开发一个应用程序 我们想为某些消息定义更高的优先级 这将使它们首先被消耗 设置生产者和消费者后 通过spring 3 1 JMSTemplate 优先级并不能完全发挥作用 事实上
  • 如何告诉 cxf 将包装类型保留在方法中?

    在我的 WSDL 中我有一个操作
  • 如何告诉 IntelliJ 使用 Java 1.6 JDK 启动 gradle?

    一个简单的问题 即使经过几个小时的尝试和搜索 我也无法弄清楚 我安装了 Java 6 和 7 如何告诉 IntelliJ 使用 JDK 版本 1 6 启动 Gradle 构建 无论我做什么 IntelliJ 都会以以下方式开始我的 grad
  • Checkstyle - 方法按修饰符排序

    是否可以添加到 checkstyle 规则以按修饰符对类中的方法进行排序 我的意思是开头的公共方法和最后的私有方法 MethodsOrderCheck做这个工作 检查文档 https www qulice com qulice checks
  • 如何修改生成的SOAP请求?

    我正处于创建输出拦截器并从 SOAP 消息中获取 OuputStream 的阶段 但是 如何在将 SOAP 信封发送到端点之前对其进行修改呢 我想删除一些 xml 元素 一种方法是获取文档并通过 XSLT 转换运行它 您可以通过调用来获取拦

随机推荐