开发者指南:精通 Salesforce Apex 类
背景与应用场景
作为一名 Salesforce 开发人员,Apex 是我们工具箱中最核心、最强大的工具。而 Apex 的基础就是 Apex Classes(Apex 类)。简单来说,一个 Apex 类就是一个蓝图或模板,用于创建对象。它封装了数据(以变量或属性的形式)和行为(以方法的形式),是 Salesforce 平台上实现复杂业务逻辑、自定义功能和与外部系统集成的基石。
在 Salesforce 中,标准功能如工作流规则、验证规则和 Flow 虽然强大,但总有其局限性。当我们面临以下场景时,Apex 类就成了不可或预的解决方案:
- 复杂的业务逻辑实现: 当业务需求超出标准公式字段或流程构建器的能力范围时,例如,需要基于多个相关对象的复杂计算来更新某个字段,或者实现一个精细的定价引擎。
- 触发器逻辑模块化: 为了避免在 Trigger(触发器)中编写大量代码,最佳实践是将逻辑委托给 Apex 类(通常称为 Trigger Handler Class)。这使得代码更易于管理、测试和重用。
- 自定义用户界面后端支持: 为 Visualforce 页面、Aura Components 或 Lightning Web Components (LWC) 提供后端控制器 (Controller),处理用户输入、查询数据并执行业务操作。
- 自定义 Web 服务: 通过创建 Apex 类,我们可以将其暴露为 REST 或 SOAP API 端点,允许外部系统安全地与 Salesforce 数据进行交互。
- 批量数据处理: 使用实现了
Database.Batchable
接口的 Apex 类,我们可以异步处理数百万条记录,而不会超出 Governor Limits(执行限制)。 - 定时任务: 通过实现
Schedulable
接口,我们可以编写 Apex 类来安排任务在特定时间或按固定周期自动执行,例如每日数据清理或生成报告。
理解并精通 Apex 类的结构、语法和最佳实践,是每一位 Salesforce 开发人员从入门到精通的必经之路。
原理说明
Apex 是一种强类型的、面向对象的编程语言,其语法与 Java 非常相似。一个 Apex 类的核心结构由定义、成员变量、构造函数和方法组成。
类的定义
一个典型的 Apex 类定义语法如下:
[访问修饰符] [共享关键字] class 类名 [implements 接口名] [extends 父类名] { ... }
- Access Modifiers (访问修饰符): 决定了类的可见性。
private
: 仅在当前内部类或代码块中可见(默认)。public
: 在您的应用程序或命名空间内任何地方都可见。global
: 对所有 Apex 代码都可见,无论其命名空间如何。常用于构建受管包 (Managed Package) 或对外暴露的 API。
- Sharing Keywords (共享关键字): 这是 Salesforce 平台的一个关键安全特性,用于控制类中的代码在执行时是否遵循当前用户的记录级共享规则。
with sharing
: 强制执行当前用户的共享规则。如果用户没有权限查看或编辑某条记录,类中的代码也同样无法访问。这是保证数据安全的推荐做法。without sharing
: 忽略共享规则,代码在系统上下文 (System Context) 中运行,拥有访问所有记录的权限。这在需要聚合数据或处理后台逻辑时非常有用,但必须谨慎使用,以防数据泄露。inherited sharing
: 继承调用它的类的共享模式。如果一个without sharing
的类调用它,它就以without sharing
模式运行,反之亦然。
- implements: 表示该类实现了某个 Interface(接口)。接口定义了一组必须由实现类提供具体实现的方法签名,常用于实现像 Batch Apex (
Database.Batchable
) 或 Schedulable Apex (Schedulable
) 这样的系统功能。 - extends: 表示该类继承自另一个类(父类),从而可以重用父类的方法和属性。
类的成员
一个类内部主要包含以下成员:
- Variables / Properties (变量 / 属性): 用于存储对象的状态。可以是基本数据类型(如
Integer
,String
,Boolean
),也可以是 sObjects(如Account
,Contact
)或其他复杂类型。变量可以是实例变量(每个对象实例独有)或静态变量(被该类的所有实例共享)。 - Methods (方法): 定义了对象的行为。方法包含可执行的代码,用于操作类的数据或执行特定任务。方法同样有访问修饰符,并且可以是实例方法(通过类的实例调用)或静态方法(直接通过类名调用,无需创建实例)。
- Constructors (构造函数): 一种特殊的方法,用于创建和初始化类的实例。它的名称必须与类名完全相同,并且没有返回类型。如果未定义构造函数,系统会提供一个默认的无参构造函数。
示例代码
下面是一个来自 Salesforce 官方文档的经典示例,展示了一个名为 EmailManager
的 Apex 类。这个类演示了如何查询联系人、更新记录以及发送邮件,是实际项目中的常见场景。
场景: 假设我们需要一个功能,可以根据指定的客户 (Account) 名称,找到该客户下的所有联系人 (Contact),并向他们发送一封欢迎邮件。同时,我们需要在联系人记录上标记邮件已发送。
// EmailManager 类负责处理与邮件发送相关的业务逻辑 // 使用 "with sharing" 关键字确保代码执行时遵守当前用户的共享规则 public with sharing class EmailManager { // 一个私有布尔变量,用于防止在同一个事务中重复发送邮件 private Boolean sendWasCalled = false; // 一个公共方法,接收一个客户名称作为参数 public void sendMail(String accountName) { // 检查邮件是否已经发送过,防止递归或重复调用 if (sendWasCalled) { return; } sendWasCalled = true; // 创建一个邮件服务类的实例,用于实际的邮件发送操作 Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage(); // **SOQL 查询** // 使用 SOQL (Salesforce Object Query Language) 查询符合条件的联系人 // 查询条件是:客户名称匹配,且邮件地址不为空 List<Contact> contacts = [SELECT Id, Email FROM Contact WHERE Account.Name = :accountName AND Email != null]; // 检查是否查询到了联系人 if (contacts.isEmpty()) { // 如果没有找到联系人,则不执行任何操作 System.debug('未找到任何符合条件的联系人。'); return; } // 创建一个空的字符串列表,用于存储收件人的邮件地址 List<String> toAddresses = new List<String>(); for(Contact con : contacts) { // 遍历查询到的联系人列表,并将他们的邮件地址添加到收件人列表中 toAddresses.add(con.Email); } // 设置邮件的收件人地址 mail.setToAddresses(toAddresses); // 设置邮件的发件人显示名称 mail.setSenderDisplayName('Salesforce Support'); // 设置邮件主题 mail.setSubject('New Salesforce Account'); // 设置邮件正文 mail.setPlainTextBody('Welcome! Your new Salesforce account has been created.'); // **DML 操作与错误处理** try { // 调用 Messaging.sendEmail 方法发送邮件 // 这是一个内置的 Apex 方法,用于处理邮件发送 Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail }); // 邮件发送成功后,更新联系人记录 // 创建一个新的联系人列表,用于存放需要更新的记录 List<Contact> updatedContacts = new List<Contact>(); for (Contact con : contacts) { // 假设联系人对象上有一个自定义字段 "Welcome_Email_Sent__c" // con.Welcome_Email_Sent__c = true; updatedContacts.add(con); } // 使用 update DML 语句批量更新联系人记录 // update updatedContacts; // 此处为示例,实际字段需存在 } catch (System.EmailException e) { // 如果邮件发送失败,捕获异常 // 使用 System.debug 记录错误信息,便于调试 System.debug('邮件发送失败:' + e.getMessage()); } } }
代码说明:
- 类定义:
public with sharing class EmailManager
定义了一个公共类,并强制执行共享规则。 - 方法定义:
public void sendMail(String accountName)
定义了一个公共方法,接收一个字符串参数。 - SOQL 查询:代码使用 SOQL 查询来获取数据。注意查询中的
:accountName
是一个绑定变量,可以有效防止 SOQL 注入。 - 批量处理:代码首先收集所有收件人地址,然后一次性设置到邮件对象中,这是批量化思想的体现。同样,在更新联系人时,也是先构建一个列表,然后执行一次
update
DML 操作。 - 错误处理:使用
try-catch
块来捕获可能发生的EmailException
,确保即使邮件发送失败,程序也不会崩溃,并能记录下错误信息。
注意事项
在编写 Apex 类时,我们必须时刻关注 Salesforce 平台的多租户架构所带来的限制和要求。
Governor Limits (执行限制)
Salesforce 为了保证所有租户的资源公平使用,对代码执行施加了严格的限制。作为开发人员,我们必须编写高效的代码来避免触碰这些红线。常见的限制包括:
- SOQL 查询: 在一个同步事务中,最多执行 100 次 SOQL 查询。
- DML 语句: 在一个同步事务中,最多执行 150 次 DML 操作(如 insert, update, delete)。
- CPU 时间: 同步事务的 CPU 执行时间限制为 10,000 毫秒。
- 堆大小 (Heap Size): 同步事务中使用的内存不能超过 6 MB。
关键: 避免在循环中执行 SOQL 查询或 DML 语句,这是导致超出限制的最常见原因。始终坚持批量化 (Bulkification) 设计模式。
安全与共享 (Security & Sharing)
始终明确你的类应该以何种共享模式运行。除非有充分的理由,否则应默认使用 with sharing
来尊重为用户配置的数据可见性规则。滥用 without sharing
可能会导致用户访问到他们本不应看到的数据,造成严重的安全漏洞。
测试覆盖率 (Test Coverage)
在 Salesforce 中,任何要部署到生产环境的 Apex 代码(包括类和触发器)都必须至少有 75% 的代码行被单元测试覆盖。但这只是最低要求,一个高质量的项目应该追求更高的覆盖率(例如 90% 以上),并且测试用例应该覆盖所有关键业务逻辑、边界条件和否定场景。使用 @isTest
注解来编写测试类是 Apex 开发不可或缺的一部分。
API 版本 (API Versioning)
每个 Apex 类都与一个特定的 Salesforce API 版本相关联。当 Salesforce 发布新版本时,旧版本的 Apex 代码行为保持不变,这确保了向后兼容性。但作为开发人员,我们应该定期审查并将代码更新到最新的 API 版本,以便利用平台的新功能和性能改进。
错误处理 (Error Handling)
健壮的 Apex 类必须包含完善的错误处理逻辑。使用 try-catch
块来捕获预期的异常(如 DmlException
, QueryException
),并提供优雅的回退或记录机制。不要让未经处理的异常直接暴露给最终用户,这会带来极差的用户体验。
总结与最佳实践
Apex 类是 Salesforce 开发的核心。一个设计良好、代码健壮的 Apex 类不仅能实现复杂的业务需求,还能保证系统的性能、安全性和可维护性。作为 Salesforce 开发人员,我们应遵循以下最佳实践:
- 单一职责原则 (Single Responsibility Principle): 每个类应该只负责一件事情。例如,一个类负责处理客户相关的业务逻辑,另一个类负责与外部系统集成。这使得代码更清晰、更易于测试和维护。
- 代码批量化 (Bulkify Your Code): 永远假设你的代码会处理多条记录,无论是从触发器、批量 Apex 还是 API 调用。使用集合(如
List
,Set
,Map
)来批量处理数据。 - 避免硬编码 ID (Avoid Hardcoding IDs): 不要将 Record IDs、URL 或其他环境特定的值直接写入代码。使用自定义元数据类型 (Custom Metadata Types)、自定义设置 (Custom Settings) 或动态查询来获取这些值。
- 采用分层架构 (Use a Layered Architecture): 对于大型项目,考虑将代码分层,例如:
- Service Layer (服务层): 封装业务逻辑,供其他部分(如控制器、批量任务)调用。
- Domain Layer (领域层): 处理特定于某个 sObject 的逻辑,通常与触发器框架结合使用。
- Selector Layer (选择器层): 集中管理所有 SOQL 查询,提高代码复用性和可维护性。
- 编写有意义的注释和文档 (Write Meaningful Comments and Docs): 使用 ApexDoc 格式为你的类和方法编写注释。清晰的文档不仅能帮助其他开发人员理解你的代码,也能在几个月后帮助你自己快速回忆起当初的逻辑。
通过遵循这些原则和最佳实践,你将能够编写出高效、可扩展且易于维护的 Apex 类,从而在 Salesforce 平台上构建出强大的自定义应用程序。
评论
发表评论