Jerry文章《浅谈Java和SAP ABAP的静态代理和动态代理,以及ABAP面向切面编程的尝试一文的源代码》

原文链接

导航目录

  • 工具类ZCL_ABAP_DYNAMIC_PROXY_FACTORY
  • 工具类zcl_abap_cglib_tool
  • Jerry Wang的ABAP专题文章

工具类ZCL_ABAP_DYNAMIC_PROXY_FACTORY

该工具类负责生产一个新的具有持久化存储的静态代理类:

class ZCL_ABAP_DYNAMIC_PROXY_FACTORY definition
  public
  final
  create public .

public section.

  class-methods GET_PROXY
    importing
      !IO_ORIGIN type ref to OBJECT
      !IV_NEW_CLASS_NAME type STRING
      !IV_PRE_EXIT type STRING
      !IV_POST_EXIT type STRING
    returning
      value(RO_PROXY) type ref to OBJECT .
protected section.
private section.

  class-data MS_VSEOCLASS type VSEOCLASS .
  class-data MT_ATTRIBUTE type SEOO_ATTRIBUTES_R .
  class-data MT_IMP_IF type SEOR_IMPLEMENTINGS_R .
  class-data MT_METHODS type SEOO_METHODS_R .
  class-data MT_PARAMETERS type SEOS_PARAMETERS_R .
  class-data MV_INTERFACE_NAME type STRING .
  class-data MV_METHOD_NAME type STRING .
  class-data MT_SOURCECODE type SEO_METHOD_SOURCE_TABLE .
  class-data MV_NEW_CLASS_NAME type STRING .
  class-data MO_ORIGIN type ref to OBJECT .
  class-data MV_PRE_EXIT type STRING .
  class-data MV_POST_EXIT type STRING .

  class-methods GENERATE_CLASS .
  class-methods PREPARE_ATTR_AND_SIGNATURE .
  class-methods PREPARE_SOURCE_CODE .
  class-methods EXTRACT_INTERFACE_INFO
    importing
      !IO_ORIGIN type ref to OBJECT .
  class-methods INIT
    importing
      !IV_NEW_CLASS_NAME type STRING
      !IO_ORIGIN type ref to OBJECT
      !IV_PRE_EXIT type STRING
      !IV_POST_EXIT type STRING .
  class-methods CREATE_INSTANCE
    returning
      value(RO_PROXY) type ref to OBJECT .
ENDCLASS.



CLASS ZCL_ABAP_DYNAMIC_PROXY_FACTORY IMPLEMENTATION.


* ---------------------------------------------------------------------------------------+
* | Static Private Method ZCL_ABAP_DYNAMIC_PROXY_FACTORY=>CREATE_INSTANCE
* +-------------------------------------------------------------------------------------------------+
* | [<-()] RO_PROXY                       TYPE REF TO OBJECT
* +--------------------------------------------------------------------------------------
  method CREATE_INSTANCE.
    TRY.

     create object ro_proxy type (mv_new_class_name)
        EXPORTING
           io_origin = mo_origin.
    CATCH cx_root INTO data(cx_root).
      WRITE:/ 'instance created failed: ', cx_root->get_text( ).
    ENDTRY.
  endmethod.


* ---------------------------------------------------------------------------------------+
* | Static Private Method ZCL_ABAP_DYNAMIC_PROXY_FACTORY=>EXTRACT_INTERFACE_INFO
* +-------------------------------------------------------------------------------------------------+
* | [--->] IO_ORIGIN                      TYPE REF TO OBJECT
* +--------------------------------------------------------------------------------------
  method EXTRACT_INTERFACE_INFO.
     data(lo_class) = cast CL_ABAP_OBJECTDESCR( cl_abap_objectdescr=>describe_by_object_ref( io_origin ) ).

     READ TABLE lo_class->interfaces INTO mv_interface_name INDEX 1 .
     CHECK sy-subrc = 0.

     "For demo purpose, I assume only one method in one interface
     SELECT SINGLE cmpname INTO mv_method_name FROM seocompo
        WHERE clsname = mv_interface_name.

  endmethod.


* ---------------------------------------------------------------------------------------+
* | Static Private Method ZCL_ABAP_DYNAMIC_PROXY_FACTORY=>GENERATE_CLASS
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------
  METHOD generate_class.
    CALL FUNCTION 'SEO_CLASS_CREATE_COMPLETE'
      EXPORTING
        devclass                   = '$TMP'
        version                    = seoc_version_active
        authority_check            = abap_true
        overwrite                  = abap_true
        suppress_method_generation = abap_false
        genflag                    = abap_false
        method_sources             = mt_sourcecode
        suppress_dialog            = abap_true
      CHANGING
        class                      = ms_vseoclass
        methods                    = mt_methods
        parameters                 = mt_parameters
        implementings              = mt_imp_if
        attributes                 = mt_attribute
      EXCEPTIONS
        existing                   = 1
        is_interface               = 2
        db_error                   = 3
        component_error            = 4
        no_access                  = 5
        other                      = 6
        OTHERS                     = 7.

  ENDMETHOD.


* ---------------------------------------------------------------------------------------+
* | Static Public Method ZCL_ABAP_DYNAMIC_PROXY_FACTORY=>GET_PROXY
* +-------------------------------------------------------------------------------------------------+
* | [--->] IO_ORIGIN                      TYPE REF TO OBJECT
* | [--->] IV_NEW_CLASS_NAME              TYPE        STRING
* | [--->] IV_PRE_EXIT                    TYPE        STRING
* | [--->] IV_POST_EXIT                   TYPE        STRING
* | [<-()] RO_PROXY                       TYPE REF TO OBJECT
* +--------------------------------------------------------------------------------------
  method GET_PROXY.
     init( iv_new_class_name = iv_new_class_name io_origin = io_origin
           iv_pre_exit = iv_pre_exit iv_post_exit = iv_post_exit ).
     extract_interface_info( io_origin ).
     prepare_source_code( ).
     prepare_attr_and_signature( ).
     generate_class( ).
     ro_proxy = create_instance( ).
  endmethod.


* ---------------------------------------------------------------------------------------+
* | Static Private Method ZCL_ABAP_DYNAMIC_PROXY_FACTORY=>INIT
* +-------------------------------------------------------------------------------------------------+
* | [--->] IV_NEW_CLASS_NAME              TYPE        STRING
* | [--->] IO_ORIGIN                      TYPE REF TO OBJECT
* | [--->] IV_PRE_EXIT                    TYPE        STRING
* | [--->] IV_POST_EXIT                   TYPE        STRING
* +--------------------------------------------------------------------------------------
  method INIT.
    clear: mv_interface_name, mv_method_name, mt_sourcecode,mt_sourcecode,
    mt_imp_if,  ms_vseoclass, mt_attribute,mt_parameters,mt_methods.
    mv_new_class_name = iv_new_class_name.
    mo_origin = io_origin.
    mv_pre_exit = iv_pre_exit.
    mv_post_exit = iv_post_exit.
  endmethod.


* ---------------------------------------------------------------------------------------+
* | Static Private Method ZCL_ABAP_DYNAMIC_PROXY_FACTORY=>PREPARE_ATTR_AND_SIGNATURE
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------
  METHOD prepare_attr_and_signature.
    DATA:ls_attribute  LIKE LINE OF mt_attribute,
         ls_parameter  LIKE LINE OF mt_parameters,
         ls_method     LIKE LINE OF mt_methods.

    ls_method-clsname = mv_new_class_name.
    ls_method-cmpname = 'CONSTRUCTOR'.
    ls_method-state = 1. "implemented
    ls_method-exposure = 2. "public
    APPEND ls_method TO mt_methods.

    ls_parameter-clsname = mv_new_class_name.
    ls_parameter-cmpname = 'CONSTRUCTOR'.
    ls_parameter-version = 1.
    ls_parameter-descript = 'Constructor automatically generated by Jerry'.
    ls_parameter-type = 'OBJECT'."mv_interface_name.
    ls_parameter-sconame = 'IO_ORIGIN'.
    ls_parameter-cmptype = 1. "METHOD
    ls_parameter-mtdtype = 0. "METHOD
    ls_parameter-pardecltyp = 0. "IMPORTING
    ls_parameter-parpasstyp = 1. "pass by reference
    ls_parameter-typtype = 3. "type ref to
    APPEND ls_parameter TO mt_parameters.

    ls_attribute-clsname = mv_new_class_name.
    ls_attribute-cmpname = 'MO_ORIGIN'.
    ls_attribute-state = 1.
    ls_attribute-attdecltyp = 0.
    ls_attribute-attexpvirt = 0. "private
    ls_attribute-typtype = 3. "type ref to
    ls_attribute-type = 'OBJECT'."mv_interface_name.
    APPEND ls_attribute TO mt_attribute.
  ENDMETHOD.


* ---------------------------------------------------------------------------------------+
* | Static Private Method ZCL_ABAP_DYNAMIC_PROXY_FACTORY=>PREPARE_SOURCE_CODE
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------
  METHOD prepare_source_code.
    DATA: ls_method_source TYPE seo_method_source,
          ls_imp_if        TYPE seor_implementing_r,
          ls_imp_det       TYPE seoredef.

    ms_vseoclass-clsname   = mv_new_class_name.
    ms_vseoclass-state     = seoc_state_implemented.
    ms_vseoclass-exposure  = seoc_exposure_public.
    ms_vseoclass-descript  = `Dynamic proxy generated by Jerry's code`.
    ms_vseoclass-langu     = sy-langu.
    ms_vseoclass-clsccincl = abap_true.
    ms_vseoclass-unicode   = abap_true.
    ms_vseoclass-fixpt     = abap_true.
    ms_vseoclass-clsfinal  = abap_true.

    ls_imp_det = ls_imp_if-clsname       = mv_new_class_name.
    ls_imp_det = ls_imp_if-refclsname    = mv_interface_name.
    ls_imp_if-state      = seoc_state_implemented.
    APPEND ls_imp_if TO mt_imp_if.

    CLEAR: ls_method_source.
    DATA: lv_name TYPE string.
    ls_method_source-cpdname = |{ mv_interface_name }~{ mv_method_name }|.
    APPEND |{ mv_pre_exit }| TO ls_method_source-source.
    APPEND |DATA(lo) = CAST { mv_interface_name }( mo_origin ).| to ls_method_source-source.
    APPEND 'lo->print( ).'  TO ls_method_source-source.
    APPEND |{ mv_post_exit }| TO ls_method_source-source.

    APPEND ls_method_source TO mt_sourcecode.

    CLEAR: ls_method_source.
    ls_method_source-cpdname = 'CONSTRUCTOR'.
    APPEND 'mo_origin = io_origin.' TO ls_method_source-source.
    APPEND ls_method_source TO mt_sourcecode.
  ENDMETHOD.
ENDCLASS.

工具类zcl_abap_cglib_tool

这个类是Jerry仿照Java CGLIB(Code Generation Library)创建的, 针对传入的一个ABAP类,能创建一个生命周期只有当前会话(current session)内的代理类,该代理类是传入ABAP类的子类。这意味着传入的ABAP类必须能够被继承,不能被标记为final.

CLASS zcl_abap_cglib_tool DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    CLASS-METHODS get_proxy
      IMPORTING
        !iv_class_name  TYPE string
        !io_pre_exit    TYPE REF TO if_preexit
        !io_post_exit   TYPE REF TO if_postexit
      RETURNING
        VALUE(ro_proxy) TYPE REF TO object .
  PROTECTED SECTION.
  PRIVATE SECTION.

    CLASS-DATA mv_class_name TYPE string .
    CLASS-DATA mt_source TYPE seop_source_string .
    CLASS-DATA mo_proxy TYPE REF TO object .
    CLASS-DATA mo_preexit TYPE REF TO if_preexit .
    CLASS-DATA mo_postexit TYPE REF TO if_postexit .

    CLASS-METHODS generate_proxy .
    CLASS-METHODS get_source_code .
ENDCLASS.



CLASS ZCL_ABAP_CGLIB_TOOL IMPLEMENTATION.


* ---------------------------------------------------------------------------------------+
* | Static Private Method ZCL_ABAP_CGLIB_TOOL=>GENERATE_PROXY
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------
  METHOD generate_proxy.
    DATA(lv_new_cls_name) = mv_class_name && '_SUB'.

    DATA(lv_inherit) = |inheriting from { mv_class_name }|.
    TRANSLATE lv_new_cls_name TO UPPER CASE.
    TRY.
        LOOP AT mt_source ASSIGNING FIELD-SYMBOL() WHERE table_line CS mv_class_name.
          REPLACE mv_class_name IN  WITH lv_new_cls_name.
        ENDLOOP.

        LOOP AT mt_source ASSIGNING FIELD-SYMBOL() WHERE table_line CS mv_class_name.
          DELETE mt_source INDEX ( sy-tabix + 1 ).
          INSERT lv_inherit INTO mt_source INDEX ( sy-tabix + 1 ).
          EXIT.
        ENDLOOP.

        GENERATE SUBROUTINE POOL mt_source NAME DATA(prog).
        WRITE: / sy-subrc.

        DATA(class) = |\\PROGRAM={ prog }\\CLASS={ lv_new_cls_name }|.

        CREATE OBJECT mo_proxy TYPE (class).

        CALL METHOD mo_proxy->('SET_PREEXIT')
          EXPORTING
            io_preexit = mo_preexit.
        CALL METHOD mo_proxy->('SET_POSTEXIT')
          EXPORTING
            io_postexit = mo_postexit.

      CATCH cx_root INTO DATA(cx_root).
        WRITE: / cx_root->get_text( ).
    ENDTRY.
  ENDMETHOD.


* ---------------------------------------------------------------------------------------+
* | Static Public Method ZCL_ABAP_CGLIB_TOOL=>GET_PROXY
* +-------------------------------------------------------------------------------------------------+
* | [--->] IV_CLASS_NAME                  TYPE        STRING
* | [--->] IO_PRE_EXIT                    TYPE REF TO IF_PREEXIT
* | [--->] IO_POST_EXIT                   TYPE REF TO IF_POSTEXIT
* | [<-()] RO_PROXY                       TYPE REF TO OBJECT
* +--------------------------------------------------------------------------------------
  METHOD get_proxy.
    mv_class_name = iv_class_name.
    mo_preexit = io_pre_exit.
    mo_postexit = io_post_exit.
    CLEAR: mo_proxy.

    get_source_code( ).

    generate_proxy( ).

    ro_proxy = mo_proxy.
  ENDMETHOD.


* ---------------------------------------------------------------------------------------+
* | Static Private Method ZCL_ABAP_CGLIB_TOOL=>GET_SOURCE_CODE
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------
  METHOD get_source_code.
    DATA:
      cifkey      TYPE seoclskey,
      clstype     TYPE seoclstype,
      source      TYPE seop_source_string,
      pool_source TYPE seop_source_string,
      l_string    TYPE string,
      source_line TYPE LINE OF seop_source_string,
      tabix       TYPE sytabix,
      includes    TYPE seop_methods_w_include,
      include     TYPE seop_method_w_include,
      cifref      TYPE REF TO if_oo_clif_incl_naming,
      clsref      TYPE REF TO if_oo_class_incl_naming,
      intref      TYPE REF TO if_oo_interface_incl_naming.

    cifkey-clsname = mv_class_name.

    CALL METHOD cl_oo_include_naming=>get_instance_by_cifkey
      EXPORTING
        cifkey = cifkey
      RECEIVING
        cifref = cifref
      EXCEPTIONS
        OTHERS = 1.
    ASSERT sy-subrc = 0.

    APPEND 'program.' TO mt_source.
    CHECK cifref->clstype = seoc_clstype_class.
    clsref ?= cifref.
    READ REPORT clsref->class_pool INTO pool_source.

    READ REPORT clsref->locals_old INTO source.
    LOOP AT source INTO source_line.
      IF source_line NS '*"*'.
        APPEND source_line TO mt_source..
      ENDIF.
    ENDLOOP.

    READ REPORT clsref->locals_def INTO source.
    LOOP AT source INTO source_line.
      IF source_line NS '*"*'.
        APPEND source_line TO mt_source..
      ENDIF.
    ENDLOOP.

    READ REPORT clsref->locals_imp INTO source.
    LOOP AT source INTO source_line.
      IF source_line NS '*"*'.
        APPEND source_line TO mt_source..
      ENDIF.
    ENDLOOP.

    READ REPORT clsref->public_section INTO source.
    LOOP AT source ASSIGNING FIELD-SYMBOL().
      IF  NS '*"*'.
        FIND REGEX '.*methods.*\.' IN  MATCH LENGTH DATA(lv_len).
        IF sy-subrc = 0.
          lv_len = lv_len - 1.
           = +0(lv_len).
          CONCATENATE  'redefinition' '.' INTO  SEPARATED BY space.
        ENDIF.

        APPEND  TO mt_source.
      ENDIF.
    ENDLOOP.
    APPEND 'methods SET_PREEXIT importing !IO_PREEXIT type ref to IF_PREEXIT .' TO mt_source.
    APPEND 'methods SET_POSTEXIT importing !IO_POSTEXIT type ref to IF_POSTEXIT .' TO mt_source.

    READ REPORT clsref->protected_section INTO source.
    LOOP AT source INTO source_line.
      IF source_line NS '*"*'.
        APPEND source_line TO mt_source.
      ENDIF.
    ENDLOOP.

    READ REPORT clsref->private_section INTO source.
    LOOP AT source INTO source_line.
      IF source_line NS '*"*'.
        APPEND source_line TO mt_source.
      ENDIF.
    ENDLOOP.

    APPEND 'data MO_PREEXIT type ref to IF_PREEXIT .' TO mt_source.
    APPEND 'data MO_POSTEXIT type ref to IF_POSTEXIT .' TO mt_source.

    CONCATENATE 'CLASS' cifkey 'IMPLEMENTATION' INTO l_string SEPARATED BY space.
    LOOP AT pool_source FROM tabix INTO source_line.
      IF source_line CS 'ENDCLASS'.
        APPEND source_line TO mt_source..
      ENDIF.
      IF source_line CS l_string.
        SKIP.
        APPEND source_line TO mt_source..
        tabix = sy-tabix.
        EXIT.
      ENDIF.
    ENDLOOP.

    includes = clsref->get_all_method_includes( ).
    LOOP AT includes INTO include.
      READ REPORT include-incname INTO source.
      INSERT 'mo_preexit->execute( ).' INTO source INDEX 2.
      INSERT 'mo_postexit->execute( ).' INTO source INDEX ( lines( source ) ).

      LOOP AT source INTO source_line.
        APPEND source_line TO mt_source..
      ENDLOOP.
    ENDLOOP.
    APPEND 'method set_preexit.  mo_preexit = IO_PREEXIT. endmethod.' TO mt_source.
    APPEND 'method set_postexit.  mo_postexit = IO_POSTEXIT. endmethod.' TO mt_source.
    LOOP AT pool_source FROM tabix INTO source_line.
      IF source_line CS 'ENDCLASS'.
        APPEND source_line TO mt_source..
      ENDIF.
    ENDLOOP.
  ENDMETHOD.
ENDCLASS.

Jerry Wang的ABAP专题文章

  • Jerry的ABAP, Java和JavaScript乱炖

  • ABAP开发人员未来应该学些什么

  • Jerry 2017年的五一小长假:8种经典排序算法的ABAP实现

  • Jerry的ABAP原创技术文章合集

  • 300行ABAP代码实现一个最简单的区块链原型

  • 使用Java+SAP云平台+SAP Cloud Connector调用ABAP On-Premise系统里的函数

  • 在SAP云平台的CloudFoundry环境下消费ABAP On-Premise OData服务

  • ABAP vs Java, 蛙泳 vs 自由泳

  • 聊聊C语言和ABAP

  • 动手使用ABAP Channel开发一些小工具,提升日常工作效率

  • 我用ABAP做过的那些无聊的事情

  • 不喜欢SAP GUI?那试试用Eclipse进行ABAP开发吧

  • 使用Visual Studio Code编写和激活ABAP代码

  • 你的ABAP程序给佛祖开过光么?来试试Jerry这个小技巧

  • 在SAP云平台ABAP编程环境上编写第一段ABAP程序

  • SAP官方发布的ABAP编程规范

  • ABAP Code Inspector那些隐藏的功能,您都知道吗?

  • 还在用ABAP进行SAP产品的二次开发?来了解下这种全新的二次开发理念吧

  • ABAP Netweaver体内的那些寄生式编程语言

  • 从SAP社区上的一篇博客开始,聊聊SAP产品命名背后的那份情怀

  • 云端的ABAP Restful服务开发

  • 如何在SAP云平台ABAP编程环境里把CDS view暴露成OData服务

  • 使用abapGit在ABAP On-Premises系统和SAP云平台ABAP环境之间进行代码传输

  • 30分钟用Restful ABAP Programming模型开发一个支持增删改查的Fiori应用

  • Jerry带您了解Restful ABAP Programming模型系列之二:Action和Validation的实现

  • Jerry带您了解Restful ABAP Programming模型系列之三:云端ABAP应用调试

  • SAP云平台上的ABAP编程环境里如何消费第三方服务

  • ABAP开发者上云的时候到了 - 现在大家可以免费使用SAP云平台ABAP环境的试用版了

  • 学而不思则罔 - SAP云平台ABAP编程环境的由来和适用场景

  • SAP云平台里的三叉戟应用

  • 如何基于Restful ABAP Programming模型开发并部署一个支持增删改查的Fiori应用

  • SAP 2019 TechEd Key Note解读:云时代下SAP从业人员如何做二次开发?

  • 有哪些ABAP关键字和语法,到了ABAP云环境上就没办法用了?

  • ABAP开发环境终于支持以驼峰命名法自动格式化ABAP变量名了

  • 利用ABAP 740的新关键字REDUCE完成一个实际工作任务

  • 一段让人瑟瑟发抖的ABAP代码

  • 昨日万圣节ABAP怪兽级代码谜团,公布答案啦

  • 介绍一种在ABAP内核态进行内表高效拷贝的方法

  • 使用SAP Cloud Application Programming模型开发OData的一个实际例子

  • 当ABAP遇见普罗米修斯

  • 使用ABAP绘制可伸缩矢量图

  • ABAP开发环境语法高亮的那些事儿

  • SAP错误消息调试之七种武器:让所有的错误消息都能被定位

  • 使用ABAP操作Excel的几种方法

  • SAP GUI里的收藏夹事务码管理工具

  • SAP GUI和Windows注册表

  • 有了Debug权限就能干坏事?小心了,你的一举一动尽在系统监控中

  • ABAP CCDEF, CCIMP, CCMAC, CCAU, CMXXX这些东东是什么鬼

  • 实现ABAP条件断点的三种方式

  • 使用SAT跟踪监控从浏览器打开的SAP应用的性能和调用栈

  • 一个13年ABAP老兵的建议:了解这些基础知识,对ABAP开发有百利而无一害

  • SAP ABAP Netweaver容器化, 不可能完成的任务吗?

  • SAP产品增强技术回顾

  • SAP API开发方法大全

你可能感兴趣的:(ABAP,Jerry,Wang的原创SAP技术文章,开源)