利用Java注解将常量类生成js文件供前端调用


注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记,没加,则等于没有某种标记,以后,javac编译器,开发工具和其他程序可以用反射来了解你的类及各种元素上有无何种标记,看你有什么标记,就去干相应的事。标记可以加在包,类,字段,方法,方法的参数以及局部变量上。

1)定义一个最简单的注解

public @interface MyAnnotation {
        //......
    }

2)把注解加在某个类上:

@MyAnnotation
    public class AnnotationTest{
        //......
    }

我们的常量类注解如下:

package com.gaochao.platform.util;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
/**
 * 常量中文转换注解
 * @author chao.gao
 * @date 2013-12-20 下午1:02:02
 * @version <b>1.0.0</b>
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ConstantsText {
    /**
    * 数据字典的label,获取中文含义时使用
    * @author chao.gao
    * @date 2013-12-20 下午1:17:19
    * @return
    */
    public String termsLable() default "";
 
    /**
    * 当数据字典中不存在对面的中文时使用,手动设置
    * @author chao.gao
    * @date 2013-12-20 下午1:17:40
    * @return
    */
    public String text() default "";
}

  注解的上方有两个元注解,是注解的注解,含义及用法如下:

//Java中提供了四种元注解,专门负责注解其他的注解,分别如下
//@Retention元注解,表示需要在什么级别保存该注释信息(生命周期)。可选的RetentionPoicy参数包括:
//RetentionPolicy.SOURCE: 停留在java源文件,编译器被丢掉
//RetentionPolicy.CLASS:停留在class文件中,但会被VM丢弃(默认)
//RetentionPolicy.RUNTIME:内存中的字节码,VM将在运行时也保留注解,因此可以通过反射机制读取注解的信息
//@Target元注解,默认值为任何元素,表示该注解用于什么地方。可用的ElementType参数包括
//ElementType.CONSTRUCTOR: 构造器声明
//ElementType.FIELD: 成员变量、对象、属性(包括enum实例)
//ElementType.LOCAL_VARIABLE: 局部变量声明
//ElementType.METHOD: 方法声明
//ElementType.PACKAGE: 包声明
//ElementType.PARAMETER: 参数声明
//ElementType.TYPE: 类、接口(包括注解类型)或enum声明

//@Documented将注解包含在JavaDoc中
//@Inheried允许子类继承父类中的注解

  在常量类中应用注解:

1
2
3
4
5
6
7
8 public class RelationshipConstants {
public static final String STATUS_SAVED = "saved";
    public static final String STATUS_FINISHED = "finished";
 
    public static final int HAVE_YES = 1;
    public static final int HAVE_NO = 0;
   
}

  添加注解后

/**
            * 计划状态:已审核
            */
            @ConstantsText(text = "我是手动审核")
            public static final String PLANSTATUS_AUDITED = "audited";
 
            /**
            * 计划状态:已作废,从数据字典查找
            */
            @ConstantsText(termsLable = "planStatus")
            public static final String PLANSTATUS_CANCELED = "canceled";
 
            /**
            * 计划状态:已完结
            */
            @ConstantsText(termsLable = "planStatus")
            public static final String PLANSTATUS_FINISHED = "finished";


解释注解:

public static String getText(Field field) throws Exception {
        String fieldValue = field.get(null).toString();
        if (field.isAnnotationPresent(ConstantsText.class)) {
            ConstantsText ct = field.getAnnotation(ConstantsText.class);
            if (!StringUtils.isBlank(ct.text())) {
                return ct.text();
            }
            if (!StringUtils.isBlank(ct.termsLable())) {
                String resultStr = "";
                if (map.get(ct.termsLable()) != null) {
                    resultStr = map.get(ct.termsLable()).get(fieldValue);
                } else {
                    logger.error("数据字典中未找到:termsLable:[" + ct.termsLable() + "]!");
                }
                if (resultStr != null)
                    return resultStr;
                else {
                    logger.error("数据字典中未找到termslable:[" + ct.termsLable() + "],termsCode:[" + fieldValue + "]的对应值!");
                }
            }
        }
        return fieldValue;
    }
}

生成js的逻辑:

public static void genJsFile(String fPath) {
        String webRoot = AppContext.getAppContext().getWebAppRoot() + "/scripts/";
        fPath = webRoot;
        String tempPath = fPath;
        PathMatchingResourcePatternResolver util = new PathMatchingResourcePatternResolver();
        String fileName = "";
        Map<String, StringBuffer> fileNameMap = new HashMap<String, StringBuffer>();
        try {
            //获取所有*Constants.class文件
            Resource[] file = util.getResources("classpath*:com/fx/oa/**/api/define/*Constants.class");
            StringBuffer jsContent = null;
            for (Resource resource : file) {
                logger.info("扫描到文件:>>>>>" + resource.getURI());
                jsContent = null;
                String clazz = resource.getURI().toString();
                jsContent = new StringBuffer();
                //获取反射需要的常量类全路径,2:!/,6:.class
                String className = clazz.substring(clazz.lastIndexOf("!") + 2, clazz.length() - 6).replace("/", ".");
                fileName = className.substring(0, className.indexOf(".api"));
                //1:.
                fileName = fileName.substring(fileName.lastIndexOf(".") + 1);
                if ((fileName.length() > 0) && fileNameMap.containsKey(fileName)) {
                    jsContent = fileNameMap.get(fileName);
                }
                logger.info("扫描到常量类:" + className);
                //获取顶级var变量名
                String varName = "oa."
                        + className.substring(className.lastIndexOf(".") + 1).replace("Constants", "").toLowerCase();
                jsContent.append(genContent(className, fileName, varName));
                if (jsContent.length() == 0) {
                    logger.info("常量类" + className + "未定义任何内容,跳过!");
                    continue;
                } else {
                    fileNameMap.put(fileName, jsContent);
                }
            }
            for (String s : fileNameMap.keySet()) {
                fPath += s;
                File outDir = new File(fPath);
                //输出JS文件
                if (!outDir.exists()) {
                    if (!outDir.mkdir()) {
                        throw new RuntimeException("创建目录:" + outDir + "失败!");
                    }
                }
                StringBuffer f = fileNameMap.get(s);
                File jsFile = new File(fPath + "/" + s + ".js");
                logger.info("生成Js文件路径(文件系统):" + jsFile.getAbsolutePath());
                logger.info("生成Js文件路径(项目相对路径):" + jsFile.getPath());
                BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(jsFile));
                bos.write(f.toString().getBytes());
                bos.close();
                fPath = tempPath;
            }
 
        } catch (Exception e) {
            logger.error(e.getMessage());
            throw new RuntimeException(e);
        }
    }
   
        @SuppressWarnings({ "rawtypes", "unchecked" })
    public static StringBuffer genContent(String className, String packageName, String varName) throws Exception {
        Class<?> clazz = Class.forName(className);
        StringBuffer jsContent = new StringBuffer();
        Field[] fields = clazz.getDeclaredFields();
        Map<String, List> map = new HashMap<String, List>();
        Map<String, List> mapText = new HashMap<String, List>();
        List<String> list = null;
        List<String> listText = null;
        String fieldName = null, pre = null, end = null;
        if (fields.length == 0) {
            logger.info(className + "尚未定义任何常量!");
            return new StringBuffer();
        }
        for (Field field : fields) {
            fieldName = field.getName();
            int fieldIndex = fieldName.indexOf("_");
            if (fieldIndex == -1) {
                logger.error(className + "字段:" + fieldName + "命名不符合规范!");
                throw new Exception(className + "字段:" + fieldName + "命名不符合规范!");
            }
            pre = fieldName.substring(0, fieldIndex);
            end = firstCharToUpper(fieldName.substring(fieldName.indexOf("_") + 1).toLowerCase(), "_");
            if (map.containsKey(pre)) {
                list = map.get(pre);
                list.add(end + "-" + field.get(null).toString());
                map.put(pre, list);
 
                listText = mapText.get(pre);
                listText.add(end + "-" + getText(field));
                mapText.put(pre, listText);
            } else {
                list = new ArrayList<String>();
                list.add(end + "-" + field.get(null).toString());
                map.put(pre, list);
 
                listText = new ArrayList<String>();
                listText.add(end + "-" + getText(field));
                mapText.put(pre, listText);
            }
        }
        String value = null;
        //处理英文
        jsContent.append(varName + " = {");
        for (String key : map.keySet()) {
            jsContent.append("\n\t" + key.toLowerCase() + " : {");
            for (int i = 0; i < map.get(key).size() - 1; i++) {
                value = (String) map.get(key).get(i);
                jsContent.append("\n\t\t\"" + value.substring(0, value.indexOf("-")) + "\"");
                jsContent.append(" : ");
                jsContent.append("\"" + value.substring(value.indexOf("-") + 1) + "\",");
            }
            value = (String) map.get(key).get(map.get(key).size() - 1);
            jsContent.append("\n\t\t\"" + value.substring(0, value.indexOf("-")) + "\"");
            jsContent.append(" : ");
            jsContent.append("\"" + value.substring(value.indexOf("-") + 1, value.length()) + "\"\n");
            jsContent.append("\t},");
        }
        jsContent.replace(jsContent.lastIndexOf(","), jsContent.lastIndexOf(",") + 1, "");
        jsContent.append("\n};\n");
 
        //处理中文
        jsContent.append(varName + "Text = {");
        for (String key : mapText.keySet()) {
            jsContent.append("\n\t" + key.toLowerCase() + " : {");
            for (int i = 0; i < mapText.get(key).size() - 1; i++) {
                value = (String) mapText.get(key).get(i);
                jsContent.append("\n\t\t\"" + value.substring(0, value.indexOf("-")) + "\"");
                jsContent.append(" : ");
                jsContent.append("\"" + value.substring(value.indexOf("-") + 1) + "\",");
            }
            value = (String) mapText.get(key).get(mapText.get(key).size() - 1);
            jsContent.append("\n\t\t\"" + value.substring(0, value.indexOf("-")) + "\"");
            jsContent.append(" : ");
            jsContent.append("\"" + value.substring(value.indexOf("-") + 1, value.length()) + "\"\n");
            jsContent.append("\t},");
        }
        jsContent.replace(jsContent.lastIndexOf(","), jsContent.lastIndexOf(",") + 1, "");
        jsContent.append("\n};\n");
        return jsContent;
    }

生成的js文件如下

requirements.js
        oa.requirements = {
              status : {
                  'saved':'saved',
                  'finished' : 'finished'
              },
              have : {
                  'yes' : 1,
                  'no' : 0
              }
        }

引入js文件后,

通过 模块名.功能标识.含义  或 模块名.功能标识[含义] 方式即可取到相应的值.

oa.requirements.status.saved

"saved"

oa.requirements.status[‘saved’]

"saved"

使用说明补充:

在常量类中需要转换成中文含义的常量上面添加注解:

@ConstantsText(termsLable=”数据字典表中的termsLable”),数据库数据字典表中一定要有相应的记录,取得记录是根据termsLable+termsCode取得后面的中文,如果取不到,那么就还是原来的英文.

@ConstantsText(text=”你自己添加的中文含义”)

生成的js就是如下效果,其实就是一个Map:

oa.recruitmentText = {
                have : {
                  "yes" : "1",
                  "no" : "0"
                },
                status : {
                  "saved" : "saved"
                },
                planstatus : {
                  "saved" : "saved",
                  "submited" : "submited",
                  "audited" : "我是手动审核",//这个是用的text
                  "canceled" : "已作废",//这个是数据字典中
                  "finished" : "已完结"//这个也是数据字典中的
                }
            };

    在js中使用,类似如下:

template: function(value) {
            return oa.recruitmentText.planstatus[value.requirementDeptNames];

  本文应用注解、反射等技术将Constants文件中定义的常量信息提供给前端js,减少了代码量,同时也使前后端文本一致。

本文永久更新链接地址

相关内容