前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Jerry文章《浅谈Java和SAP ABAP的静态代理和动态代理,以及ABAP面向切面编程的尝试一文的源代码》

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

作者头像
Jerry Wang
发布2020-04-21 11:46:55
4650
发布2020-04-21 11:46:55
举报

工具类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.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Private Method ZCL_ABAP_DYNAMIC_PROXY_FACTORY=>CREATE_INSTANCE
* +-------------------------------------------------------------------------------------------------+
* | [<-()] RO_PROXY                       TYPE REF TO OBJECT
* +--------------------------------------------------------------------------------------</SIGNATURE>
  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.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Private Method ZCL_ABAP_DYNAMIC_PROXY_FACTORY=>EXTRACT_INTERFACE_INFO
* +-------------------------------------------------------------------------------------------------+
* | [--->] IO_ORIGIN                      TYPE REF TO OBJECT
* +--------------------------------------------------------------------------------------</SIGNATURE>
  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.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Private Method ZCL_ABAP_DYNAMIC_PROXY_FACTORY=>GENERATE_CLASS
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
  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.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | 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
* +--------------------------------------------------------------------------------------</SIGNATURE>
  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.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | 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
* +--------------------------------------------------------------------------------------</SIGNATURE>
  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.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Private Method ZCL_ABAP_DYNAMIC_PROXY_FACTORY=>PREPARE_ATTR_AND_SIGNATURE
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</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.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Private Method ZCL_ABAP_DYNAMIC_PROXY_FACTORY=>PREPARE_SOURCE_CODE
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
  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.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Private Method ZCL_ABAP_CGLIB_TOOL=>GENERATE_PROXY
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
  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(<source1>) WHERE table_line CS mv_class_name.
          REPLACE mv_class_name IN <source1> WITH lv_new_cls_name.
        ENDLOOP.

        LOOP AT mt_source ASSIGNING FIELD-SYMBOL(<source>) 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.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | 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
* +--------------------------------------------------------------------------------------</SIGNATURE>
  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.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Private Method ZCL_ABAP_CGLIB_TOOL=>GET_SOURCE_CODE
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
  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(<source_line>).
      IF <source_line> NS '*"*'.
        FIND REGEX '.*methods.*\.' IN <source_line> MATCH LENGTH DATA(lv_len).
        IF sy-subrc = 0.
          lv_len = lv_len - 1.
          <source_line> = <source_line>+0(lv_len).
          CONCATENATE <source_line> 'redefinition' '.' INTO <source_line> SEPARATED BY space.
        ENDIF.

        APPEND <source_line> 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.
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-04-19 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 工具类ZCL_ABAP_DYNAMIC_PROXY_FACTORY
  • 工具类zcl_abap_cglib_tool
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档