代码实例说明:关于建立“代码管理监控机制”的思路
经过年复一年的开发积累,企业的代码仓逐渐变得臃肿,甚至变成屎山代码。
这些屎山代码,往往经过N个程序员之手,他们水平参差不起,风格不一。
如何对这些屎山代码进行统一的管理,让它们可以被监控、评价和批量改造?
建立“代码管理系统”的第一个难点在于,如何在庞大的代码仓中,快速的查找出具有某些特征的代码段。
由于我们需要查找的是代码段,而不是代码行,用传统的正则表达式难以实现,需要通过语法解析器进行自定义语法配置,然后进行代码查找。
以小实例说明 :
### 实例1: 找出JAVA代码中,入参数量超过4个的函数:
# 配置查找规则(Code_manage.syn)如下所示:
__DEF_CASE_SENSITIVE__ Y
__DEF_FUZZY__ Y
__DEF_DEBUG__ N
__DEF_LINE_COMMENT__ //
__DEF_LINES_COMMENT__ /* */
__DEF_STR__ __NAME__
<1,200>
[1,1]ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_$??
[0,199]ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_??
[NO] import if else for while break continue class return try except finally final static public private
__DEF_PATH__ __FUNCTION_DEF__
0101 : x1 @ | public
: x2 @ + private
0 0 : x3 @ CAN_SKIP | static
1 1 : x4 @ | __NAME__
: x5 @ | __NAME__
: x6 @ | (
1111 : p1 @ CAN_SKIP | final
: p11 @ | __NAME__
: p111 @ | __NAME__
: p2 @ | ,
: p22 @ CAN_SKIP | final
: p222 @ | __NAME__
: p2222 @ | __NAME__
: p3 @ | ,
: p33 @ CAN_SKIP | final
: p333 @ | __NAME__
: p3333 @ | __NAME__
NNNN : p4 @ | ,
: p44 @ CAN_SKIP | final
: x444 @ | __NAME__
: x4444 @ | __NAME__
1111 : xx @ | )
# 假设java代码(MyCode.java) 如下所示:
private int alreadyBufferedSize = 0;
// The index in the byte[] found at buffers.getLast() to be written next
private int index = 0;
// Is the stream closed?
private boolean closed = false;
public FastByteArrayOutputStream(int initialBlockSize) {
Assert.isTrue(initialBlockSize > 0, "Initial block size must be greater than 0");
this.initialBlockSize = initialBlockSize;
this.nextBlockSize = initialBlockSize;
}
@Override
public void applyBeanPropertyValues(Object existingBean, String beanName, int autowireMode, boolean dependencyCheck, int initSize)
throws BeansException {
markBeanAsCreated(beanName);
BeanDefinition bd = getMergedBeanDefinition(beanName);
BeanWrapper bw = new BeanWrapperImpl(existingBean);
initBeanWrapper(bw);
applyPropertyValues(beanName, bd, bw, bd.getPropertyValues());
}
@Override
public Object initializeBean(Object existingBean, String beanName) {
return initializeBean(beanName, existingBean, null);
}
根据配置规则,执行查找命令: ZGLanguage -e Code_manage.syn -f MyCode.java
可以得到结果:
C:\>ZGLanguage -e Code_manage.syn -f MyCode.java Run type : Find Syntax file : Code_manage.syn code file : MyCode.java Output file : out.zgl -------------------------------------------------------------------- ### Found code by : __FUNCTION_DEF__ | Lines : 17 ~ 17 : -------------------------------------------------------------------- public void applyBeanPropertyValues(Object existingBean, String beanName, int autowireMode, boolean dependencyCheck, int initSize)
可以看出,查找结果只输出了函数 applyBeanPropertyValues,它的入参数量为5个,其他2个函数的入参均不超过4个,因此被忽略。
### 实例2: 提取SQL代码中的关联(on)和筛选(where)代码段:
# 配置查找规则(Code_manage.syn)如下所示:
__DEF_DEBUG__ N
__DEF_FUZZY__ Y
__DEF_CASE_SENSITIVE__ N
__DEF_LINE_COMMENT__ --
__DEF_LINES_COMMENT__ /* */
__DEF_PATH__ __WHERE__
1 : x1 | where
: x2 | __PATH_4_EXPR__
__DEF_PATH__ __ON__
1 : x1 | __\b__
: x2 + __\t__
: x3 + __\n__
: x4 | on
: x5 | __PATH_4_EXPR__
__DEF_SUB_PATH__ __PATH_4_EXPR__
1 : x1 | __SUB_PATH_EXPR__
: x2 + __ONE_PATH_EXPR__
__DEF_SUB_PATH__ __SUB_PATH_EXPR__
1 : x1 | (
: x2 | __ONE_PATH_EXPR__
: x3 | )
__DEF_SUB_PATH__ __ONE_PATH_EXPR__
NN : @ | __NAME__
: @ + __INT__
: @ + __FLOAT__
: @ + __CASE_WHEN__
: @ + __STRING__
: @ + __CAST_AS__
: @ + __FUNCTION__
: @ + __SUB_PATH_EXPR__
: @ + =
: @ + <>
: @ + !=
: @ + >
: @ + >=
: @ + <
: @ + <=
: @ + .
: @ + ,
: @ + +
: @ + -
: @ + *
: @ + /
: @ + ||
: @ + null
: @ + between
: @ + and
: @ + or
: @ + like
: @ + in
: @ STRING + not in
: @ STRING + is null
: @ STRING + is not null
__DEF_STR__ __NAME__
<1,100>
[1,1]ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_??
[0,100]ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_??
[NO] select inner left join on from where group order by having union all with as table date time
__DEF_STR__ __FLOAT__
<1,100>
[1,50]0123456789
[1,1].
[1,50]0123456789
__DEF_STR__ __INT__
<1,100>
[1,100]0123456789
__DEF_SUB_PATH__ __STRING__
1 : x1 | '
: x2 | __ANY__
: x3 | '
__DEF_SUB_PATH__ __DECIMAL__
111 : x1 | decimal
0 : x2 | (
01 : x3 | __INT__
00 : x4 | ,
00 : x5 | __INT__
01 : x6 | )
__DEF_SUB_PATH__ __VAR_NAME__
1 : x1 | $
: x2 | {
: x3 | __NAME__
: x4 | }
__DEF_SUB_PATH__ __CASE_WHEN__
1 : x1 @ | case
N : x2 @ | when
: x3 @ | __PATH_4_EXPR__
: x4 @ | then
: x5 @ | __PATH_4_EXPR__
1 : x6 @ CAN_SKIP | else
: x7 @ CAN_SKIP | __PATH_4_EXPR__
: x8 @ | end
__DEF_SUB_PATH__ __CAST_AS__
1 : x1 | cast
: x2 | (
1 : x3 | __PATH_4_EXPR__
: x4 | as
: x5 | date
: x6 + int
: n1 + double
: n2 + float
: n3 + bigint
: x8 + __DECIMAL__
1 : xx | )
__DEF_SUB_PATH__ __FUNCTION__
1 : x1 @ | __NAME__
: x2 @ | (
N : x3 @ CAN_SKIP | __PATH_4_EXPR__
e : x4 @ CAN_SKIP | ,
1 : x5 @ | )
# 假设SQL代码(myproc.sql) 如下所示:
CREATE OR REPLACE PROCEDURE PROC_F_CWWS_LOAN
(
P_AS_OF_DATE IN DATE,
RET_FLG OUT VARCHAR2,
RET_MSG OUT VARCHAR2
) IS
-- 声明变量并初始化
V_COUNT NUMBER := 0;
V_PROC_NAME VARCHAR2(200) := 'PROC_F_CWWS_LOAN';
V_PROC_DESC VARCHAR2(100) := 'xxxx业务数据ETL处理';
V_P_FREQ VARCHAR2(4) := '';
BEGIN
--写入初始日志
INSERT INTO M_RUNLOG VALUES (SYSDATE, V_PROC_NAME, 'it is 1');
COMMIT;
--设置会话日期格式
EXECUTE IMMEDIATE ' ALTER SESSION SET NLS_DATE_FORMAT = ''YYYY-MM-DD''';
--查询参数表中,该程序对应的频率值
SELECT P_FREQ
INTO V_P_FREQ
FROM ETL_PROC_STATUS_DEF
WHERE PROC_NAME = V_PROC_NAME;
--判断是调度频率
ETL.ETL_ADD_PARTITION('MA_F_LOAN', P_AS_OF_DATE, 'ETL');
--从还款计划表中取每笔账户最近一次小于等于数据日期还款日,作为上次还款日
INSERT INTO ETL.TMP_XD_LAST_PAYDATE
(OBJECTNO, LAST_PAYDATE)
SELECT OBJECTNO, LAST_PAYDATE
FROM (SELECT T.OBJECTNO,
MAX(TO_DATE(PAYDATE, 'YYYY-MM-DD')) LAST_PAYDATE
FROM NYBDP.O_CWWS_ACCT_PAYMENT_SCHEDULE T
WHERE T.AS_OF_DATE = P_AS_OF_DATE
AND T.SEQID <> '999'
AND TO_DATE(T.PAYDATE, 'YYYY-MM-DD') < P_AS_OF_DATE
GROUP BY T.OBJECTNO);
INSERT INTO M_RUNLOG VALUES (SYSDATE, V_PROC_NAME, 'it is 3');
COMMIT;
MERGE INTO ETL.MA_F_LOAN A
USING (SELECT /*+PARALLEL(8)*/
T.ACCOUNT_NUMBER, T.GL_ACCOUNT_ID, T.INT_GL_ACCOUNT_ID
FROM ETL.MA_F_LOAN T
INNER JOIN ETL.MA_D_GL_SUBJECT T1
ON T.INT_GL_ACCOUNT_ID = T1.SUBJECT_NO3
AND T1.SUBJECT_NAME3 LIKE '%已减值%'
AND T1.AS_OF_DATE = P_AS_OF_DATE
WHERE T.AS_OF_DATE = P_AS_OF_DATE
AND T.ACCOUNT_NUMBER IN
(SELECT ACCOUNT_NUMBER
FROM (SELECT /*+PARALLEL(8)*/
T2.ACCOUNT_NUMBER, COUNT(1)
FROM ETL.MA_F_LOAN T2
WHERE T2.AS_OF_DATE = P_AS_OF_DATE
GROUP BY T2.ACCOUNT_NUMBER
HAVING COUNT(1) > 1))) B
ON (A.ACCOUNT_NUMBER = B.ACCOUNT_NUMBER AND A.AS_OF_DATE = P_AS_OF_DATE AND A.GL_ACCOUNT_ID = B.GL_ACCOUNT_ID AND A.INT_GL_ACCOUNT_ID = B.INT_GL_ACCOUNT_ID)
WHEN MATCHED THEN
UPDATE SET A.CUR_BOOK_BAL = 0, A.OVERDUE_BAL = 0;
COMMIT;
RET_FLG := '0';
RET_MSG := '执行成功';
EXCEPTION
WHEN OTHERS THEN
--写入异常日志
ETL.PROC_ETL_LOG(P_AS_OF_DATE,V_PROC_NAME,V_PROC_DESC,V_COUNT,-1,SQLCODE,SQLERRM);
RET_MSG := SQLCODE || ':' || SQLERRM;
END;
/
根据配置规则,执行查找命令: ZGLanguage -e Code_manage.syn -f myproc.sql
可以得到结果:
C:\>ZGLanguage -e Code_manage.syn -f myproc.sql
Run type : Find
Syntax file : Code_manage.syn
code file : myproc.sql
Output file : out.zgl
--------------------------------------------------------------------
### Found code by : __WHERE__ | Lines : 27 ~ 27 :
--------------------------------------------------------------------
WHERE PROC_NAME = V_PROC_NAME
--------------------------------------------------------------------
### Found code by : __WHERE__ | Lines : 39 ~ 41 :
--------------------------------------------------------------------
WHERE T.AS_OF_DATE = P_AS_OF_DATE
AND T.SEQID <> '999'
AND TO_DATE(T.PAYDATE, 'YYYY-MM-DD') < P_AS_OF_DATE
--------------------------------------------------------------------
### Found code by : __ON__ | Lines : 52 ~ 54 :
--------------------------------------------------------------------
ON T.INT_GL_ACCOUNT_ID = T1.SUBJECT_NO3
AND T1.SUBJECT_NAME3 LIKE '%宸插噺鍊?'
AND T1.AS_OF_DATE = P_AS_OF_DATE
--------------------------------------------------------------------
### Found code by : __WHERE__ | Lines : 55 ~ 56 :
--------------------------------------------------------------------
WHERE T.AS_OF_DATE = P_AS_OF_DATE
AND T.ACCOUNT_NUMBER IN
--------------------------------------------------------------------
### Found code by : __WHERE__ | Lines : 61 ~ 61 :
--------------------------------------------------------------------
WHERE T2.AS_OF_DATE = P_AS_OF_DATE
--------------------------------------------------------------------
### Found code by : __ON__ | Lines : 64 ~ 66 :
--------------------------------------------------------------------
ON (A.ACCOUNT_NUMBER = B.ACCOUNT_NUMBER AND A.AS_OF_DATE = P_AS_OF_DATE AND A.GL_ACCOUNT_ID = B.GL_ACCOUNT_ID AND A.INT_GL_ACCOUNT_ID = B.INT_GL_ACCOUNT_ID)
WHEN MATCHED THEN
UPDATE SET A.CUR_BOOK_BAL = 0, A.OVERDUE_BAL = 0
可以看出,查找结果将 myproc.sql 代码中的 where 和 on 代码块及其所在行号提取出来。
- 点赞
- 收藏
- 关注作者
评论(0)