如何在SpringMVC框架中利用Java反射机制和Javassist实现Java对象、属性、注解的动态创建生成

简单介绍Java的反射原理

Java的反射机制是Java特性之一,反射机制是构建框架技术的基础所在。Java程序要能够运行,java虚拟机需要事先加载java类,目前我们的程序在编译期就已经确定哪些java类需要被加载。

Java的反射机制是在编译时并不确定哪个类需要被加载,而是在程序运行时才加载、探知、自审。这样的特点就是反射。

何为自审:通过Java的反射机制能够探知到java类的基本结构,这种对Java类结构探知的能力,我们称为Java类的“自审”。

Java的反射原理最典型的应用就是各种Java IDE:比如Jcreator,eclipse,idea等,当我们构建出一个对象时,去调用该对象的方法和属性的时候。一按点,IDE工具就会自动的把该对象能够使用的所有的方法和属性全部都列出来,供我们进行选择。这就是利用了Java反射的原理,是对我们创建对象的探知、自审的过程。
直接上代码。
主方法类

import java.io.FileWriter;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import java.util.Date;

import com.soa.mdm.exception.CustomException;
import com.soa.mdm.util.DateUtil;
import com.soa.mdm.util.DbUtil;
import com.soa.mdm.util.GetEntityPathUtil;
import com.soa.mdm.util.PropertiesUtil;
import com.soa.mdm.util.StringUtil;
import com.soa.mdm.util.tableNameUtil;

/**
 * 如何在SpringMVC框架中利用Java反射机制和Javassist实现Java对象、属性、注解的动态创建生成
 * 
 * @author Mr.z
 * @createTime 2016年12月23日-下午3:54:47
 * @version 1.0.0
 */
public class DynamicCreateObjectFromDatabase {

    private String packageOutPath = "com.soa.mdm.entity";// 指定实体生成所在包的路径
    private String[] colNames; // 列名数组
    private String[] colTypes; // 列名类型数组
    private int[] colSizes; // 列名大小数组
    private boolean isImportUtil = false; // 是否需要导入包java.util.*
    private boolean isImportSql = false; // 是否需要导入包java.sql.*
    private String tableName = ""; // 数据库表名称
    private String entityTableName = ""; // 实体表名称
    private String primaryKeyName = ""; // 表主键
    private String authorName = "Mr.Z";

    public static void main(String[] args) throws Exception {
        DynamicCreateObjectFromDatabase dcofd = new DynamicCreateObjectFromDatabase();
        dcofd.GenEntityOracle("code_rules");//code_rules为数据库中的表名
    }

    /**
     * 生成oracle表实体
     *
     * @author Mr.Z
     * @throws Exception
     * @Time 2016年12月22日 下午3:57:30
     */
    public void GenEntityOracle(String tablename) throws Exception {
        this.tableName = tablename;
        entityTableName = tableNameUtil.initTableName(tablename);
        String schema = tableNameUtil.upperCaseTableName(PropertiesUtil.getValue("jdbc.username"));
        Connection con = null;
        String sql = "select * from " + tablename;
        con = DbUtil.getCon();
        DatabaseMetaData databaseMetaData = con.getMetaData();
        ResultSet rsPriKeys = databaseMetaData.getPrimaryKeys(null, schema,
                tableNameUtil.upperCaseTableName(tablename));
        if (rsPriKeys.getFetchSize() < 1) {
            throw new CustomException("表或视图不存在!");
        }
        if (rsPriKeys.next()) {
            if (StringUtil.isEmpty(rsPriKeys.getString(4))) {
                throw new CustomException("表主键不能为空!");
            }
            primaryKeyName = rsPriKeys.getString(4);
        }
        Statement pstmt = con.createStatement();
        ResultSet rs = pstmt.executeQuery(sql);
        ResultSetMetaData rsmd = rs.getMetaData();
        int size = rsmd.getColumnCount();
        System.out.println(size);
        colNames = new String[size];
        colTypes = new String[size];
        colSizes = new int[size];
        for (int i = 0; i < size; i++) {
            colNames[i] = rsmd.getColumnName(i + 1); // 获取列命
            colTypes[i] = rsmd.getColumnTypeName(i + 1); // 获取列类型
            if (colTypes[i].equalsIgnoreCase("date") || colTypes[i].equalsIgnoreCase("timestamp")) {
                isImportUtil = true;
            }
            if (colTypes[i].equalsIgnoreCase("blob") || colTypes[i].equalsIgnoreCase("char")) {
                isImportSql = true;
            }
            colSizes[i] = rsmd.getColumnDisplaySize(i + 1);
        }
        String content = parse(colNames, colTypes, colSizes);
        // File directory = new File("");
        // System.out.println("绝对路径:"+directory.getAbsolutePath());
        // System.out.println("相对路径:"+directory.getCanonicalPath());
        String outputPath = GetEntityPathUtil.getEntityPath(entityTableName);
        /*
         * outputPath =
         * "E:\\workspace\\Mdm\\src\\main\\java\\com\\soa\\mdm\\entity\\" + initcap(tablename) + "
         * .java";
         */
        FileWriter fw = new FileWriter(outputPath);
        PrintWriter pw = new PrintWriter(fw);
        pw.println(content);
        pw.flush();
        pw.close();
        DbUtil.closeCon(con);
    }

    /**
     * 功能:生成实体类主体代码
     * 
     * @param colNames
     *            列名数组
     * @param colTypes
     *            列名类型数组
     * @param colSizes
     *            列名大小数组
     * @return
     */
    private String parse(String[] colNames, String[] colTypes, int[] colSizes) {
        StringBuffer sb = new StringBuffer();
        sb.append("package " + this.packageOutPath + ";\r\n\r\n");
        // 判断是否导入工具包
        if (isImportUtil) {
            sb.append("import java.util.Date;\r\n\r\n");
        }
        if (isImportSql) {
            sb.append("import java.sql.*;\r\n\r\n");
        }
        sb.append("import javax.persistence.Entity;\r\n");
        sb.append("import javax.persistence.GeneratedValue;\r\n");
        sb.append("import javax.persistence.Id;\r\n");
        sb.append("import javax.persistence.Table;\r\n\r\n");
        sb.append("import org.hibernate.annotations.GenericGenerator;\r\n");
        sb.append("\r\n");
        // 注释部分
        sb.append("/**\r\n");
        sb.append("* @author cc\r\n");
        sb.append("* " + entityTableName + " 实体类\r\n");
        sb.append("* @version 1.0.0\r\n");
        sb.append("* @date" + DateUtil.formatDate(new Date(), "yyyy年MM月dd日") + " " + this.authorName + "\r\n");
        sb.append("* @company china-sa\r\n");
        sb.append("*/ \r\n");
        // Hibernate注解部分
        sb.append("@Entity\r\n");
        sb.append("@Table(name = \"" + tableName + "\")\r\n");
        // 实体部分
        sb.append("public class " + tableNameUtil.initTableName(entityTableName) + "{\r\n\r\n");
        processAllAttrs(sb);// 属性
        sb.append("\r\n");
        processAllMethod(sb);// get set方法
        sb.append("}\r\n\r\n");
        return sb.toString();
    }

    /*
     * 功能:生成所有属性
     * 
     * @param sb
     */
    private void processAllAttrs(StringBuffer sb) {

        for (int i = 0; i < colNames.length; i++) {
            sb.append("\tprivate " + sqlType2JavaType(colTypes[i]) + " " + colNames[i] + ";\r\n");
        }

    }

    /**
     * 功能:生成所有方法
     * 
     * @param sb
     */
    private void processAllMethod(StringBuffer sb) {

        for (int i = 0; i < colNames.length; i++) {
            if (primaryKeyName.equals(colNames[i])) {
                sb.append("\t@Id\r\n\t");
                sb.append("@GeneratedValue(generator = \"system-uuid\")\r\n\t");
                sb.append("@GenericGenerator(name = \"system-uuid\", strategy = \"uuid\")\r\n");
            }
            sb.append("\tpublic " + sqlType2JavaType(colTypes[i]) + " get" + tableNameUtil.initTableName(colNames[i])
                    + "(){\r\n");
            sb.append("\t\treturn " + colNames[i] + ";\r\n");
            sb.append("\t}\r\n\r\n");
            sb.append("\tpublic void set" + tableNameUtil.initTableName(colNames[i]) + "("
                    + sqlType2JavaType(colTypes[i]) + " " + colNames[i] + "){\r\n");
            sb.append("\t\t this." + colNames[i] + "=" + colNames[i] + ";\r\n");
            sb.append("\t}\r\n\r\n");

        }

    }

    /**
     * 功能:获得列的数据类型
     * 
     * @param sqlType
     * @return
     */
    private String sqlType2JavaType(String sqlType) {

        if (sqlType.equalsIgnoreCase("binary_double")) {
            return "double";
        } else if (sqlType.equalsIgnoreCase("binary_float")) {
            return "float";
        } else if (sqlType.equalsIgnoreCase("blob")) {
            return "byte[]";
        } else if (sqlType.equalsIgnoreCase("blob")) {
            return "byte[]";
        } else if (sqlType.equalsIgnoreCase("char") || sqlType.equalsIgnoreCase("nvarchar2")
                || sqlType.equalsIgnoreCase("varchar2")) {
            return "String";
        } else if (sqlType.equalsIgnoreCase("date") || sqlType.equalsIgnoreCase("timestamp")
                || sqlType.equalsIgnoreCase("timestamp with local time zone")
                || sqlType.equalsIgnoreCase("timestamp with time zone")) {
            return "Date";
        } else if (sqlType.equalsIgnoreCase("number")) {
            return "Integer";
        }

        return "String";
    }
}

工具类:

import java.io.File;

/**
 * 获取路径
 * 
 * @author cc
 * @createTime 2016年12月22日-下午4:48:06
 * @version 1.0.0
 */
public class GetEntityPathUtil {

    public static String getEntityPath(String tableName) throws Exception {
        File directory = new File("");
        String packageOutPath = "com\\soa\\mdm\\entity";// 指定实体生成所在包的路径
        System.out.println("绝对路径:" + directory.getAbsolutePath());
        /* System.out.println("相对路径:" + directory.getCanonicalPath()); */
        String outputPath = directory.getAbsolutePath() + "\\src\\main\\java\\" + packageOutPath + "\\" + tableName
                + ".java";
        return outputPath;
    }

public class tableNameUtil {

    public static void main(String[] args) {
        // 将表名去掉下划线转化为峰驼式
        String tableName = tableNameUtil.initTableName("code_rules");
        System.out.println(tableName);
        // 将表名全部转化为大写
        String upperCasetableName = tableNameUtil.upperCaseTableName("code_rules");

    }

    /**
     * 功能:将表名转化为实体名称,以下划线分割 如:code_rules => CodeRules
     * 
     * @param str
     * @return
     */
    public static String initTableName(String str) {
        StringBuffer resultStr = new StringBuffer();
        String[] strArr = str.split("_");
        for (String s : strArr) {
            char[] ch = s.toCharArray();
            if (ch[0] >= 'a' && ch[0] <= 'z') {
                ch[0] = (char) (ch[0] - 32);
            }
            resultStr.append(new String(ch));
        }
        return resultStr.toString();
    }

    /**
     * 功能:将表名转化大写 如:code_rules => CODE_RULES
     * 
     * @param str
     * @return
     */
    public static String upperCaseTableName(String str) {
        char[] ch = str.toCharArray();
        StringBuilder sb = new StringBuilder();
        for (char c : ch) {
            if (c >= 'a' && c <= 'z') {
                c = (char) (c - 32);
            }
            sb.append(c);
        }
        return sb.toString();
    }
}


import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
* 读取配置文件
*
* @author
*
*/

public class PropertiesUtil {

public static void main(String[] args) {
    System.out.println(PropertiesUtil.getValue("jdbc.password"));
}

/**
 * 读取配置文件
 * 
 * @param key
 * @return value
 */
public static String getValue(String key) {
    Properties prop = new Properties();
    InputStream in = new PropertiesUtil().getClass().getResourceAsStream("/jdbc.properties");
    try {
        prop.load(in);
    } catch (IOException e) {
        e.printStackTrace();
    }
    return prop.getProperty(key);
}

}


部分工具包没贴出来,比如日期转字符串、JDBC连接数据库等
**效果图:**
![此图为生成的注释、注解等信息](https://img-blog.csdn.net/20161223133528465?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjc5NTQ5NDk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
![此图为生成的注释、注解等信息](https://img-blog.csdn.net/20161223133542356?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjc5NTQ5NDk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
发布了2 篇原创文章 · 获赞 1 · 访问量 3461
展开阅读全文

使用javassist实现动态修改注解,在tomcat下无法获取class路径

12-08

public ExcelBean createExcelBean(String[] propertyArray, String[] typeArray, String[] columnNameArray) throws NotFoundException, CannotCompileException, InstantiationException, IllegalAccessException { ExcelBean excelBean = null; ClassPool pool = ClassPool.getDefault(); //在tomcat环境下,向pool插入一个类对象的搜索路径 pool.insertClassPath("com.camb.common.web.bean.ExcelBean"); //获取要修改的类 CtClass ctClass = pool.get("com.camb.common.web.bean.ExcelBean"); for (int i = 0; i < propertyArray.length; i++) { //生成属性以及get和set方法 String property = propertyArray[i]; String method = property.substring(0, 1).toUpperCase() + property.substring(1, property.length()); String type = typeArray[i]; String column = columnNameArray[i]; ctClass.addField(CtField.make("private " + type + " " + property + ";", ctClass)); ctClass.addMethod(CtMethod.make("public void set" + method + "(" + type + " " + property + "){this." + property + " = " + property + ";}", ctClass)); ctClass.addMethod(CtMethod.make("public " + type + " get" + method + "(){return this." + property + ";}", ctClass)); //获取类里的属性 CtField ctField = ctClass.getField(property); FieldInfo fieldInfo = ctField.getFieldInfo(); System.out.println("属性名:" + ctField.getName()); ConstPool cp = fieldInfo.getConstPool(); //获取注解信息 AnnotationsAttribute attribute = new AnnotationsAttribute(cp, AnnotationsAttribute.visibleTag); Annotation annotation = new Annotation("com.camb.common.web.util.Excel", cp); //修改名称为name的注解 annotation.addMemberValue("name", new StringMemberValue(column, cp)); attribute.setAnnotation(annotation); fieldInfo.addAttribute(attribute); //打印修改后注解 Annotation annotation2 = attribute.getAnnotation("com.camb.common.web.util.Excel"); String value = ((StringMemberValue) annotation2.getMemberValue("name")).getValue(); System.out.println("修改后的注解参数===" + value); } //修改后的class Class<?> c = ctClass.toClass(); excelBean = (ExcelBean) c.newInstance(); ctClass.detach(); return excelBean; } pool.insertClassPath("com.camb.common.web.bean.ExcelBean");这个设置并不能找到class的位置,如果使用pool.insertClassPath(new ClassClassPath(ExcelBean.class));虽然能找class,但是会产生异常 com.camb.common.web.exception.ProcessException: javassist.CannotCompileException: by java.lang.LinkageError: loader (instance of org/apache/catalina/loader/WebappClassLoader): attempted duplicate class definition for name: "com/camb/common/web/bean/ExcelBean" 问答

javassist一个很奇怪的问题

08-20

采用javassist来将一个类中的field改变类型。待改变的类代码如下: [code="java"]public class JassistTest { @Autowired private StpService stpService; public void doit(Long userid){ PeakSeasonMainResponse res = stpService.getPeakOverView(userid); System.out.println(stpService.getClass().getName()); System.out.println("hello" + res); } }[/code] 进行改变操作的类如下: [code="java"]public class TestMain { public TestMain(){ } public static void main(String[] args) throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("JassistTest"); CtField f = cc.getDeclaredField("stpService"); cc.removeField(f); cc.addField(CtField.make("private Proxy stpService;", cc)); cc.writeFile("D:\\DevProgram\\eclipse-jee-kepler-R-win32\\workspace\\stable\\fc-deimos\\target\\test-classes"); JassistTest test = new JassistTest(); test.doit(7060L); } } [/code] class文件生成后,我用反编译工具查看,Javassist已经变为如下: [code="java"]public class JassistTest { private Proxy stpService; public void doit(Long userid) { PeakSeasonMainResponse res = this.stpService.getPeakOverView(userid); System.out.println(this.stpService.getClass().getName()); System.out.println("hello" + res); } }[/code] 但当我执行TestMain.java文件中的test.doit()方法时,还是报异常了。 [code="java"]Exception in thread "main" java.lang.NoSuchFieldError: stpService at JassistTest.doit(JassistTest.java:13) at TestMain.main(TestMain.java:33) [/code] 实在搞不懂,请教大家 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览