精通 Salesforce Apex 类:开发者结构与最佳实践指南
作者:Salesforce 开发人员
背景与应用场景
作为一名 Salesforce 开发人员,Apex 是我们工具箱中最核心、最强大的工具。它是一种强类型的、面向对象的编程语言,允许我们在 Salesforce 平台上执行自定义的业务逻辑和事务控制。而 Apex classes (Apex 类) 则是构建这一切的基础。从复杂的业务流程自动化、创建自定义 Web 服务,到为 Lightning Web Components (LWC) 或 Aura 组件提供后端支持,Apex 类无处不在。
在标准功能无法满足企业独特的业务需求时,我们就需要借助 Apex 类来填补空白。例如:
1. 复杂的业务逻辑实现: 当一个对象的记录在创建或更新时,需要触发一系列复杂的计算、验证或关联记录的操作,而这些操作超出了标准工作流、Process Builder 或 Flow 的能力范围时,我们可以通过 Apex 触发器(Trigger)调用 Apex 类中的方法来完成。
2. 自定义用户界面后端支持: 无论是 Visualforce 页面、Aura 组件还是 LWC,都需要从服务器端获取数据或将数据写回服务器。Apex 类作为控制器(Controller),充当了前端界面与后端数据库之间的桥梁。
3. 系统集成: 当需要将 Salesforce 与外部系统(如 ERP、财务软件等)进行数据同步时,我们可以编写 Apex 类来处理出站(outbound)的 REST 或 SOAP 调用,或者创建自定义的 API 端点来接收来自外部系统的入站(inbound)请求。
4. 异步处理: 对于需要处理大量数据或执行耗时较长的操作,为了不影响用户体验和避免超出 Governor Limits (总督限制),我们可以使用 Apex 类实现异步处理逻辑,如 Batch Apex、Queueable Apex 或 Scheduled Apex。
因此,深刻理解 Apex 类的结构、原理及其最佳实践,是每一位 Salesforce 开发人员高效、稳定地构建可扩展应用程序的基石。
原理说明
Apex 类本质上是创建对象(Object)的蓝图或模板。它封装了数据(以成员变量的形式)和行为(以方法的形式)。Apex 遵循了经典的 Object-Oriented Programming (OOP) (面向对象编程) 范式,支持封装、继承和多态等核心概念。
类的基本结构
一个典型的 Apex 类定义包含以下几个部分:
1. 访问修饰符 (Access Modifier):
它定义了类的可见性。最常用的有 private
(仅在当前类内部可见,默认值)、public
(在您的应用程序或命名空间内任何地方都可见)和 global
(对任何命名空间、应用程序都可见,通常用于构建可供外部调用的 Web 服务或托管包的 API)。
2. 共享关键字 (Sharing Keywords):
这是 Salesforce 平台特有的一个关键概念,它决定了类在执行时如何应用当前用户的共享规则。
with sharing
: 类将强制执行当前用户的共享规则。这意味着代码只能看到和操作用户有权限访问的记录。这是安全方面的最佳实践。without sharing
: 类将在系统上下文中运行,忽略用户的共享规则。代码可以访问和操作组织内的所有记录。使用时必须非常谨慎,以防数据泄露。inherited sharing
: 类会继承调用它的上下文的共享模式。例如,如果一个with sharing
的类调用了一个inherited sharing
的类,那么后者将以with sharing
的模式运行。如果不明确指定,类的默认模式是without sharing
,但这在未来的版本中可能会改变,因此最佳实践是始终显式声明共享模式。
3. 类名与扩展/实现:
类名必须唯一。通过 extends
关键字,一个类可以继承另一个类(Apex 不支持多重继承)。通过 implements
关键字,一个类可以实现一个或多个接口(Interface)。
4. 成员变量 (Member Variables):
也称为属性(properties),用于存储对象的状态。它们同样有访问修饰符。
5. 构造函数 (Constructor):
一个特殊的方法,用于在创建类的实例(对象)时进行初始化。如果未定义构造函数,系统会提供一个默认的、无参数的构造函数。
6. 方法 (Methods):
定义了类的行为。方法包含了具体的业务逻辑,可以接收参数并返回值。
示例代码
下面是一个来自 Salesforce 官方文档的 Apex 类示例。这个类 AccountManager
创建了一个 REST API 端点,允许外部系统通过 HTTP GET 请求来获取客户(Account)记录。这个例子很好地展示了类的结构、注解(Annotation)的使用以及如何与 Salesforce 数据交互。
这个类通过 @RestResource
注解将其暴露为一个 REST 服务,路径为 /Accounts/*
。@HttpGet
注解表明 getAccount
方法将处理 GET 请求。
@RestResource(urlMapping='/Accounts/*') global with sharing class AccountManager { @HttpGet global static Account getAccount() { // 从请求上下文中获取 RestRequest 对象 RestRequest req = RestContext.request; // 从请求的 URI 中提取客户 ID // 例如, 请求 URI 可能是 /services/apexrest/Accounts/001D000000IB3g0/ // split('/') 会将其分解成一个字符串数组 String accountId = req.requestURI.substring(req.requestURI.lastIndexOf('/')+1); // 使用 SOQL (Salesforce Object Query Language) 查询客户记录 // 我们只查询需要的字段,这是一种最佳实践 Account result = [SELECT Id, Name, Phone, Website FROM Account WHERE Id = :accountId]; // 返回查询到的客户记录 // Salesforce 会自动将其序列化为 JSON 或 XML 格式 return result; } }
代码注释详解
@RestResource(urlMapping='/Accounts/*')
: 这是一个注解,它告诉 Salesforce 平台这个类是一个 REST 资源。urlMapping
参数定义了访问这个资源的 URL 路径。星号(*)是一个通配符,表示可以匹配该路径下的任何值,我们通常用它来传递记录 ID。global with sharing class AccountManager
:global
: 访问修饰符,因为要作为公共 API 暴露给外部系统,所以必须是global
。with sharing
: 明确声明此类在执行时会遵循当前用户的共享规则。如果一个用户通过 API 调用此服务,但他们没有权限查看请求的客户记录,那么查询将不会返回任何结果,从而保证了数据安全。
@HttpGet
: 这个注解标记了下面的方法getAccount
是用来处理 HTTP GET 请求的。global static Account getAccount()
:global
: 因为它是一个 REST 服务方法,所以也必须是global
。static
: 静态方法,意味着调用它时不需要创建类的实例。对于 REST 和 SOAP 服务,方法通常是静态的。Account
: 方法的返回类型。它将返回一个 Account 对象。
RestContext.request
: 这是一个上下文变量,提供了对当前 REST 请求的访问,包括 URI、请求头(headers)和请求体(body)。[SELECT Id, Name, Phone, Website FROM Account WHERE Id = :accountId]
: 这是一个 SOQL (Salesforce Object Query Language) 查询。它非常类似于 SQL,用于从 Salesforce 数据库中检索数据。冒号(:)用于绑定 Apex 变量accountId
到查询中,这可以有效防止 SOQL 注入攻击。
注意事项
在编写 Apex 类时,我们必须时刻注意 Salesforce 平台的多租户(multi-tenant)架构所带来的限制和要求。
1. Governor Limits (总督限制)
为了保证所有客户共享的资源能够被公平使用,Salesforce 对每个事务(transaction)中的资源消耗都设有严格的限制。作为开发人员,我们必须编写高效的代码来避免触及这些限制。
- SOQL 查询: 每个事务最多只能执行 100 条 SOQL 查询。
- DML 操作: 每个事务最多只能执行 150 条 DML (Data Manipulation Language) (数据操作语言,如
insert
,update
,delete
) 语句。 - CPU 时间: 每个事务的 CPU 执行时间不能超过 10,000 毫秒。
- 堆大小 (Heap Size): 每个事务的内存使用不能超过 6MB。
触及这些限制会导致事务立即失败并回滚。避免这些问题的关键在于代码的批量化(Bulkification),即确保我们的代码能够一次性处理多条记录,而不是在循环中逐条处理。
2. 安全与数据访问
如前所述,with sharing
和 without sharing
的使用至关重要。始终以最小权限原则为指导,默认使用 with sharing
,除非有非常明确的业务需求(例如,一个记录汇总服务需要跨越用户的权限边界)才考虑使用 without sharing
,并且必须仔细评估其安全风险。
3. 错误处理
健壮的 Apex 代码必须包含完善的错误处理机制。应该使用 try-catch-finally
块来捕获和处理潜在的异常,例如 DML 异常、查询异常或调用外部服务时的异常。这不仅可以防止整个事务失败,还能向用户提供友好的错误提示,或记录详细的错误日志以供调试。
4. API 版本控制
每个 Apex 类都与一个特定版本的 Salesforce API 相关联。在保存类时,会自动设置其 API 版本。不同版本的 API 可能会有不同的行为。最佳实践是让所有的类都使用最新的、统一的 API 版本,以便利用最新的功能和性能改进。
总结与最佳实践
Apex 类是 Salesforce 开发的核心。一个设计良好、代码高效、安全可靠的 Apex 类是构建强大应用程序的基础。作为开发人员,我们应该遵循以下最佳实践来确保代码质量:
代码批量化是第一原则:
永远不要在循环(如for
或while
)中执行 SOQL 查询或 DML 操作。应该先收集所有需要处理的记录 ID,然后一次性进行查询或 DML 操作。显式声明共享模式:
不要依赖默认的共享行为。为每个 Apex 类明确指定with sharing
或without sharing
(或inherited sharing
),使代码意图清晰,行为可预测。编写单元测试:
Salesforce 要求 Apex 代码必须有至少 75% 的测试覆盖率才能部署到生产环境。但我们的目标不应仅仅是满足覆盖率,而应是编写有意义的测试,覆盖所有业务逻辑分支、正面和负面场景,以及批量化处理的情况。单一职责原则 (Single Responsibility Principle):
让每个类只做一件事,并把它做好。例如,将触发器逻辑、业务逻辑和数据访问逻辑分离到不同的类中(如 Trigger Handler 模式),可以使代码更易于维护、测试和重用。避免硬编码 ID:
不要在代码中直接写入记录 ID、URL 或其他环境相关的值。应该使用自定义元数据(Custom Metadata Types)、自定义设置(Custom Settings)或标签(Labels)来存储这些值,以提高代码的可移植性和可配置性。查询优化:
只查询你需要的字段,避免使用SELECT *
。在WHERE
子句中使用索引字段(如 ID、所有者 ID、查找字段等)来提高查询性能。
通过遵循这些原则和最佳实践,我们可以编写出不仅能满足当前业务需求,而且在未来依然健壮、可扩展和易于维护的 Apex 代码,从而最大化 Salesforce 平台的价值。
评论
发表评论