精通 Salesforce Apex 类:开发者结构与最佳实践指南

作者:Salesforce 开发人员


背景与应用场景

作为一名 Salesforce 开发人员Apex 是我们工具箱中最核心、最强大的工具。它是一种强类型的、面向对象的编程语言,允许我们在 Salesforce 平台上执行自定义的业务逻辑和事务控制。而 Apex classes (Apex 类) 则是构建这一切的基础。从复杂的业务流程自动化、创建自定义 Web 服务,到为 Lightning Web Components (LWC)Aura 组件提供后端支持,Apex 类无处不在。

在标准功能无法满足企业独特的业务需求时,我们就需要借助 Apex 类来填补空白。例如:

1. 复杂的业务逻辑实现: 当一个对象的记录在创建或更新时,需要触发一系列复杂的计算、验证或关联记录的操作,而这些操作超出了标准工作流、Process BuilderFlow 的能力范围时,我们可以通过 Apex 触发器(Trigger)调用 Apex 类中的方法来完成。

2. 自定义用户界面后端支持: 无论是 Visualforce 页面、Aura 组件还是 LWC,都需要从服务器端获取数据或将数据写回服务器。Apex 类作为控制器(Controller),充当了前端界面与后端数据库之间的桥梁。

3. 系统集成: 当需要将 Salesforce 与外部系统(如 ERP、财务软件等)进行数据同步时,我们可以编写 Apex 类来处理出站(outbound)的 RESTSOAP 调用,或者创建自定义的 API 端点来接收来自外部系统的入站(inbound)请求。

4. 异步处理: 对于需要处理大量数据或执行耗时较长的操作,为了不影响用户体验和避免超出 Governor Limits (总督限制),我们可以使用 Apex 类实现异步处理逻辑,如 Batch ApexQueueable ApexScheduled 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 sharingwithout sharing 的使用至关重要。始终以最小权限原则为指导,默认使用 with sharing,除非有非常明确的业务需求(例如,一个记录汇总服务需要跨越用户的权限边界)才考虑使用 without sharing,并且必须仔细评估其安全风险。

3. 错误处理

健壮的 Apex 代码必须包含完善的错误处理机制。应该使用 try-catch-finally 块来捕获和处理潜在的异常,例如 DML 异常、查询异常或调用外部服务时的异常。这不仅可以防止整个事务失败,还能向用户提供友好的错误提示,或记录详细的错误日志以供调试。

4. API 版本控制

每个 Apex 类都与一个特定版本的 Salesforce API 相关联。在保存类时,会自动设置其 API 版本。不同版本的 API 可能会有不同的行为。最佳实践是让所有的类都使用最新的、统一的 API 版本,以便利用最新的功能和性能改进。


总结与最佳实践

Apex 类是 Salesforce 开发的核心。一个设计良好、代码高效、安全可靠的 Apex 类是构建强大应用程序的基础。作为开发人员,我们应该遵循以下最佳实践来确保代码质量:

  1. 代码批量化是第一原则:

    永远不要在循环(如 forwhile)中执行 SOQL 查询或 DML 操作。应该先收集所有需要处理的记录 ID,然后一次性进行查询或 DML 操作。
  2. 显式声明共享模式:

    不要依赖默认的共享行为。为每个 Apex 类明确指定 with sharingwithout sharing(或 inherited sharing),使代码意图清晰,行为可预测。
  3. 编写单元测试:

    Salesforce 要求 Apex 代码必须有至少 75% 的测试覆盖率才能部署到生产环境。但我们的目标不应仅仅是满足覆盖率,而应是编写有意义的测试,覆盖所有业务逻辑分支、正面和负面场景,以及批量化处理的情况。
  4. 单一职责原则 (Single Responsibility Principle):

    让每个类只做一件事,并把它做好。例如,将触发器逻辑、业务逻辑和数据访问逻辑分离到不同的类中(如 Trigger Handler 模式),可以使代码更易于维护、测试和重用。
  5. 避免硬编码 ID:

    不要在代码中直接写入记录 ID、URL 或其他环境相关的值。应该使用自定义元数据(Custom Metadata Types)、自定义设置(Custom Settings)或标签(Labels)来存储这些值,以提高代码的可移植性和可配置性。
  6. 查询优化:

    只查询你需要的字段,避免使用 SELECT *。在 WHERE 子句中使用索引字段(如 ID、所有者 ID、查找字段等)来提高查询性能。

通过遵循这些原则和最佳实践,我们可以编写出不仅能满足当前业务需求,而且在未来依然健壮、可扩展和易于维护的 Apex 代码,从而最大化 Salesforce 平台的价值。

评论

此博客中的热门博文

Salesforce 登录取证:深入解析用户访问监控与安全

Salesforce Experience Cloud 技术深度解析:构建社区站点 (Community Sites)

精通 Salesforce Email Studio:咨询顾问指南之 AMPscript 与数据扩展实现动态个性化邮件