精通 Salesforce Apex 类:开发者深度指南


身份:Salesforce 开发人员


背景与应用场景

作为一名 Salesforce 开发人员,我们日常工作的核心就是编写代码来扩展平台的标准功能,以满足复杂多变的业务需求。而在 Salesforce 平台中,实现这一切的基石便是 Apex Class (Apex 类)。Apex 是一种强类型、面向对象的编程语言,语法上与 Java 和 C# 非常相似,它允许我们在 Salesforce 的多租户环境中执行自定义的业务逻辑。

简单来说,一个 Apex Class 就是一个模板或蓝图,用于创建 Object (对象)。这个蓝图定义了对象可以拥有的属性(变量)和它可以执行的操作(方法)。在 Salesforce 的世界里,Apex 类的应用场景无处不在,几乎所有定制化开发都离不开它:

1. 自定义业务逻辑: 当标准的验证规则、工作流规则或流程构建器(Process Builder)无法满足复杂的校验逻辑或数据处理需求时,我们需要通过 Apex 触发器(Trigger)调用 Apex 类来执行这些高级操作。

2. 自定义控制器:Visualforce 页面或 Aura/Lightning Web Components (LWC) 提供后端数据支持和业务逻辑处理。例如,从数据库查询记录、保存用户输入的数据、调用外部服务等。

3. Web 服务集成: 编写 Apex 类可以将其暴露为 RESTSOAP Web 服务,允许外部系统安全地与 Salesforce 进行数据交互。同样,我们也可以在 Apex 类中调用外部的 Web 服务。

4. 异步处理: 对于需要处理大量数据或耗时较长的操作,为了不阻塞用户界面并避免超出 Governor Limits (执行限制),我们可以使用异步 Apex,如 Batch Apex (批处理 Apex)、Queueable Apex (可队列化 Apex) 和 Scheduled Apex (计划 Apex),而这些异步任务的逻辑都是在 Apex 类中定义的。

5. 邮件服务: 创建自定义的邮件服务,根据收到的邮件内容自动创建或更新 Salesforce 中的记录。

理解并熟练掌握 Apex 类的结构、原理和最佳实践,是每一位 Salesforce 开发人员的必备技能,也是构建稳定、高效、可扩展的 Salesforce 应用的基础。

原理说明

一个 Apex 类是 Object-Oriented Programming (OOP, 面向对象编程) 思想在 Salesforce 平台上的具体实现。它封装了数据(以变量的形式)和行为(以方法的形式)。下面我们来详细拆解一个 Apex 类的核心构成要素。

1. 类的定义与访问修饰符

一个基本的类定义由访问修饰符、关键字 class、类名以及类主体构成。

public class MyClassName {
    // 类的主体部分:变量、构造方法、方法等
}

访问修饰符决定了谁可以访问这个类:

private: 仅在当前类内部可见。这是默认的访问修饰符,如果不指定,则默认为 private。

public: 在你的应用程序或命名空间(namespace)内的任何地方都可以访问。例如,Visualforce 控制器、REST API 控制器通常需要声明为 public。

global: 对所有 Apex 代码都可见,无论它在哪个命名空间。通常用于需要被外部命名空间(例如来自 AppExchange 的托管包)或 Web 服务调用的情况。

2. 共享关键字 (Sharing Keywords)

这是 Salesforce 平台特有的一个重要概念,它决定了类在执行时是否遵循当前用户的共享规则和权限设置。

with sharing: 当类被声明为 `with sharing` 时,代码在执行时会强制应用当前用户的共享规则。这意味着用户只能看到和编辑他们有权访问的记录。这是确保数据安全的首选方式。

without sharing: 当类被声明为 `without sharing` 时,代码会以系统模式运行,忽略当前用户的共享规则。这在某些需要跨用户权限进行数据处理的场景下很有用,但必须谨慎使用,以防数据泄露。

inherited sharing: 如果一个类被声明为 `inherited sharing`,它会继承调用它的那个类的共享模式。如果它被一个 `with sharing` 的类调用,它就以 `with sharing` 模式运行;如果被 `without sharing` 的类调用,则以 `without sharing` 模式运行。如果它处于调用链的顶端(例如被匿名执行窗口调用),则默认为 `with sharing`。

3. 变量 (Variables)

变量用于在类中存储数据。它们可以是基本数据类型(如 Integer, String, Boolean),也可以是 sObject(如 Account, Contact)或其他复杂的 Apex 类型。

实例变量 (Instance Variables): 每个类的实例(对象)都拥有自己的一套实例变量。它们在没有 `static` 关键字的情况下声明。

静态变量 (Static Variables): 变量前带有 `static` 关键字。静态变量属于类本身,而不是类的任何特定实例。所有实例共享同一个静态变量的副本。

4. 方法 (Methods)

方法定义了类可以执行的操作。它由访问修饰符、返回类型、方法名和参数列表组成。

public static String getWelcomeMessage(String name) {
    return 'Welcome, ' + name;
}

实例方法 (Instance Methods): 没有 `static` 关键字。它们必须通过类的实例来调用,并且可以访问该实例的实例变量。

静态方法 (Static Methods): 带有 `static` 关键字。它们可以直接通过类名调用,无需创建类的实例。静态方法只能访问静态变量。

5. 构造方法 (Constructors)

构造方法是一种特殊的方法,用于创建和初始化类的实例(对象)。它的名称必须与类名完全相同,并且没有返回类型。

public class MyController {
    private final Account acct;

    // 这是一个构造方法
    public MyController() {
        acct = [SELECT Id, Name FROM Account WHERE Id = :ApexPages.currentPage().getParameters().get('id')];
    }
}

如果一个类没有定义任何构造方法,系统会自动提供一个默认的、无参数的公共构造方法。一旦你定义了任何一个构造方法,这个默认的构造方法就不再存在。

示例代码

下面是一个来自 Salesforce 官方文档的 Apex 类示例,该类用于发送一封简单的电子邮件。这个例子非常实用,清晰地展示了一个公共类、一个静态方法、参数传递以及如何与 Salesforce 平台的核心功能(如邮件服务)进行交互。

这个 `EmailManager` 类包含一个公共的静态方法 `sendMail`,它接收收件人地址、邮件主题和邮件正文作为参数,然后构建并发送邮件。

// A utility class for sending emails.
public class EmailManager {

    // Public method for sending an email.
    // @param toAddress - The email address of the recipient.
    // @param subject - The subject of the email.
    // @param body - The plain text body of the email.
    public static void sendMail(String toAddress, String subject, String body) {
        
        // Step 1: Create a new instance of a single email message.
        // Messaging.SingleEmailMessage 是 Salesforce 内置的用于处理单封邮件的类。
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
        
        // Step 2: Set the recipient's email address.
        // setToAddresses 方法需要一个字符串列表(List)。
        // 这里我们创建一个只包含一个地址的列表。
        String[] toAddresses = new String[] {toAddress};
        mail.setToAddresses(toAddresses);
        
        // Step 3: Set the subject and body of the email.
        // setSubject 设置邮件主题。
        mail.setSubject(subject);
        // setPlainTextBody 设置纯文本格式的邮件正文。
        mail.setPlainTextBody(body);
        
        // Step 4: Send the email.
        // Messaging.sendEmail 方法负责实际发送邮件。它接收一个邮件消息列表。
        // 将其放入 try-catch 块中是最佳实践,用于捕获可能发生的发送失败异常。
        try {
            Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
        } catch (EmailException e) {
            // 如果邮件发送失败,捕获异常并将其记录到调试日志中。
            // 在实际项目中,这里可能需要更复杂的错误处理逻辑,例如记录到自定义对象中。
            System.debug('Email failed to send to ' + toAddress + '. Error: ' + e.getMessage());
        }
    }
}

如何调用这个方法?

由于 `sendMail` 是一个静态方法,我们可以直接通过类名来调用它,而无需创建 `EmailManager` 类的实例。例如,你可以在匿名执行窗口中运行以下代码来测试它:

EmailManager.sendMail('test@example.com', 'Test Subject from Apex', 'This is a test email sent from an Apex class.');

注意事项

在编写 Apex 类时,必须时刻关注 Salesforce 平台的多租户架构和其强制执行的限制与规则。

1. Governor Limits (执行限制): 这是最重要的注意事项。Salesforce 为每个事务(transaction)设置了严格的资源使用限制,以确保不会有任何单一的代码执行影响到共享资源的其他用户。常见的限制包括:

  • 每个事务中的 SOQL 查询总数(同步:100,异步:200)
  • 每个事务中的 DML 语句总数(150)
  • 每个事务中可以检索的总记录数(50,000)
  • CPU 执行总时间(同步:10,000 毫秒)
  • 总堆大小(Heap size)(同步:6MB)

因此,编写高效、Bulkified (批量化) 的代码至关重要,例如,绝对不能在循环中执行 SOQL 查询或 DML 操作。

2. 安全与共享规则: 始终考虑数据安全。默认情况下,Apex 类以系统模式运行(`without sharing`),这会绕过用户的字段级安全(Field-Level Security)和共享规则。除非业务逻辑明确要求,否则应始终使用 `with sharing` 关键字来强制执行当前用户的权限,防止未经授权的数据访问。

3. 代码覆盖率 (Test Coverage): 为了确保代码质量和稳定性,Salesforce 强制要求在部署到生产环境之前,Apex 类和触发器的代码覆盖率必须达到至少 75%。这意味着你必须为你的 Apex 类编写同样高质量的测试类(Test Class),覆盖各种业务场景,包括正面和负面测试用例。

4. API 版本: 每个 Apex 类都与一个特定的 Salesforce API 版本相关联。这决定了该类在执行时所遵循的行为和语法规则。当 Salesforce 发布新版本时,旧版本的类行为保持不变,这确保了向后兼容性。在开发时,建议使用最新的稳定 API 版本以利用新功能和改进。

5. 异常处理 (Exception Handling): 健壮的代码必须能够优雅地处理意外情况。始终使用 `try-catch` 块来捕获和处理潜在的异常,如 `DmlException`(当 DML 操作失败时)、`QueryException`(当 SOQL 查询无效时)或任何自定义异常。这可以防止未处理的错误中断整个事务并向用户显示不友好的错误消息。

总结与最佳实践

Apex 类是 Salesforce 开发的基石,它为平台提供了无与伦比的灵活性和强大的功能。作为开发人员,我们的目标是编写不仅能实现功能,而且高效、安全且易于维护的代码。

以下是一些在编写 Apex 类时应遵循的最佳实践:

单一职责原则 (Single Responsibility Principle): 让每个类只专注于一个功能。例如,创建一个专门处理客户相关业务逻辑的服务类,一个专门用于与外部系统集成的调用类,以及一个存放通用辅助方法的工具类。这使得代码更易于理解、测试和维护。

代码批量化 (Bulkify Your Code): 永远假设你的代码会一次性处理多条记录,而不仅仅是一条。使用集合(如 List, Set, Map)来处理数据,避免在循环中进行数据库操作。

避免在循环中执行 SOQL/DML: 这是导致触及 Governor Limits 的最常见原因。在进入循环之前,一次性查询出所有需要的数据;在循环中构建好要操作的数据列表,最后在循环外执行一次 DML 操作。

利用 Map 进行高效查询和数据关联: 当需要根据一组 ID 查询相关记录时,使用 `Map` 可以极大地提高数据检索和处理效率,避免嵌套循环。

编写有意义的测试: 不要只为了达到 75% 的覆盖率而写测试。你的测试类应该验证代码的实际逻辑是否正确,包括使用 `System.assert()` 来验证结果,并覆盖各种边界条件和错误情况。

清晰的命名规范和注释: 使用清晰、一致的命名规范来命名你的类、方法和变量。对于复杂的逻辑,添加注释来解释其目的和工作方式,这将为未来的你和你的同事节省大量时间。

通过遵循这些原则和最佳实践,你将能够构建出高质量的 Salesforce 应用程序,充分发挥平台潜力,为最终用户提供卓越的体验。

评论

此博客中的热门博文

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

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

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