文档化你的 Apex 代码
ApexDoc 是一种标准化的注释格式,使人类、文档生成器和 AI Agent 更容易理解你的代码库。我们强烈推荐使用 ApexDoc 注释来促进代码协作并提高长期代码可维护性。基于 JavaDoc 标准,ApexDoc 提供了专为 Apex 和 Salesforce 生态系统定制的语法规范、专用标签和指南。注意:虽然 Apex 编译器强制执行现有注释语法,但不强制 ApexDoc 语法或检查注释准确性。
ApexDoc 基本注释格式
ApexDoc 注释通过起始分隔符与普通 Apex 注释区分:普通多行注释使用 /* */,ApexDoc 注释以 /** 开头,以 */ 结尾。ApexDoc 注释紧接在它文档化的类、接口、枚举、方法、构造函数或属性声明之前——注释块和被描述元素之间不能有其他代码或注释。
如果 ApexDoc 跨越多行,每个后续行以星号(*)开头。文档解析器忽略前导星号及其之前的空白:
/**
* 这是一个简单的 ApexDoc 注释。
*/
public with sharing class MyClass {
//...
}
主描述:ApexDoc 注释中的第一个文本块——没有显式标签。首先包含一个一句话摘要(用句号结尾——文档生成工具通常提取此句用于摘要表或索引),然后提供关于该元素的任何额外上下文(如前置/后置条件、链接到相关文档或描述变量约束)。
ApexDoc 块标签与内联标签
块标签用于 ApexDoc 注释的主描述之后。以 @ 符号开头后跟标签名。每个块标签出现在新行上,相关信息在标签名之后。关键块标签包括:
| 标签 | 适用元素 | 说明 |
|---|---|---|
| @author value | Class/Interface/Enum | 作者,允许多个 @author |
| @deprecated description | 所有 | 标记弃用,提供原因和替代方案 |
| @param paramName description | Method/Constructor | 参数说明——必须匹配参数顺序和名称 |
| @return description | Method | 返回值说明——void 方法和构造函数不使用 |
| @throws exceptionType desc | Method/Constructor | 文档化可能抛出的异常 |
| @see reference | 所有 | 添加"参见"引用——支持 @see class#member、@see "文本"、@see [label](URL) |
| @since value | 所有 | 版本或日期——对包作者特别有用 |
| @example example | 所有 | 使用示例(配合 {@code} 格式化为代码) |
| @version value | Class/Interface/Enum | 元素版本 |
内联标签用于主描述或块标签描述中。以 @ 开头但用花括号包围({@...}):
{@code text}—— 将文本格式化为内联代码{@link reference}—— 创建到另一个元素的内联链接(语法同 @see){@literal text}—— 不解释 HTML 标签,直接显示字面文本{@hidden text}—— 阻止元素出现在生成的文档中
文档化类与接口
类文档化:在摘要句中描述类的总体目的。解释共享模型选择的原因(如为什么使用 without sharing 执行特定特权操作)。推荐使用 @author、@version、@since、@see、@group 等标签。
/**
* 此服务类处理关键数据聚合任务。
* 使用 'without sharing' 以确保访问所有必要记录进行计算,
* 不受运行用户共享规则的影响。调用此类方法时需谨慎。
* @author Jane Doe
* @since 0.1.0
*/
public without sharing class DataAggregationService { }
接口文档化:关注契约而非实现。在主描述中文档化接口的整体目的和契约。对每个方法声明文档化其预期行为、参数和返回值——这为任何实现该接口的类设定期望。@author、@version、@since、@see 均适用。
文档化枚举、方法与构造函数
枚举文档化:文档化枚举的目的和所代表的概念集。如果枚举常量的名称不够自明,在枚举类型的 ApexDoc 主描述中说明,或直接在常量前的标准注释中说明。隐式方法(values()、valueOf()、name()、ordinal())通常不需要在每个枚举中显式文档化。
/**
* 一年中可能的季节
*/
public enum Season { WINTER, SPRING, SUMMER, FALL }
方法文档化:使用 @param 标签(每个参数对应一个,描述参数名、目的及类型/内容预期),@return 标签(指定返回值——包括 null 的条件或特定数据结构),@throws 标签(列出所有可能的 checked/unchecked 异常及触发条件——这对于识别错误处理缺口至关重要)。
/**
* 计算产品列表总价并应用折扣。
* @param productCodes 唯一产品代码列表——每个必须对应现有 Product2 记录
* @param discountPercentage 折扣百分比(如10.5为10.5%)——必须在0.0到100.0之间
* @return 折后总价Decimal——如productCodes为null或空列表返回0.0
* @throws InvalidArgumentException 如果discountPercentage超出范围
* @throws ProductNotFoundException 如果任何productCode不匹配现有产品
*/
public Decimal calculateTotalPrice(List<String> productCodes, Decimal discountPercentage) { }
构造函数文档化:与方法的指南相同——描述参数、异常以及构造过程的任何副作用或前置条件。
文档化属性、触发器和注解
属性/变量文档化:文档化构成类公共 API 的 public/global 属性和成员变量。说明属性目的、数据类型(如声明不明显)和重要使用注意事项(如是否只读、默认值)。使用 @see、@since、@deprecated 等标签。
/**
* 存储操作的最大重试次数。
* 如未显式设置,默认为3。
* @since 0.1.1
*/
public Integer maxRetries { get { return maxRetries ?? 3; } set { maxRetries = value; } }
触发器文档化:ApexDoc 没有触发器特定的注释规范——强烈建议将所有业务逻辑委托给单独的 handler 类或触发器框架。但可以使用标准标签如 @since、@see。
关键注解的文档化指南:
| 注解 | 文档化要点 |
|---|---|
| @AuraEnabled | 说明是否 cacheable=true、缓存设置的影响 |
| @InvocableMethod | 元素作为可调用动作的功能;label 和 description 属性 |
| @Future | 异步运行的影响——单独事务、Governor Limit、callout 行为 |
| @Deprecated | 必须包含@deprecated 标签:弃用原因和推荐替代方案 |
| @IsTest | 测试的场景/功能;seeAllData/onInstall 原因 |
| @TestVisible | 修改可见性的理由 |
| @RestResource | 描述整体资源及每个HTTP方法的角色 |
ApexDoc 完整示例
以下是一个包含类、属性、方法、异常及各类标签的完整 ApexDoc 示例:
/**
* 管理客户账户信息及相关操作。
* 此类通过'without sharing'绕过用户记录访问权限,以便在批处理类中使用。
* @author John Developer
* @since 0.1.0
* @version 0.3.1
* @see AccountProcessingBatch
* @group Account
*/
public without sharing class AccountManager {
/** 如未指定,新账户的默认区域。 */
public static final String DEFAULT_REGION = 'North America';
/** 存储此实例管理的活跃账户数量——使用{@link AccountService}后填充。 */
@TestVisible
private Integer activeAccountCount;
/**
* 创建具有给定名称和行业的新Account sObject。
* @param accountName 新账户的期望名称。不能为null或空。
* @param industry 新账户的行业分类。
* @return 新创建的Account sObject,ID已填充。
* @throws AccountManager.AccountException 如果accountName无效或DML操作失败。
*/
public Account createAccount(String accountName, String industry) {
if (String.isBlank(accountName))
throw new AccountException('Account name cannot be blank.');
Account acc = new Account(Name = accountName, Industry = industry);
try { insert acc; }
catch (DmlException e) { throw new AccountException('Failed to create account: ' + e.getMessage()); }
return acc;
}
/** 表示特定于AccountManager操作的异常。 */
public class AccountException extends Exception {}
}
地理编码服务完整示例(含 @deprecated + @AuraEnabled):
/**
* 提供地理定位和地址转换服务。
* @author Dennis Smith
* @version 0.3.0
* @since 0.1.0
*/
global with sharing class GeolocationService {
@AuraEnabled public Decimal latitude;
@AuraEnabled public Decimal longitude;
/** @deprecated 0.2.0 使用 {@link #geocodeAddress} 替代——更准确、更健壮。 */
@Deprecated
global static Coordinates convertAddressToCoordinates(String fullAddress) { /* ... */ }
/**
* 使用健壮的外部地理编码服务将结构化地址编码为精确坐标。
* @param street 街道地址("123 Main St")
* @param city 城市("Anytown")
* @param state 州/省缩写("CA")
* @param postalCode 邮政编码("90210")
* @param country 国家名称或代码("USA")
* @return 包含经纬度的Coordinates对象
* @throws GeocodingException 如果地址无法编码或外部服务不可用
* @since 0.2.0
*/
global static Coordinates geocodeAddress(String street, String city,
String state, String postalCode, String country) { return new Coordinates(0, 0); }
global class DeprecatedMethodCalledException extends Exception {}
global class GeocodingException extends Exception {}
}
@AuraEnabled 方法示例:
/**
* 检索给定账户的未关闭商机列表,可从Lightning Web Components访问。
* 如果在与组件交互期间商机集合可能变化,作者需使用{@code refreshApex()}。
* @param accountId 要检索商机的账户ID
* @return 未关闭Opportunity记录列表——如未找到或accountId无效则返回空列表
* @see OpportunitySelector
*/
@AuraEnabled(cacheable=true)
public static List<Opportunity> getOpenOpportunities(Id accountId) { /* ... */ }
ApexDoc 不仅是文档标准——它是创建可维护、可协作、可由 AI 理解的 Salesforce 代码库的关键实践。投入时间编写高质量的 ApexDoc 注释将为你和你的团队带来长期回报。