Apex 开发入门
欢迎阅读 Apex 开发者指南。本章将带你了解 Apex 开发生命周期,并通过分步教程学习如何创建 Apex 类和触发器,并将其部署到生产环境中。Apex 是第一款多租户、按需运行的编程语言,专为希望构建下一代企业级应用的开发者而设计。Apex 彻底改变了开发者创建按需应用的方式。
Apex 简介
Apex 是一种强类型、面向对象的编程语言,允许开发者在 Salesforce 服务器上执行流程和事务控制语句,并结合 API 调用。Apex 使用看起来像 Java 的语法,行为类似于数据库存储过程,使开发者能够将业务逻辑添加到大多数系统事件中,包括按钮点击、相关记录更新和 Visualforce 页面。Apex 代码可以通过 Web 服务请求和对象上的触发器来启动。
虽然 Salesforce 用户界面提供了许多自定义选项,例如定义新字段、对象、工作流和审批流程,但开发者也可以使用 SOAP API 从客户端程序发出数据操作命令(如 delete()、update() 或 upsert())。这些客户端程序通常使用 Java、JavaScript、.NET 或其他编程语言编写,为企业的自定义开发提供了更大的灵活性。然而,由于这些客户端程序的控制逻辑不在 Salesforce 服务器上,它们在执行常见业务事务时受到多次往返 Salesforce 站点的性能成本限制,同时也受到托管服务器代码(如 Java 或 .NET)的成本和复杂性限制。
什么是 Apex?
Apex 是一种强类型、面向对象的编程语言,允许开发者在 Salesforce 服务器上执行流程和事务控制语句,并结合 API 调用。使用看起来像 Java 的语法,行为类似于数据库存储过程,Apex 使开发者能够将业务逻辑添加到大多数系统事件中,包括按钮点击、相关记录更新和 Visualforce 页面。Apex 代码可以通过 Web 服务请求和对象上的触发器来启动。
Apex 语言的八大特征
作为一门编程语言,Apex 具有以下八个核心特征:
1. 集成性(Integrated)
Apex 为常见的 Lightning Platform 惯用模式提供了内置支持,包括:
- DML 操作:内置的 INSERT、UPDATE 和 DELETE 等数据操作语言调用,包含内置的 DmlException 异常处理
- 内联查询:内联的 Salesforce 对象查询语言(SOQL)和 Salesforce 对象搜索语言(SOSL)查询,返回 sObject 记录列表
- 批量处理:支持循环批量处理多条记录
- 锁定机制:防止记录更新冲突的锁定语法
- 自定义 API:可以从存储的 Apex 方法构建自定义公共 API 调用
- 引用保护:当用户尝试编辑或删除被 Apex 引用的自定义对象或字段时,会发出警告和错误
2. 易用性(Easy to Use)
Apex 基于熟悉的 Java 习惯用法,包括变量和表达式语法、代码块和条件语句语法、循环语法、对象和数组表示法。在引入新元素时,Apex 使用易于理解并鼓励高效利用 Lightning Platform 的语法和语义。因此,Apex 编写的代码既简洁又易于编写。
3. 数据聚焦(Data Focused)
Apex 的设计目标是将多个查询和 DML 语句串联成 Salesforce 服务器上的一个工作单元。这与数据库存储过程将多个事务语句串联在数据库服务器上的方式类似。与其他数据库存储过程一样,Apex 并不试图为用户界面中的元素渲染提供通用支持。
4. 严谨性(Rigorous)
Apex 是一种强类型语言,使用对模式对象(如对象和字段名称)的直接引用。如果任何引用无效,它会在编译时快速失败。它将所有自定义字段、对象和类依赖项存储在元数据中,以确保它们在活跃的 Apex 代码需要时不会被删除。
5. 托管性(Hosted)
Apex 完全由 Lightning Platform 进行解释、执行和控制。
6. 多租户感知(Multitenant Aware)
与 Lightning Platform 的其他部分一样,Apex 在多租户环境中运行。Apex 运行时引擎被设计为严密防范失控代码,防止其独占共享资源。任何违反限制的代码都会以易于理解的错误消息提示失败。
7. 易于测试(Easy to Test)
Apex 为单元测试的创建和执行提供了内置支持。它包括指示代码覆盖率以及代码中哪些部分可以更高效的测试结果。Salesforce 通过在平台升级之前执行所有单元测试,确保所有自定义 Apex 代码按预期工作。
8. 版本化(Versioned)
你可以将 Apex 代码保存为针对不同版本的 API。这使你能够维护代码行为。Apex 包含在 Performance Edition、Unlimited Edition、Developer Edition、Enterprise Edition 和 Database.com 中。
理解 Apex 核心概念
Apex 代码通常包含许多你在其他编程语言中熟悉的内容。本节介绍 Apex 的基本功能以及一些核心概念。
使用版本设置
在 Salesforce 用户界面中,你可以指定保存 Apex 类或触发器所使用的 Salesforce API 版本。此设置不仅指示要使用的 SOAP API 版本,还指示要使用的 Apex 版本。你可以在保存后更改版本。每个类或触发器名称必须唯一,不能将同一个类或触发器保存为不同版本。
你还可以使用版本设置将类或触发器与从 AppExchange 安装到组织中的托管包的特定版本关联起来。如果安装了更高版本的托管包,该类或触发器将继续使用此版本,除非你手动更新版本设置。要添加已安装的托管包到设置列表,请从可用包列表中选择一个包。该列表仅在你已安装的托管包尚未与类或触发器关联时显示。
变量、方法和类的命名规则
在命名变量、方法或类时,不能使用任何 Apex 保留关键字。这些包括属于 Apex 和 Lightning Platform 的词汇,如 list、test 或 account,以及 Apex 参考文档中记录的其他保留关键字。建议遵循 Java 命名约定:类名使用 PascalCase(大驼峰),方法和变量名使用 camelCase(小驼峰),并选择描述性强、有意义的名称。
使用变量和表达式
Apex 是一种强类型语言,也就是说,你必须在首次引用变量时声明其数据类型。Apex 数据类型包括基本类型(如 Integer、Date 和 Boolean),以及更高级的类型(如列表、映射、对象和 sObject)。
变量使用名称和数据类型进行声明。你可以在声明变量时为其赋值,也可以稍后再赋值。声明变量时使用以下语法:
datatype variable_name [ = value];
注意:代码语句末尾的分号不是可选的。你必须用分号结束所有语句。
以下是一些变量声明的示例:
// 以下变量数据类型为 Integer,名称为 Count,值为 0
Integer Count = 0;
// 以下变量数据类型为 Decimal,名称为 Total
// 注意:尚未为其赋值
Decimal Total;
// 以下变量是一个 Account,也称为 sObject
Account MyAcct = new Account();
值传递 vs 引用传递
在 Apex 中,所有原始数据类型参数(如 Integer 或 String)都是通过值传递到方法中的。这意味着对参数的任何更改仅存在于方法的范围内。当方法返回时,对这些参数的更改将丢失。
非原始数据类型参数(如 sObject)则是通过引用传递到方法中的。因此,当方法返回时,传入的参数仍然引用与方法调用之前相同的对象。在方法内部,引用不能更改为指向另一个对象,但对象字段的值可以被更改。这一区别对于理解方法调用中的副作用至关重要。
使用语句
语句是执行操作的任何编码指令。在 Apex 中,语句必须以分号结尾,可以是以下类型之一:
- 赋值语句:将值赋给变量
- 条件语句(if-else)
- 循环语句:Do-while、While、For
- 锁定语句
- 数据操作语言(DML)
- 事务控制
- 方法调用
- 异常处理
代码块是用花括号 {} 分组的一系列语句,可以在任何允许使用单个语句的地方使用。
代码块示例
多语句代码块(需要花括号):
if (true) {
System.debug(1);
System.debug(2);
} else {
System.debug(3);
System.debug(4);
}
单语句代码块(花括号可选):
if (true)
System.debug(1);
else
System.debug(2);
使用集合
Apex 有以下三种类型的集合:
List(列表)
List 是一组元素的集合,如整数、字符串、对象或其他集合。当元素的顺序很重要时使用 List。List 中可以有重复的元素。List 中的第一个索引位置始终为 0。
创建 List 的语法:
List<datatype> list_name
[= new List<datatype>();] |
[= new List<datatype>{value1, value2...};]
示例:
List<Integer> My_List = new List<Integer>();
Set(集合)
Set 是一组唯一、无序的元素集合。它可以包含原始数据类型(如 String、Integer、Date 等),也可以包含更复杂的数据类型(如 sObject)。Set 中的每个元素必须唯一。
创建 Set 的语法:
Set<datatype> set_name
[= new Set<datatype>();] |
[= new Set<datatype>{value1, value2...};]
示例:
Set<String> My_String = new Set<String>{'a', 'b', 'c'};
Map(映射)
Map 是键值对的集合。键可以是任何原始数据类型。值可以包括原始数据类型,以及对象和其他集合。当需要通过键来查找内容时使用 Map。Map 中可以有重复的值,但每个键必须是唯一的。
创建 Map 的语法:
Map<key_datatype, value_datatype> map_name
[= new Map<key_datatype, value_datatype>();] |
[= new Map<key_datatype, value_datatype>
{key1 => value1, key2 => value2...};]
示例:
Map<Integer, String> My_Map =
new Map<Integer, String>{1 => 'a', 2 => 'b', 3 => 'c'};
使用分支
if 语句是一种真-假测试,它使你的应用程序能够根据条件执行不同的操作。基本语法如下:
if (Condition) {
// 如果条件为真,执行此代码
} else {
// 如果条件不为真,执行此代码
}
条件必须计算为布尔值。else 分支是可选的。你可以使用 else if 链接多个条件,并支持嵌套 if 语句。
使用循环
if 语句使你的应用程序能够根据条件执行不同操作,而循环则告诉你的应用程序根据条件重复执行相同的操作。Apex 支持以下三种类型的循环:
- Do-While 循环:在代码执行后检查条件,确保代码块至少执行一次
- While 循环:在代码执行前检查条件,如果条件初始为 false,则可能一次也不执行
- For 循环:允许更精细地控制循环条件。Apex 既支持传统的 For 循环(设置起始、结束和增量条件),也支持使用列表和 SOQL 查询作为条件的 For 循环
何时应该使用 Apex?
Salesforce 提供了自定义预构建应用以适应企业需求的能力。对于复杂的业务流程,你可以使用各种工具(包括 Apex 和 Lightning Components)来实现自定义功能和用户界面。
Apex 的典型使用场景
在以下情况下使用 Apex:
- 创建 Web 服务
- 创建电子邮件服务
- 跨多个对象执行复杂验证
- 创建 Flow Builder 不支持的复杂业务流程
- 创建自定义事务逻辑(在整个事务范围内而不仅仅是单个记录或对象上执行的逻辑)
- 将自定义逻辑附加到另一个操作上(例如保存记录),以便无论该操作来源于用户界面、Visualforce 页面还是 SOAP API,逻辑都会在操作执行时触发
Lightning Components
开发 Lightning 组件以自定义 Lightning Experience、Salesforce 移动应用,或构建自己的独立应用。你还可以使用开箱即用的组件来加快开发速度。从 Spring '19(API 版本 45.0)开始,你可以使用两种编程模型构建 Lightning 组件:Lightning Web Components 模型和原始的 Aura Components 模型。
Lightning Web Components(LWC)是使用 HTML 和现代 JavaScript 构建的自定义 HTML 元素。Lightning Web Components 和 Aura Components 可以在一个页面上共存和互操作。管理员和最终用户不知道组件是使用哪种编程模型开发的——对他们来说,它们就是 Lightning 组件。
我们推荐使用 Lightning Web Components(LWC)模型来创建自定义用户界面。LWC 遵循 W3C Web 标准,你可以使用标准的 JavaScript 语法构建和打包组件。通过 LWC,你可以使用 Apex 和 Lightning Data Service 轻松处理 Salesforce 数据。
Visualforce 与 SOAP API
Visualforce 由基于标签的标记语言组成,为开发者提供了一种更强大的方式来构建应用和自定义 Salesforce 用户界面。使用 Visualforce,你可以:
- 构建向导和其他多步骤流程
- 创建自定义的应用程序流控制
- 定义导航模式和数据特定规则,以实现最佳、高效的应用交互
SOAP API 适用于当你想要向一次只处理一种记录类型且不需要任何事务控制(如设置保存点或回滚更改)的组合应用添加功能时,使用标准的 SOAP API 调用。
工具选择指南:
- Apex —— 服务器端业务逻辑
- Lightning Components —— 现代用户界面
- Visualforce —— 自定义页面布局和向导
- SOAP API —— 外部应用集成
Apex 如何工作?
所有 Apex 完全在 Lightning Platform 上按需运行。开发者编写 Apex 代码并将其保存到平台,最终用户通过用户界面触发 Apex 代码的执行。Apex 在 Lightning Platform 上完成编译、存储和运行的全过程。
Apex 执行模型
编译阶段(开发者保存代码时):
- 开发者编写 Apex 代码并保存到平台
- 平台应用服务器首先将代码编译为一组抽象的指令,这些指令可以被 Apex 运行时解释器理解
- 然后将这些指令保存为元数据
执行阶段(最终用户触发 Apex 时):
- 最终用户触发 Apex 的执行——可能是通过点击按钮或访问 Visualforce 页面
- 平台应用服务器从元数据中检索已编译的指令
- 将这些指令通过运行时解释器处理后返回结果
最终用户在执行时间上与标准平台请求相比没有任何差异。
在云端开发代码
Apex 编程语言在云端——多租户平台中——保存和运行。Apex 专为平台上的数据访问和数据操作而定制,使你能够向系统事件添加自定义业务逻辑。虽然它为自动化平台上的业务流程提供了许多好处,但它并不是一种通用编程语言。
Apex 的限制
Apex 不能用于以下操作:
- 渲染用户界面元素 —— 除了错误消息之外,不能用于 UI 渲染
- 更改标准功能 —— Apex 只能阻止功能发生或添加额外功能,无法修改标准行为
- 创建临时文件
- 生成线程
这些限制反映了 Apex 作为基于云的多租户编程语言的设计定位——专注于业务逻辑而非通用计算。
Governor Limits(调控器限制)与批量处理
所有 Apex 代码都在 Lightning Platform 上运行,这是一个由所有其他组织共享的资源。为了保证一致的性能和可扩展性,Apex 的执行受到 Governor Limits 的约束,确保没有单个 Apex 执行会影响 Salesforce 的整体服务质量。这意味着所有 Apex 代码在单个流程中可以执行的操作数量(如 DML 或 SOQL)是有限的。
批量处理要求:
- 所有 Apex 请求返回一个包含 1 到 50,000 条记录的集合
- 你不能假设你的代码只对一条记录进行操作
- 必须实现考虑批量处理的编程模式
- 如果不这样做,可能会触碰到 Governor Limits
因此,在设计所有 Apex 代码时都必须考虑处理多条记录的能力——这被称为"Bulkification"(批量化的设计原则)。
总结
通过本章的学习,我们掌握了以下核心内容:
- Apex 的定义:一种强类型、面向对象的编程语言,语法类似 Java,行为类似数据库存储过程
- 八大语言特征:集成性、易用性、数据聚焦、严谨性、托管性、多租户感知、易于测试、版本化
- 核心编程概念:版本设置、命名规则、变量和表达式(值传递 vs 引用传递)、语句、集合(List、Set、Map)、分支和循环
- 工具选择:Apex 用于服务器端业务逻辑,Lightning Components(推荐 LWC)用于现代 UI,Visualforce 用于自定义页面,SOAP API 用于外部集成
- 执行模型:从编译到执行完全在 Lightning Platform 上进行,最终用户感受不到任何性能差异
- 云端开发的现实:理解 Apex 的约束限制以及 Governor Limits 和批量处理模式的至关重要性
以上知识为你后续深入学习 Apex 开发奠定了坚实的基础。
感谢阅读本指南。如需更详细的信息,请参阅 Apex 开发者指南后续章节。