精通 Salesforce Apex 类:开发者综合指南


背景与应用场景

在 Salesforce 平台中,管理员和开发者可以使用一系列强大的声明式工具(如 Flow、校验规则)来快速实现业务需求。然而,当业务逻辑变得复杂、需要与外部系统进行深度集成,或者需要处理大量数据时,声明式工具往往会遇到瓶颈。这时,Apex 就成为了不可或缺的编程解决方案。

Apex 是 Salesforce 平台提供的一种强类型、面向对象的编程语言,其语法与 Java 类似。通过编写 Apex Classes (Apex 类),开发者可以构建稳健、可扩展的应用程序,以满足最复杂的业务需求。

Apex 类的主要应用场景包括:

  • 复杂的业务逻辑实现: 当业务规则包含多步计算、复杂的条件判断或跨多个对象的操作时,Apex 类可以提供声明式工具无法比拟的灵活性。
  • 自定义 Web 服务: 创建 REST 或 SOAP API,使外部系统能够安全地与 Salesforce 数据进行交互。
  • Visualforce 控制器: 为 Visualforce 页面提供后端逻辑支持,控制页面行为和数据展示。
  • Aura 和 Lightning Web Components (LWC) 后端控制器: 作为 Lightning 组件的数据服务层,执行服务器端调用(例如,查询数据、执行 DML 操作)。
  • 批处理作业 (Batch Apex): 异步处理数百万条记录,而不会超出平台的 Governor Limits (管控限制)。
  • 计划任务 (Scheduled Apex): 在指定时间自动执行 Apex 代码,用于日常维护、数据同步等任务。
  • 自定义邮件服务: 处理入站电子邮件,并根据邮件内容自动创建或更新 Salesforce 记录。

理解和掌握 Apex 类的编写是每一位 Salesforce 开发者的核心技能,也是构建企业级应用的基础。


原理说明

Apex 类是创建对象的蓝图或模板。一个类封装了数据(以变量形式)和行为(以方法形式)。所有 Apex 代码都在 Salesforce 的多租户环境中执行,这意味着平台会强制实施严格的资源限制(即 Governor Limits),以确保没有单个客户的代码会垄断共享资源。

面向对象编程 (Object-Oriented Programming, OOP)

Apex 遵循 OOP 的核心原则,这使得代码更加模块化、可重用和易于维护:

  • 封装 (Encapsulation): 将数据(属性)和操作数据的代码(方法)捆绑在一个单元(类)中。通过访问修饰符(`private`, `protected`, `public`, `global`)控制对内部成员的访问。
  • 继承 (Inheritance): 一个类可以从另一个类(父类)继承属性和方法,从而实现代码重用。使用 `extends` 关键字实现继承。
  • 多态 (Polymorphism): 允许使用父类类型的变量来引用子类的对象,并通过 `virtual` 和 `override` 关键字实现方法的重写,使得程序可以根据对象的实际类型执行不同的行为。

类的关键组成部分

  • 访问修饰符 (Access Modifiers):
    • `private`:仅在当前类内部可见。这是最严格的访问级别。
    • `public`:对命名空间内的所有 Apex 代码可见。
    • `global`:对所有 Apex 代码可见,无论其命名空间如何。通常用于构建可供外部调用的 Web 服务或在受管包中暴露 API。
  • 共享关键字 (Sharing Keywords):
    • `with sharing`:强制执行当前用户的共享规则。这是默认的(如果类是从 Aura/LWC 调用的)。
    • `without sharing`:不强制执行共享规则,代码可以访问和操作组织内的所有数据(但仍受用户权限和许可证限制)。
    • `inherited sharing`:继承调用它的类的共享模式。这是推荐的最佳实践,因为它使类的行为更具可预测性。
  • 变量 (Variables): 用于存储数据的成员。可以是基本数据类型(如 `Integer`, `String`, `Boolean`)或复杂类型(如 sObjects, List, Map)。
  • 方法 (Methods): 定义类的行为。方法可以接受参数并返回值。
    • 构造函数 (Constructor): 一种特殊的方法,用于在创建类的实例时进行初始化。
    • 静态方法 (Static Methods): 属于类本身而不是类的实例,可以直接通过类名调用,无需创建对象。

示例代码

以下示例均来自 Salesforce 官方开发者文档,展示了 Apex 类的不同方面。

示例 1: 一个基础的 Apex 类

这个例子定义了一个简单的 `EmailManager` 类,它包含一个 `public` 方法,用于向一组联系人发送邮件。这个类展示了基本的类结构、方法定义和 DML 操作。

// public 访问修饰符意味着这个类可以被组织内的任何 Apex 代码访问。
public class EmailManager {
    // 这是一个 public 方法,接受一个字符串列表作为收件人地址。
    // 它不返回值 (void)。
    public void sendMail(String[] addresses, String subject, String body) {
        // 创建一个 Messaging.SingleEmailMessage 对象列表来存储要发送的邮件。
        // 在循环外创建集合是 "Bulkification" 的第一步。
        List<Messaging.SingleEmailMessage> messages = new List<Messaging.SingleEmailMessage>();

        for (String address : addresses) {
            // 创建一个邮件实例。
            Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
            
            // 设置收件人地址。
            String[] toAddresses = new String[] {address};
            mail.setToAddresses(toAddresses);
            
            // 设置邮件主题。
            mail.setSubject(subject);
            
            // 设置纯文本邮件正文。
            mail.setPlainTextBody(body);
            
            // 将准备好的邮件添加到列表中。
            messages.add(mail);
        }

        // 使用 Messaging.sendEmail() 方法一次性发送列表中的所有邮件。
        // 这是一个高效的 DML 操作,有助于避免超出 Governor Limits。
        Messaging.SendEmailResult[] results = Messaging.sendEmail(messages);

        // 检查发送结果。
        if (results[0].isSuccess()) {
            System.debug('The email was sent successfully.');
        } else {
            System.debug('The email failed to send: ' + results[0].getErrors()[0].getMessage());
        }
    }
}

示例 2: 带有 SOQL 查询和共享关键字的类

这个例子展示了一个带有 `with sharing` 关键字的类,它会遵守当前用户的共享规则。它执行一个 SOQL (Salesforce Object Query Language) 查询来获取与特定客户关联的联系人。

// 'with sharing' 关键字确保该类的代码在当前用户的共享权限下运行。
// 这意味着用户只能查询到他们有权访问的客户和联系人记录。
public with sharing class ContactSearch {
    
    // 这是一个 public static 方法,可以直接通过类名调用,例如:ContactSearch.searchForContacts(...)
    // 它返回一个 Contact 对象的列表。
    public static List<Contact> searchForContacts(String accountName, String mailingPostalCode) {
        // 执行 SOQL 查询。
        // 这个查询会查找与给定客户名称和邮政编码匹配的联系人。
        // 这是一个安全、高效的查询,因为它使用了绑定变量(冒号前缀的变量)来防止 SOQL 注入。
        List<Contact> contacts = [SELECT Id, Name FROM Contact 
                                  WHERE Account.Name = :accountName 
                                  AND MailingPostalCode = :mailingPostalCode];
        return contacts;
    }
}

示例 3: 带有构造函数的类

这个例子展示了一个 `AccountHelper` 类,它包含一个构造函数,在创建类的实例时接收一个 `Account` 记录。这是一种常见的封装 sObject 记录并为其提供附加业务逻辑的模式。

public class AccountHelper {
    // 声明一个 private 成员变量来存储 Account 记录。
    // 'private' 意味着它只能在此类内部访问。
    // 'final' 意味着一旦在构造函数中赋值,就不能再更改。
    private final Account acct;

    // 这是一个构造函数。它的名称与类名相同,没有返回值。
    // 当使用 'new AccountHelper(someAccount)' 创建实例时,此构造函数被调用。
    public AccountHelper(Account a) {
        // 将传入的 Account 记录赋值给成员变量。
        this.acct = a;
    }

    // 这是一个 public 方法,用于验证客户的年度收入字段。
    public Boolean validate() {
        if (this.acct.AnnualRevenue == null || this.acct.AnnualRevenue <= 0) {
            // 如果年度收入为空或小于等于零,则验证失败。
            return false;
        }
        return true;
    }
}

// 如何使用这个类:
// Account myAccount = [SELECT Id, Name, AnnualRevenue FROM Account WHERE Name = 'Test Corp' LIMIT 1];
// AccountHelper helper = new AccountHelper(myAccount);
// Boolean isValid = helper.validate();
// System.debug('Is the account valid? ' + isValid);

注意事项

在编写 Apex 类时,必须时刻牢记 Salesforce 平台的特性和限制。

Governor Limits (管控限制)

Salesforce 在多租户架构上运行,为防止任何单一代码事务消耗过多共享资源,平台设置了严格的限制。常见的限制包括:

  • SOQL 查询: 每个事务中最多执行 100 个同步 SOQL 查询。
  • DML 语句: 每个事务中最多执行 150 次 DML 操作(如 `insert`, `update`, `delete`)。
  • 总记录数: DML 语句处理的总记录数不能超过 10,000 条。
  • CPU 时间: 每个事务的 CPU 执行时间有限制(例如,同步为 10,000 毫秒)。
  • 堆大小 (Heap Size): 同步事务中为 6MB,异步为 12MB。

解决方案: 始终遵循 Bulkification (批量化) 模式。即,设计代码来处理记录集合(`List`, `Set`, `Map`),而不是在循环中逐条处理记录并执行 SOQL 或 DML。

错误处理

健壮的代码必须包含完善的错误处理机制。使用 `try-catch` 块来捕获和处理潜在的异常,如 `QueryException` 或 `DmlException`。对于 DML 操作,可以考虑使用 `Database` 类的方法(如 `Database.insert(records, allOrNone)`),它允许部分记录成功并返回失败记录的详细错误信息,而不是抛出异常。

安全与权限

始终考虑数据安全。默认情况下,Apex 在系统模式下运行,这意味着它会忽略对象级和字段级安全性(FLS)。

  • 使用 `with sharing` 关键字来强制执行用户的共享规则。
  • 在动态 SOQL 或处理敏感数据时,使用 `Schema` 方法(如 `isAccessible()`, `isUpdateable()`)来编程检查用户是否具有对特定对象或字段的访问权限。

测试覆盖率

Salesforce 强制要求 Apex 代码(类和触发器)必须有至少 75% 的测试覆盖率才能部署到生产环境。测试类不仅是满足部署要求的工具,更是确保代码质量、功能正确性和防止未来更新破坏现有功能的关键保障。应编写能够覆盖所有业务逻辑分支、正面和负面场景的测试用例。使用 `Test.startTest()` 和 `Test.stopTest()` 来隔离代码并获取一组新的 Governor Limits。


总结与最佳实践

Apex 类是 Salesforce 平台开发的核心。一个设计良好的 Apex 类应该是高效、可读、可维护且安全的。作为技术架构师,我推荐遵循以下最佳实践:

  1. 单一职责原则: 每个类应该只负责一项功能。例如,一个类负责处理业务逻辑,另一个类负责数据查询。这使得代码更易于理解和测试。
  2. 代码批量化 (Bulkify Your Code): 永远不要在循环中放置 SOQL 查询或 DML 语句。始终使用集合来处理数据。
  3. 善用 Map 优化查询: 当需要基于一组 ID 查询相关记录时,先将查询结果放入一个 `Map` 中,这样可以在后续逻辑中通过 ID 高效地获取记录,而无需再次查询。
  4. 明确共享模式: 为每个类显式声明共享关键字(`with sharing`, `without sharing`, `inherited sharing`)。`inherited sharing` 是最安全和最灵活的选择,因为它会适应其调用上下文。
  5. 编写有意义的测试: 不要只为了达到 75% 的覆盖率而测试。使用 `System.assertEquals()` 来断言代码的行为是否符合预期,并测试各种边界条件。
  6. 代码注释和命名规范: 使用清晰的变量名和方法名,并为复杂的逻辑块添加注释。这对于团队协作和长期维护至关重要。
  7. 避免硬编码 ID: 永远不要在 Apex 代码中硬编码记录 ID。应该使用动态查询、自定义元数据类型 (Custom Metadata Types) 或自定义设置 (Custom Settings) 来存储和检索这些值。

通过遵循这些原则和最佳实践,您可以构建出不仅能满足当前需求,而且能够随着业务发展而轻松扩展的高质量 Salesforce 应用程序。

评论

此博客中的热门博文

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

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

Salesforce Data Loader 全方位指南:数据迁移与管理的最佳实践