首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Salesforce 编程语言 Apex 概述

Salesforce 编程语言 Apex 概述

原创
作者头像
编程小妖女
发布2025-09-11 23:26:17
发布2025-09-11 23:26:17
810
举报
文章被收录于专栏:后端开发后端开发

在 Salesforce 生态里,那个被普遍称为平台内核脚本的家伙,名叫 Apex。它是 Salesforce 自主设计并持续演进的一门语言:强类型、面向对象,语法接近 Java,却运行在 Salesforce 多租户云端执行环境,由平台统一编译、部署与执行。Apex 的角色并不是通用计算语言,而是围绕 CRM 数据与事务控制的业务逻辑语言,用来在平台上执行流程控制、数据持久化、权限校验、集成调用等任务。官方开发者指南把这种定位说得很清楚:Apex 允许开发者在 Salesforce 服务器上执行流程与事务控制语句,并与 API 调用配合,常被用于自动化业务过程与扩展平台行为。(Salesforce Developers, Salesforce Documentation)

Apex 的运行范式与传统 IaaS 或本地运行完全不同。代码被托管在 Salesforce 服务器,执行与数据库、对象模型、权限体系深度耦合,整个生命周期由平台治理,并受一组所谓的 Governor Limits 约束,例如单个事务内 SOQL 语句的数量、CPU 时间、堆内存、并发长事务配额等,这些限制的本质目的是保护多租户环境中的公平性与稳定性。(Salesforce Developers)

这门语言到底长什么样

如果你熟悉 Java 或 C#,看 Apex 会有很强的亲切感:有类、有接口、有泛型风格的集合类型,也有异常处理和注解式编程模型。与 SQL 打交道时,Apex 使用两套查询语言:SOQL 与 SOSL。SOQL 的语义与 SQL 的 SELECT 接近,但针对 Salesforce 对象模型定制;SOSL 则面向全文检索与跨对象搜索。官方文档展示了语法、子句与性能注意事项,适合把它当作 CRM 模型的查询 DSL 来理解。(Salesforce Developers)

在哪些场景必须用 Apex

1)触发器:围绕数据变更的细粒度钩子

当你希望在记录被插入、更新、删除或恢复时自动执行业务规则,就需要 Apex Trigger。触发器可以定义 beforeafter 两类时机,用于校验、衍生字段、级联更新、写审计日志、调用外部系统等。Salesforce 还定义了详细的执行顺序,从系统校验、流程自动化,到触发器与提交阶段的关系,都有严格规则。(Salesforce Developers)

2)面向 UI 的控制器:Visualforce 与 LWC 背后的服务端逻辑

在较早的 Visualforce 技术栈里,Apex 充当控制器或控制器扩展,负责把页面事件与数据访问粘合起来;对于现代的 Lightning Web Components,Apex 通过 @AuraEnabled 注解暴露为可调用的服务方法,既支持通过 @wire 的响应式数据绑定,也支持命令式调用与缓存控制。这让前端组件能够以安全、受治理的方式访问平台数据与业务能力。(Salesforce Developers)

3)平台级异步作业:批量处理、排队、定时、事务外操作

当逻辑会跨越大量记录、涉及外部 HTTP 调用、或需要延时执行时,Apex 提供一整套异步模型:Queueable 排队作业支持链式调度与复杂参数;Batch Apex 让你以批次方式处理超大数据集;Schedulable 支持基于 CRON 的定时;此外还可以使用可调用的异步方式把耗时任务移出同步事务。官方指南对这些模型的选择与约束有系统说明。(Salesforce Developers, Salesforce Ben)

4)对外集成:把 Apex 类暴露为 REST 或 SOAP 服务

Apex 能直接暴露为 REST 资源或 SOAP Web Service,通过注解声明端点、方法与 HTTP 动作,快速把平台能力安全地开放给外围系统。相较在中间件里再二次封装,这条路径能复用平台的权限、审计与事务能力。(Salesforce Developers)

5)测试与可维护性:平台内建的单元测试框架

Salesforce 强制要求变更需要测试覆盖,Apex 自带测试框架与隔离机制,支持注解式的测试类与测试方法、Mock 外部调用、以及事务回滚保证幂等。对多租户平台而言,这种内建测试是保护性工程实践的关键一环。(Salesforce Developers)

什么时候不该用 Apex

当标准配置、Flow 自动化或声明式工具足以覆盖需求时,不必为了写代码而写代码。Apex 的场景应聚焦在声明式方式无法覆盖、或需要高复杂度业务编排与可控事务边界的地方。触发器最佳实践也提倡把复杂逻辑下沉到可复用的类与服务中,通过单一触发器加处理器的模式来保持可维护性与可测试性。(Salesforce Developers)


可运行的示例:一组相互协作的 Apex 代码片段

下面给出一个小而完整的演示,覆盖触发器、触发器处理器、异步队列、批处理、定时作业、REST 端点与单元测试。示例业务设定为:当 Opportunity 成交时,自动创建一个后续 Task,并异步调用外部评分服务把客户打分回写;同时提供一个 REST 端点给外部系统查询评分;每日还有一个批处理清理过期评分记录。所有字符串均使用单引号,避免英文双引号,以满足格式限制。

触发器:监听 Opportunity 更新

代码语言:apex
复制
trigger OpportunityTrigger on Opportunity (after update) {
    if (Trigger.isAfter && Trigger.isUpdate) {
        OpportunityTriggerHandler.handleAfterUpdate(Trigger.new, Trigger.oldMap);
    }
}

触发器处理器:最小化 DML 与 SOQL,遵循批量化

代码语言:apex
复制
public class OpportunityTriggerHandler {
    public static void handleAfterUpdate(List<Opportunity> news, Map<Id, Opportunity> oldMap) {
        List<Task> tasks = new List<Task>();
        List<Id> toScore = new List<Id>();
        for (Opportunity o : news) {
            Opportunity oldO = oldMap.get(o.Id);
            Boolean turnedWon = (o.StageName == 'Closed Won') && (oldO.StageName != 'Closed Won');
            if (turnedWon) {
                tasks.add(new Task(
                    WhatId = o.Id,
                    Subject = 'Post-Sale Follow-up',
                    Status  = 'Not Started',
                    Priority = 'Normal'
                ));
                toScore.add(o.AccountId);
            }
        }
        if (!tasks.isEmpty()) {
            insert tasks;
        }
        if (!toScore.isEmpty()) {
            System.enqueueJob(new AccountScoringJob(toScore));
        }
    }
}

Queueable 作业:异步调用外部评分服务并回写 Account

代码语言:apex
复制
public class AccountScoringJob implements Queueable, Database.AllowsCallouts {
    private List<Id> accountIds;
    public AccountScoringJob(List<Id> ids) { this.accountIds = ids; }

    public void execute(QueueableContext ctx) {
        List<Account> accts = [SELECT Id, Name FROM Account WHERE Id IN :accountIds];
        for (Account a : accts) {
            HttpRequest req = new HttpRequest();
            req.setMethod('GET');
            req.setEndpoint('callout:ScoringService/scores?accountId=' + a.Id);
            Http http = new Http();
            HttpResponse res = http.send(req);
            if (res.getStatusCode() == 200) {
                // 假设响应是簡單的分值,例如 87
                Integer score = Integer.valueOf(res.getBody());
                a.put('Customer_Score__c', score);
            }
        }
        if (!accts.isEmpty()) {
            update accts;
        }
    }
}

批处理:每天清理 90 天未更新的评分

代码语言:apex
复制
global class ScoreCleanupBatch implements Database.Batchable<SObject>, Schedulable {
    global Database.QueryLocator start(Database.BatchableContext bc) {
        return Database.getQueryLocator(
            'SELECT Id, Customer_Score__c, LastModifiedDate FROM Account ' +
            'WHERE Customer_Score__c != NULL AND LastModifiedDate < LAST_N_DAYS:90'
        );
    }
    global void execute(Database.BatchableContext bc, List<SObject> scope) {
        List<Account> accs = (List<Account>)scope;
        for (Account a : accs) { a.put('Customer_Score__c', null); }
        if (!accs.isEmpty()) { update accs; }
    }
    global void finish(Database.BatchableContext bc) {}
    global void execute(SchedulableContext sc) {
        Database.executeBatch(new ScoreCleanupBatch(), 200);
    }
}

定时调度示例(可在 Execute Anonymous 里运行)

代码语言:apex
复制
// 每天凌晨 2:15 运行
System.schedule('Daily Score Cleanup', '0 15 2 * * ?', new ScoreCleanupBatch());

REST 端点:供外部系统查询评分

代码语言:apex
复制
@RestResource(urlMapping='/score/*')
global with sharing class ScoreApi {
    @HttpGet
    global static String getScore() {
        Id accountId = RestContext.request.params.get('accountId');
        Account a = [SELECT Id, Customer_Score__c FROM Account WHERE Id = :accountId LIMIT 1];
        return String.valueOf(a.get('Customer_Score__c'));
    }
}

测试代码:覆盖触发器、异步与 REST

代码语言:apex
复制
@IsTest
private class DemoTests {
    @IsTest
    static void testClosedWonFlow() {
        // 构造账户与商机
        Account acc = new Account(Name='Acme');
        insert acc;
        Opportunity o = new Opportunity(Name='Deal', StageName='Prospecting', CloseDate=Date.today().addDays(10), AccountId=acc.Id);
        insert o;

        // 模拟外部评分服务
        Test.setMock(HttpCalloutMock.class, new SimpleScoreMock(88));

        Test.startTest();
        o.StageName = 'Closed Won';
        update o; // 触发器将插入 Task 并排队评分作业
        Test.stopTest(); // 等待异步完成

        // 验证评分已写回
        acc = [SELECT Customer_Score__c FROM Account WHERE Id = :acc.Id];
        System.assertEquals(88, acc.get('Customer_Score__c'));
    }

    // 简单的 HTTP 模拟
    private class SimpleScoreMock implements HttpCalloutMock {
        Integer score;
        SimpleScoreMock(Integer s) { score = s; }
        public HTTPResponse respond(HTTPRequest req) {
            HttpResponse res = new HttpResponse();
            res.setStatusCode(200);
            res.setBody(String.valueOf(score));
            return res;
        }
    }

    @IsTest
    static void testRestApi() {
        Account a = new Account(Name='Beta');
        a.put('Customer_Score__c', 73);
        insert a;

        RestRequest req = new RestRequest();
        req.requestUri = '/services/apexrest/score?accountId=' + a.Id;
        req.httpMethod = 'GET';
        RestContext.request = req;
        RestContext.response = new RestResponse();

        String body = ScoreApi.getScore();
        System.assertEquals('73', body);
    }
}

这组代码体现了 Apex 的典型工程化实践:在触发器里只做路由,不直接写复杂逻辑;批量化处理集合;外部调用放入 Queueable 异步,避免阻塞事务;通过批处理与定时作业维护数据健康;用内建测试框架隔离与校验。异步与队列的选择、限制与监控方法,可以在官方文档里查到细节,包括如何链式队列、何时允许 Callout、以及不同模型的适用场景。(Salesforce Developers)


开发者最关心的两件事:数据访问与平台约束

面向数据的 DSL

所有与数据交互的读写,都建议用 SOQL、SOSL 与 DML 语句完成。SOQL 的 SELECT ... FROM ... WHERE ... 思路与 SQL 相近,语法却与对象模型绑定,包含关系查询、聚合、子查询、选择性过滤等;SOSL 用于跨对象的全文检索。理解它们与对象权限、字段级权限的协作,是写出安全代码的关键。(Salesforce Developers)

多租户下的资源配额

Governor Limits 是平台秩序的守门人。它强制你在设计时考虑批量化、缓存、异步解耦与最少化 SOQL/DML 的模式。例如触发器最佳实践页面强调,用集合合并 DML 操作、避免循环里查询与写入、把重活交给异步。监控与调优这部分,建议在沙盒里用日志与限制检查辅助验证。(Salesforce Developers)


把 Apex 放在方法论里看

从工程角度看,Apex 既是语言,也是运行时契约。语言层面提供熟悉的面向对象与注解模型;运行时层面与 Salesforce 的对象、权限、审计、自动化、集成栈紧密耦合。正因为这种耦合,Apex 才适合落地那些需要强事务一致性、需要尊重平台共享资源约束、以及需要借力平台安全边界的场景。它不是要取代 Java、Python 那类通用语言,而是把 CRM 核心域的复杂性收束在一个可治理的环境里。对于前端的 LWC,Apex 成为可被 @AuraEnabled 暴露的服务端 API;对于集成,则能以注解快速暴露 REST 或 SOAP 服务;对于批处理与延迟任务,平台的异步能力提供了低维护成本的解法。(Salesforce Developers)


延伸资料与权威入口

想进一步系统化学习,建议从以下官方入口出发:Apex 开发者指南的总目录、Apex 参考手册、以及 SOQL 与 SOSL 指南。这些文档持续更新,覆盖语言特性、API、限制、优化实践、以及版本演进。(Salesforce Developers)


一句话回看主题

Salesforce 自研的 Apex 语言,生来就为平台内的业务自动化、数据访问、事务控制与外部集成而生,通过触发器、控制器、异步作业与 Web 服务四大支点,构成了 CRM 应用扩展与定制的骨架;它依赖 SOQL/SOSL 操作数据,遵循 Governor Limits 的资源契约,并借助内建测试框架确保可演进的质量。这样一门语言,最大的价值不是让你写出更酷的语法,而是在多租户、强约束的云平台里,把复杂业务安全而稳定地跑起来。(Salesforce Developers)

undefined

undefined

undefined

undefined

undefined

undefined

undefined

undefined

undefined

undefined

undefined

undefined

undefined

undefined

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 这门语言到底长什么样
  • 在哪些场景必须用 Apex
    • 1)触发器:围绕数据变更的细粒度钩子
    • 2)面向 UI 的控制器:Visualforce 与 LWC 背后的服务端逻辑
    • 3)平台级异步作业:批量处理、排队、定时、事务外操作
    • 4)对外集成:把 Apex 类暴露为 REST 或 SOAP 服务
    • 5)测试与可维护性:平台内建的单元测试框架
  • 什么时候不该用 Apex
  • 可运行的示例:一组相互协作的 Apex 代码片段
  • 开发者最关心的两件事:数据访问与平台约束
  • 把 Apex 放在方法论里看
  • 延伸资料与权威入口
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档