Appearance
子程序(Subroutine)开发
一、子程序概要
- 核心作用:封装可重用代码逻辑,提升代码维护性
- 执行方式:通过
PERFORM
语句显式调用 - 存储位置:在程序本地或全局包含文件中定义
- 生命周期:仅在主程序运行时有效
二、子程序定义语法
abap
FORM <subroutine_name> [USING ... CHANGING ...].
" 业务逻辑
ENDFORM.
三、参数传递类型
参数类型 | 内存操作方式 | 修改影响范围 | 适用场景 |
---|---|---|---|
USING 传值 | 值传递 | 局部变量副本 | 输入型参数保护原始数据(如查询条件、只读配置) |
USING VALUE | 值传递 | 局部变量副本 | 输入型参数显式声明值传递(强调不修改原值,常用于简单类型参数) |
CHANGING | 引用传递 | 直接修改原变量 | 需要返回处理结果(如计算结果、状态更新) |
RETURNING | 值传递 | 返回新变量 | 函数方法返回值(单值输出场景,需配合EXPORTING 使用) |
1.USING 传值:
abap
METHODS: calculate_price
IMPORTING
iv_base TYPE p DECIMALS 2. " 值传递,原始数据保护
" 调用时原值不受影响
DATA(lv_base) = '100.00'.
calculate_price( iv_base = lv_base ). " lv_base 值仍为 100.00
2.USING VALUE 显式值传递:
abap
METHODS: format_data
IMPORTING
VALUE(iv_text) TYPE string. " 显式声明值传递
" 方法内部修改不影响外部变量
DATA(lv_input) = 'Original'.
format_data( iv_text = lv_input ). " lv_input 保持 'Original'
3.CHANGING 引用传递:
abap
METHODS: update_status
CHANGING
cv_status TYPE char1. " 引用传递,直接修改原变量
" 调用后原变量被修改
DATA(lv_status) = 'A'.
update_status( CHANGING cv_status = lv_status ). " lv_status 可能变为 'B'
4.RETURNING 返回值:
abap
METHODS: get_tax_rate
RETURNING
VALUE(rv_rate) TYPE p DECIMALS 2. " 单值返回
" 直接接收返回值
DATA(lv_tax) = get_tax_rate( ).
5.性能与安全对比:
维度 | USING 传值(值传递) | CHANGING(引用传递) | RETURNING(返回值) |
---|---|---|---|
内存占用 | 较高(创建数据副本) | 低(仅传递内存地址引用) | 中等(返回新创建的值或结构) |
数据安全 | 高(原始数据不受修改影响) | 低(直接操作原始数据) | 高(返回独立的新值,不影响输入参数) |
适用数据量 | 小规模数据(如基本类型、简单结构) | 大数据对象(如内表、复杂结构体) | 单值或简单结构(如函数结果返回值) |
6.示例代码:
abap
FORM calculate_tax USING iv_amount TYPE p
CHANGING cv_tax TYPE p.
cv_tax = iv_amount * '0.13'. " 13%税率计算
ENDFORM.
四、子程序调用方式
abap
" 常规调用
PERFORM calculate_total USING 1000 CHANGING lv_total.
" 动态调用(需异常处理)
DATA(lv_form) = 'CALCULATE_TAX'.
PERFORM (lv_form) IN PROGRAM sy-repid IF FOUND.
" 条件调用
IF lv_condition = abap_true.
PERFORM validate_data.
ENDIF.
五、异常处理
1. 异常分类体系
异常类型 | 典型场景 | 对应类名 |
---|---|---|
算术运算异常 | 除零错误(如分母为0的除法运算) 数值溢出(超出数据类型范围的计算结果) | CX_SY_ARITHMETIC_ERROR |
数据类型异常 | 非法类型转换(如字符串转数值失败) 不兼容数据操作(文本与数字直接运算) | CX_SY_CONVERSION_ERROR |
动态调用异常 | 调用不存在的方法(动态方法名错误) 参数类型/数量不匹配(动态传参错误) | CX_SY_DYN_CALL_ERROR |
资源访问异常 | 数据库锁冲突(并发事务导致锁定) 文件IO错误(路径无效/权限不足) | CX_SY_OPEN_SQL_ERROR |
算术运算异常
当程序执行除法运算且分母为0时,会触发CX_SY_ARITHMETIC_ERROR
。数值溢出常见于大整数运算超出i
/p
类型范围数据类型异常
强制转换非法字符到数值类型(如MOVE 'ABC' TO lv_number
)将抛出CX_SY_CONVERSION_ERROR
动态调用异常
使用CALL METHOD
动态调用未定义方法时,系统抛出CX_SY_DYN_CALL_ERROR
,参数校验失败也属于此类别资源访问异常
数据库操作中出现锁冲突(如ENQUEUE
失败)或文件打开失败(如路径不存在),触发CX_SY_OPEN_SQL_ERROR
2. 异常处理语法结构
abap
TRY.
" 可能抛出异常的代码
DATA(lv_result) = 1 / 0.
CATCH cx_sy_arithmetic_error INTO DATA(lx_error).
WRITE: / '错误代码:', lx_error->get_text( ).
CATCH cx_sy_dyn_call_error INTO DATA(lx_dyn_error).
WRITE: / '动态调用错误:', lx_dyn_error->error_message.
CLEANUP.
" 资源释放代码
ENDTRY.
"➊ 函数模块异常定义
FUNCTION Z_MATERIAL_UPDATE.
EXCEPTIONS:
material_not_found = 1
quantity_error = 2
OTHERS = 3.
"➋ 调用时异常捕获
CALL FUNCTION 'Z_MATERIAL_UPDATE'
EXPORTING
iv_matnr = lv_matnr
EXCEPTIONS
material_not_found = 1
OTHERS = 3.
IF sy-subrc <> 0.
CASE sy-subrc.
WHEN 1.
MESSAGE e001(zmm) WITH '物料不存在'.
WHEN 3.
MESSAGE e002(zmm) WITH '未知错误'.
ENDCASE.
ENDIF.
3. 传统异常处理(兼容旧版本)
abap
CATCH SYSTEM-EXCEPTIONS arithmetic_errors = 4.
DATA(lv_value) = 1 / 0.
ENDCATCH.
IF sy-subrc = 4.
WRITE: / '捕获到算术错误'.
ENDIF.
4.高级异常处理技巧
自定义异常类
abap
CLASS cx_custom_error DEFINITION INHERITING FROM cx_static_check.
PUBLIC SECTION.
METHODS:
constructor IMPORTING iv_msg TYPE string,
get_text REDEFINITION.
ENDCLASS.
METHOD get_text.
result = me->msg.
ENDMETHOD.
异常传播机制
abap
METHOD process_data.
TRY.
validate_input( ).
CATCH cx_input_error INTO DATA(lx_error).
RAISE EXCEPTION TYPE cx_application_error
EXPORTING
previous = lx_error.
ENDMETHOD.
全局异常处理器
abap
CLASS global_handler DEFINITION.
PUBLIC SECTION.
CLASS-METHODS:
handle_exceptions FOR EVENT if_oo_adt_classrun~main
IMPORTING sender.
ENDCLASS.
METHOD handle_exceptions.
" 统一处理未捕获异常
ENDMETHOD.
六、子程序结束控制
1.子程序终止机制基本原理,标准终止流程
abap
FORM <subroutine_name>.
"业务逻辑代码
[...]
"程序执行到ENDFORM自动终止
ENDFORM.
FUNCTION <function_module>.
[...]
"执行到ENDFUNCTION自动退出
ENDFUNCTION.
当执行流到达ENDFORM/ENDFUNCTION
时自动执行以下操作:
- 释放子程序内部局部变量内存
- 将CHANGING参数值回传主程序
- 更新SY-SUBRC系统字段状态
2.强制终止方法
abap
" 强制退出子程序
IF lv_error = abap_true.
EXIT. " 退出当前子程序
CHECK 1=0. " 条件退出
ENDIF.
3.参数传递与结束控制
1. 参数类型对终止的影响
- 传值参数(USING VALUE):终止时不回传修改值7
- 传址参数(CHANGING):终止时自动同步最新值7
2. 表参数特殊处理
abap
FORM process_data TABLES tt_data.
LOOP AT tt_data ASSIGNING <fs_data>.
IF <fs_data>-value IS INITIAL.
DELETE tt_data. "直接操作原内表
CONTINUE.
ENDIF.
[...]
ENDLOOP.
ENDFORM.
- 表参数始终采用传址方式
- 任何修改在子程序终止时立即生效
4.异常终止场景处理
1. 函数模块异常控制
abap
FUNCTION z_check_material.
[...]
IF sy-subrc <> 0.
RAISE material_not_found. "触发异常终止
ENDIF.
ENDFUNCTION.
"调用方处理
CALL FUNCTION 'Z_CHECK_MATERIAL'
EXCEPTIONS
material_not_found = 1
OTHERS = 2.
IF sy-subrc = 1.
[...] "异常处理逻辑
ENDIF.
2. CHECK 与 EXIT 对比
语句 | 作用域 | 执行特点 | 典型使用场景 |
---|---|---|---|
CHECK | 循环/程序块 | - 在循环中:条件不满足时跳过当前循环次,继续下一轮循环(类似CONTINUE )- 在程序块中:终止当前处理块(FORM/METHOD/EVENT) | 批量数据处理时跳过无效记录 在子程序中前置参数有效性校验 |
EXIT | 循环/程序块 | - 在循环中:立即终止当前层循环(嵌套循环仅退出当前层) - 在程序块中:终止当前处理块并返回调用点 | 发现致命错误时终止处理 多层循环中提前退出指定层级 |
abap
FORM validate_input.
CHECK sy-uname IS NOT INITIAL. "条件不满足直接终止
[...] "后续代码不会执行
ENDFORM.
FORM emergency_exit.
IF sy-datum > '20251231'.
EXIT. "立即终止子程序
ENDIF.
[...]
ENDFORM.
七、临时子程序(Local Subroutine)
可以定义在主内存中执行的动态子程序。 语法:
abap
GENERATE Subroutine POOL <itab> NAME <prog> .
上面语句有在执行中程序的主内存里创建子程序池的功能。将子程序池的代码插入到内表<itab>
中,创建8位类型为℃的子程序名<prog>
。即当需要使用动态的子程序时利用子程序池(程序),动态定义内表时经常使用此语句。例如,假设做一本根据用户指定的期间查询该期间销售额的程序。此时用户每次指定的日期是不一样的,因此无法事先预测内表的字段个数。这时用 GENERATE语句就可以根据日期数动态地定义内表,此外还有很多动态定义内表的方法,在此不一一列举。子程序池只存在于程序执行过程中,在一个程序中可以创建 36个子程序池。发生错误时SY-SUBRC返回8。
abap
REPORT Z04 14.
DATA:gt code(72)OCCURS 10,
gv_prog(8),
gv msg(120).
APPEND 'PROGRAM SUBPOOL.' TO gt_code.
APPEND 'FORM dynamic subr.' TO gt code.
APPEND"WRITE /"Dynamic Subroutine is called".' TO gt code
APPEND 'ENDFORM' TO gt code.
GENERATE SUBROUTINE POOL gt code NAME gv prog
MESSAGE gv msg
IF sy-subrc <> 0.
WRITE: /'Subroutine POOL is failed'.
ELSE.
WRITE: /'Subroutine POOL name : ', gv_prog.
SKIP 1.
PERFORM dynamic subr IN PROGRAM (gv_prog).
ENDIF.
Subroutine POOL name:%_T002S0
Dynamic Subroutine is called
八、PERFORM ON COMMIT 特殊用法
1. 功能概述
PERFORM ON COMMIT
是SAP ABAP中用于延迟执行子程序的特殊语法,主要应用于需要与数据库提交/回滚操作保持事务一致性的场景:ml-citation{ref="1,3" data="citationList"}。
核心特性:
- 将子程序执行与LUW(逻辑工作单元)绑定,仅当显式执行
COMMIT WORK
或ROLLBACK WORK
时触发 - 支持多层级事务控制(
COMMIT LEVEL n
) - 适用于需要与数据库操作保持原子性的业务逻辑:ml-citation
2. 语法结构
基础语法格式
abap
PERFORM <subroutine> [ON COMMIT|ON ROLLBACK] [LEVEL <n>]
3、执行逻辑
基础执行流程
abap
DATA: gs_data TYPE scarr.
PERFORM update_db ON COMMIT. "ⓘ 声明延迟执行
gs_data-carrid = 'AA'.
gs_data-connid = '0017'.
PERFORM update_params ON COMMIT. "ⓘ 第二个延迟任务
gs_data-connid = '0064'. "ⓘ 变量值会被覆盖
COMMIT WORK AND WAIT. "ⓘ 触发所有ON COMMIT子程序执行
变量覆盖说明:由于全局变量gs_data在COMMIT前被多次修改,最终执行时仅保留最后一次赋值结果
多层级事务控制
abap
PERFORM phase1 ON COMMIT LEVEL 1. "ⓘ 一级提交任务
PERFORM phase2 ON COMMIT LEVEL 2. "ⓘ 二级提交任务
COMMIT WORK LEVEL 1. "ⓘ 仅执行LEVEL<=1的子程序
4. 开发示例
增强开发中的数据一致性
abap
DATA: gt_log TYPE TABLE OF zaudit_log.
FORM update_data.
"ⓘ 主数据更新
UPDATE zmain_table SET field = lv_value WHERE id = lv_id.
"ⓘ 声明审计日志写入(延迟到COMMIT时执行)
PERFORM write_audit_log ON COMMIT.
ENDFORM.
FORM write_audit_log.
"ⓘ 实际写入操作(使用最终状态数据)
INSERT zaudit_log FROM TABLE gt_log.
ENDFORM.
事务完整性保障
abap
PERFORM delete_old_data ON COMMIT. "ⓘ 声明删除操作
PERFORM insert_new_data ON COMMIT. "ⓘ 声明插入操作
FORM delete_old_data.
DELETE FROM ztemp_table WHERE session = sy-uname.
ENDFORM.
FORM insert_new_data.
INSERT ztemp_table FROM TABLE gt_buffer.
ENDFORM.
IF lv_success = abap_true.
COMMIT WORK. "ⓘ 同时执行删除和插入
ELSE.
ROLLBACK WORK. "ⓘ 撤销所有操作
ENDIF.
5. 宏(Macro)概述
宏是 ABAP 语言中用于代码复用的预编译级模块化工具,通过文本替换机制简化重复逻辑的编写,提升代码紧凑性。
定义与语法
宏通过 DEFINE...END-OF-DEFINITION
语句定义,语法如下:
abap
DEFINE macro_name.
" 宏逻辑(如 &1 + &2)
END-OF-DEFINITION.
参数传递:使用 &1
, &2
等占位符接收调用时传入的参数,按顺序匹配
宏调用
直接通过宏名调用,参数以空格分隔:
abap
macro_name param1 param2.
核心特性
特性 | 说明 |
---|---|
编译时展开 | 宏在编译阶段直接替换为代码文本,无运行时开销,适用于高频简单逻辑 |
作用域限制 | 宏仅在其定义位置之后的代码中可用,且仅在当前程序内有效 |
参数类型 | 仅支持文本替换,无法处理复杂数据类型(如结构体、内表) |
特性详解
- 编译时展开:
宏在程序编译阶段通过文本替换实现逻辑复用,避免了运行时调用(如函数模块或子程序)的性能损耗。
适用于高频调用的简单操作(如字段计算、条件判断等),但对复杂逻辑的支持有限。
- 作用域限制:
宏的作用域受限于定义位置,只能在定义后的代码中被调用。
无法跨程序使用,且不支持嵌套定义(如宏内调用其他宏)。
- 参数类型:
宏通过
&1
,&2
等占位符接收参数,仅支持直接文本替换,无法传递结构化数据或动态变量。
宏的优缺点
维度 | 优点 | 缺点 |
---|---|---|
执行效率 | 无运行时调用开销,性能高 | 逻辑复杂度受限,仅适合简单场景 |
维护性 | 修改需重定义,全局影响代码 | 调试困难(错误定位到展开后的代码) |
可读性 | 减少冗余代码,提升简洁性 | 过度使用可能导致代码可读性下降 |
典型应用场景
- 重复代码简化:例如批量字段计算、格式化输出等高频操作。
- 性能敏感逻辑:对执行效率要求高的场景(如循环内简单计算)。
注意事项
1. 调试限制:宏错误需检查展开后的代码,建议优先使用子程序或函数模块调试。
2. 作用域管理:避免在跨程序或嵌套逻辑中使用宏(作用域不可穿透)。
3. 参数数量:需严格匹配宏定义的参数数量,否则导致编译错误。
示例代码
abap
DEFINE sum_three.
&3 = &1 + &2.
END-OF-DEFINITION.
DATA: a TYPE i VALUE 10,
b TYPE i VALUE 20,
result TYPE i.
" 调用宏
sum_three a b result.
WRITE: / 'Result:', result. " 输出:30