基于Ant+Velocity的简单代码生成器的思路与实现
(原文:http://www.javaeye.com/topic/30893)
在SSH项目中,我们应用了service layer模式,所以针对一个模块,它就存在pojo、dao、daoImpl、service、serviceImpl,再到struts中的action、form。假设设计是面向数据库的,针对一个数据库表,那么就要产生7个java文件,如果还要做异常处理,那么就是8个java文件。如果数据库有50个表,那么就是50*8=400个java文件。工程不小。
至于为什么要用service layer模式,论坛上已有讨论http://www.javaeye.com/topic/29867
然而我们都知道,web中出现最多的操作是CURD,这400个java文件中有多少代码是重复的?几乎占了80%甚至更多。编写这样重复的代码是很枯燥无味的,而且如果是由不同人负责不同的模块的分工方式,程序员编码的风格是各不相同(虽然可能有规范约束,但是最后出来的东西还是避免不了的带有程序员个人风格的)。
所以为了节省时间和精力,便做一个程序来生成程序。
只要配置好你的项目名,你的模块名,模块路径,就可以在几秒之内完成一个模块的CURD代码,同时你可以自定义模板。
这是工具的大概设计思路:
由ant处理编译、生成目录的工作,velocity处理程序模板,contentEngine为核心处理程序。
产生的目录结构和代码路径:
模块名
--子模块1
----model
------businessobject
------dao
--------hibernate
----service
------impl
----view
------action
------form
----Exception
--子模块2
...
其中model/businessobject中是pojo和hbm.xml,这个由hibernate工具根据数据库表产生。
我们假设模块名为course,子模块名为table,类名为CourseMember。因篇幅问题,我们只看一个daoImpl的例子。
首先我们利用建立一个daoImpl的模板
ObjectDaoHibernateImpl.vm
- ${package_Hibernate}
- ${import_SQLException}
- ${import_List}
- ${import_HibernateCallback}
- ${import_HibernateObjectRetrievalFailureException}
- ${import_HibernateDaoSupport}
- ${import_HibernateException}
- ${import_Query}
- ${import_Session}
- ${import_ObjectNameDao}
- ${import_ObjectName}
- ${import_Finder}
- ${import_Page}
- ${import_Criteria}
- ${import_Projections}
- /**
- * The Hibernate implementation of the <code>${ObjectName}Dao</code>.
- *
- * @author ${Author}
- * @see ${ObjectName}Dao
- */
- public class ${ObjectName}DaoHibernateImpl extends HibernateDaoSupport implements ${ObjectName}Dao {
- /**
- * Default constructor.
- */
- public ${ObjectName}DaoHibernateImpl() {
- super();
- }
- /**
- * @see ${ObjectName}Dao#save${ObjectName}(${ObjectName})
- */
- public ${ObjectName} save${ObjectName}(${ObjectName} ${objectname}) {
- this.getHibernateTemplate().save(${objectname});
- return ${objectname};
- }
- /**
- * @see ${ObjectName}Dao#get${ObjectName}(String)
- */
- public ${ObjectName} get${ObjectName}(String id) {
- return (${ObjectName})this.getHibernateTemplate().load(${ObjectName}.class, id);
- }
- /**
- * @see ${ObjectName}Dao#update${ObjectName}(${ObjectName})
- */
- public void update${ObjectName}(${ObjectName} ${objectname}) {
- this.getHibernateTemplate().update(${objectname});
- }
- /**
- * @see ${ObjectName}Dao#delete${ObjectName}(${ObjectName})
- */
- public void delete${ObjectName}(${ObjectName} ${objectname}) {
- this.getHibernateTemplate().delete(${objectname});
- }
- /**
- * @see ${ObjectName}Dao#getAll${ObjectName}s()
- */
- public List getAll${ObjectName}s() {
- return getHibernateTemplate().executeFind(new HibernateCallback() {
- public Object doInHibernate(Session session)
- throws HibernateException, SQLException {
- StringBuffer sb = new StringBuffer(100);
- //sb.append("select distinct ${objectname} ");
- sb.append("SELECT ${objectname} ");
- sb.append("FROM ${ObjectName} ${objectname} ");
- sb.append("order by ${objectname}.id");
- Query query = session.createQuery(sb.toString());
- List list = query.list() ;
- return list;
- }
- });
- }
- public Object query(final ${ObjectName} ${objectname},
- final int pageNo, final int maxResult) {
- return getHibernateTemplate().execute(new HibernateCallback() {
- public Object doInHibernate(Session session)
- throws HibernateException, SQLException {
- Criteria criteria=session.createCriteria(${ObjectName}.class);
- Criteria anothercriteria=session.createCriteria(${ObjectName}.class);
- criteria.setProjection(Projections.rowCount());
- // if (!${objectname}.get${objectname}Name().equals("")
- // && ${objectname}.get${objectname}Name() != null) {
- // criteria.add(Expression.ilike("contactName","%"+customerContactForm.getContactName()+"%"));
- // anothercriteria.add(Expression.ilike("contactName","%"+customerContactForm.getContactName()+"%"));
- // }
- Integer count=(Integer)criteria.uniqueResult();
- List list=anothercriteria.setFirstResult((pageNo-1)*maxResult).setMaxResults(maxResult).list();
- Page page=new Page(count.intValue(), maxResult, pageNo);
- return new Finder(list, page);
- }
- });
- }
- public boolean deleteBybatch(final String[] chxSong) {
- StringBuffer cusIdList = new StringBuffer(200);
- cusIdList.append("delete from ${ObjectName} where ${objectName}No=");
- for (int i = 0; i < chxSong.length; i++) {
- if (i == 0)
- cusIdList.append(chxSong[i]);
- else
- cusIdList.append(" or ${objectName}No=" + chxSong[i]);
- }
- this.getSession().createQuery(cusIdList.toString()).executeUpdate();
- return true;
- }
- }
声明:
1)其中${}是模板语言中的变量,变量的来源一是通过对应的.properties文件,另外是通过参数传递。
2)注释部分因是分页查询条件,这个涉及到具体字段,无法预知,所以需要在产生代码之后程序员根据查询条件自行修改。另外也涉及到个人项目的分页方法,这个根据具体情况自定义模板。
template.properties
公共属性文件,是所有template文件(.vm)的变量声明处,这个会在后面代码中进行设置。
对于属性文件,可有两种方式:
一是针对每一个template模板文件都建立一个属性文件,优点是在后面ant中设置的参数就少了,而且方便修改。缺点是模板文件数量增多,另外公共部分声明重复。
二是设定一个公共属性文件,将特定的变量交给参数传递。
我们这里先用公共属性文件的方式。
- Author = Cmas R&D Team
- import_Arraylist = import java.util.ArrayList;
- import_List = import java.util.List;
- import_Set = import java.util.Set;
- import_FacesException = import javax.faces.FacesException;
- import_BeanUtils = import org.apache.commons.beanutils.BeanUtils;
- import_Log = import org.apache.commons.logging.Log;
- import_LogFactory = import org.apache.commons.logging.LogFactory;
- import_SQLException = import java.sql.SQLException;
- import_HibernateCallback = import org.springframework.orm.hibernate3.HibernateCallback;
- import_HibernateObjectRetrievalFailureException = import org.springframework.orm.hibernate3.HibernateObjectRetrievalFailureException;
- import_HibernateDaoSupport = import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
- import_HibernateException = import org.hibernate.HibernateException;
- import_Query = import org.hibernate.Query;
- import_Session = import org.hibernate.Session;
- import_Map = import java.util.Map;
- import_HashMap = import java.util.HashMap;
- import_Iterator = import java.util.Iterator;
- import_Criteria=import org.hibernate.Criteria;
- import_Projections=import org.hibernate.criterion.Projections;
- import_DispatchActionSupport=import org.springframework.web.struts.DispatchActionSupport;
- import_Action=import org.apache.struts.action.*;
- import_HttpServletRequest=import javax.servlet.http.HttpServletRequest;
- import_HttpServletResponse=import javax.servlet.http.HttpServletResponse;
- import_BeanUtils=import org.apache.commons.beanutils.BeanUtils;
- import_DataIntegrity=import org.springframework.dao.DataIntegrityViolationException;
接下来是ant部分,我们编写build.xml
build.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <project name="cmas" basedir="../" default="all">
- <!-- Project settings -->
- <property name="project.distname" value="cmas" /><!-- 设定项目名 -->
- <property name="project/operationName" value="course/table" /><!-- 设定模块名,如果有多层以“/”方式扩充,此为目录结构变量设定 -->
- <property name="project.operationName" value="course.table" /><!-- 设定模块名,如果有多层以“.”方式扩充,此为包结构变量设定 -->
- <property name="ObjectName" value="CourseMember" /><!-- 模块名类名,大写 -->
- <property name="objectName" value="courseMember" /><!-- 模块名变量名,小写 -->
- <!-- Local system paths -->
- <property file="${basedir}/ant/build.properties" /><!-- 设定ant的一些属性,这里我们没有额外的设置,使用默认 -->
- <property file="${basedir}/${webroot.dir}/template/build.properties" />
- <!--Save_path-->
- <!-- 建立目录结构 -->
- <mkdir dir="${basedir}/JavaSource/com/bnu/${project.distname}/${project/operationName}/model" />
- <mkdir dir="${basedir}/JavaSource/com/bnu/${project.distname}/${project/operationName}/service" />
- <mkdir dir="${basedir}/JavaSource/com/bnu/${project.distname}/${project/operationName}/view" />
- <!-- 声明目录结构变量 -->
- <property name="model.src.dir" location="${basedir}/JavaSource/com/bnu/${project.distname}/${project/operationName}/model" />
- <property name="service.src.dir" location="${basedir}/JavaSource/com/bnu/${project.distname}/${project/operationName}/service" />
- <property name="view.src.dir" location="${basedir}/JavaSource/com/bnu/${project.distname}/${project/operationName}/view" />
- <property name="overwrite" value="false" />
- <property name="debug" value="true" />
- <property name="webroot.dir" value="${basedir}/WebContent" />
- <property name="webinf.dir" value="${webroot.dir}/WEB-INF" />
- <property name="build.dir" value="build" />
- <!-- 模板文件的声明,这里暂时只写ObjectDaoHibernateImpl -->
- <property name="template.dir" value="${webroot.dir}/template" />
- <property name="ObjectDaoHibernateImpl.template" value="./ObjectDaoHibernateImpl.vm" />
- <property name="template.properties" value="${template.dir}/template.properties" />
- <!--设定classpath,这些包不能少-->
- <property name="classpath" value="${webinf.dir}/classes/" />
- <!-- classpath for JSF 1.1.01 -->
- <path id="compile.classpath">
- <pathelement path="${webinf.dir}/lib/hibernate3.jar" />
- <pathelement path="${webinf.dir}/lib/log4j-1.2.9.jar" />
- <pathelement path="${webinf.dir}/lib/commons-beanutils.jar" />
- <pathelement path="${webinf.dir}/lib/commons-collections.jar" />
- <pathelement path="${webinf.dir}/lib/commons-digester.jar" />
- <pathelement path="${webinf.dir}/lib/commons-logging.jar" />
- <pathelement path="${webinf.dir}/lib/jsf-api.jar" />
- <pathelement path="${webinf.dir}/lib/jsf-impl.jar" />
- <pathelement path="${webinf.dir}/lib/jstl.jar" />
- <pathelement path="${webinf.dir}/lib/standard.jar" />
- <pathelement path="${webinf.dir}/lib/log4j.jar" />
- <pathelement path="${webinf.dir}/lib/velocity-1.4.jar" />
- <pathelement path="${webinf.dir}/lib/velocity-1.4-dev.jar" />
- <pathelement path="${webinf.dir}/classes" />
- <pathelement path="${classpath.external}" />
- <pathelement path="${classpath}" />
- </path>
- <!--*****************Build_Dao_Hibernate_Impl*开始创建daoImpl**********************-->
- <!-- define your folder for deployment -->
- <property name="build_daoimpl.dir" value="build_daoimpl" />
- <!-- Check timestamp on files -->
- <target name="build_daoimpl_prepare">
- <tstamp />
- </target>
- <!-- Copy any resource or configuration files -->
- <target name="build_daoimpl_resources">
- <copy todir="${webinf.dir}/classes" includeEmptyDirs="no">
- <fileset dir="JavaSource">
- <patternset>
- <include name="**/*.conf" />
- <include name="**/*.properties" />
- <include name="**/*.xml" />
- </patternset>
- </fileset>
- </copy>
- </target>
- <target name="build_daoimpl_init">
- <!-- Create the time stamp -->
- <tstamp />
- <!-- Create the build directory structure used by compile -->
- <mkdir dir="${model.src.dir}/dao/hibernate" />
- </target>
- <!-- Normal build of application -->
- <target name="build_daoimpl_compile" depends="build_daoimpl_prepare,build_daoimpl_resources,build_daoimpl_init">
- <javac srcdir="${basedir}/JavaSource/com/bnu/exception/" destdir="${webinf.dir}/classes/">
- <classpath refid="compile.classpath" />
- </javac>
- <!--编译核心java文件contentEngine,这个路径根据具体情况设定,也可以在前面对其进行统一声明-->
- <javac srcdir="${basedir}/JavaSource/com/bnu/tools" destdir="${webinf.dir}/classes/">
- <classpath refid="compile.classpath" />
- </javac>
- <!--运行contentEngine,参数设定-->
- <java classname="com.bnu.tools.ContentEngine">
- <classpath refid="compile.classpath" />
- <arg value="DaoImpl" />
- <arg value="${template.dir}" />
- <arg value="${template.properties}" />
- <arg value="${ObjectDaoHibernateImpl.template}" />
- <arg value="package com.bnu.${project.distname}.${project.operationName}.model.dao.hibernate;" />
- <arg value="import com.bnu.${project.distname}.${project.operationName}.model.dao.${ObjectName}Dao;" />
- <arg value="import com.bnu.${project.distname}.${project.operationName}.model.businessobject.${ObjectName};" />
- <arg value="${objectName}" />
- <arg value="${ObjectName}" />
- <arg value="${model.src.dir}/dao/hibernate" />
- <arg value="${ObjectName}DaoHibernateImpl.java" />
- </java>
- </target>
- <!-- Remove classes directory for clean build -->
- <target name="build_daoimpl_clean" description="Prepare for clean build">
- <delete dir="${webinf.dir}/classes" />
- <mkdir dir="${webinf.dir}/classes" />
- </target>
- <!-- Build entire project -->
- <target name="build_daoimpl_build" depends="build_daoimpl_prepare,build_daoimpl_compile" />
- <target name="build_daoimpl_rebuild" depends="build_daoimpl_clean,build_daoimpl_prepare,build_daoimpl_compile" />
- <target name="build_daoimpl" depends="build_daoimpl_build">
- <delete file="${build_daoimpl.dir}/${project.distname}.war" />
- <delete dir="${build_daoimpl.dir}/${project.distname}" />
- </target>
- <target name="clean" description="clean">
- <delete dir="${build.dir}" />
- <delete dir="${webinf.dir}/classes" />
- <delete dir="${dist.dir}" />
- </target>
- <target name="all" description="build all" depends="clean,build_daoimpl">
- </target>
- </project>
这里摘取了daoImpl的声明段,重要部分已经做了注释。
核心代码部分,contentEngine文件。
- package com.bnu.tools;
- import org.apache.velocity.Template;
- import org.apache.velocity.VelocityContext;
- import org.apache.velocity.app.Velocity;
- import org.apache.velocity.exception.ParseErrorException;
- import org.apache.velocity.exception.ResourceNotFoundException;
- import com.bnu.exception.AppException;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.io.PrintWriter;
- import java.io.StringWriter;
- import java.util.Iterator;
- import java.util.Properties;
- /**
- *
- * To change the template for this generated type comment go to
- * Window>Preferences>Java>Code Generation>Code and Comments
- */
- public class ContentEngine {
- private VelocityContext context = null;
- private Template template = null;
- // private String properties = null ;
- /**
- *
- * @param properties
- * @throws Exception
- */
- public void init(String properties) throws Exception {
- if (properties != null && properties.trim().length() > 0) {
- Velocity.init(properties);
- } else {
- Velocity.init();
- }
- context = new VelocityContext();
- }
- public void init(Properties properties) throws Exception {
- Velocity.init(properties);
- context = new VelocityContext();
- }
- /**
- *
- * @param key
- * @param value
- */
- public void put(String key, Object value) {
- context.put(key, value);
- }
- /**
- * 设置模版
- *
- * @param templateFile
- * 模版文件
- * @throws AppException
- */
- public void setTemplate(String templateFile) throws AppException {
- try {
- template = Velocity.getTemplate(templateFile);
- } catch (ResourceNotFoundException rnfe) {
- rnfe.printStackTrace();
- throw new AppException(" error : cannot find template "
- + templateFile);
- } catch (ParseErrorException pee) {
- throw new AppException(" Syntax error in template " + templateFile
- + ":" + pee);
- } catch (Exception e) {
- throw new AppException(e.toString());
- }
- }
- /**
- * 设置模版
- *
- * @param templateFile
- * 模版文件
- * @throws AppException
- */
- public void setTemplate(String templateFile, String characterSet)
- throws AppException {
- try {
- template = Velocity.getTemplate(templateFile, characterSet);
- } catch (ResourceNotFoundException rnfe) {
- rnfe.printStackTrace();
- throw new AppException(" error : cannot find template "
- + templateFile);
- } catch (ParseErrorException pee) {
- throw new AppException(" Syntax error in template " + templateFile
- + ":" + pee);
- } catch (Exception e) {
- throw new AppException(e.toString());
- }
- }
- /**
- * 转换为文本文件
- */
- public String toText() throws AppException {
- StringWriter sw = new StringWriter();
- try {
- template.merge(context, sw);
- } catch (Exception e) {
- throw new AppException(e.toString());
- }
- return sw.toString();
- }
- /**
- *
- * @param fileName
- */
- public void toFile(String fileName) throws AppException {
- try {
- StringWriter sw = new StringWriter();
- template.merge(context, sw);
- PrintWriter filewriter = new PrintWriter(new FileOutputStream(
- fileName), true);
- filewriter.println(sw.toString());
- filewriter.close();
- } catch (Exception e) {
- throw new AppException(e.toString());
- }
- }
- public static void main(String[] args) {
- ContentEngine content = new ContentEngine();
- try {
- Properties p = new Properties();
- Properties varp = new Properties();
- String path = args[1];
- p.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, path);
- p.setProperty(Velocity.RUNTIME_LOG, path + "velocity.log");
- content.init(p);
- FileInputStream in = new FileInputStream(args[2]);
- varp.load(in);
- content.setTemplate(args[3], "gb2312");
- Iterator it = varp.keySet().iterator();
- String key = "";
- String value = "";
- while (it.hasNext()) {
- key = (String) it.next();
- value = varp.getProperty(key);
- content.put(key, value);
- }
- if (args[0].equals("DaoImpl")) {
- content.put("package_Hibernate", args[4]);
- content.put("import_ObjectNameDao", args[5]);
- content.put("import_ObjectName", args[6]);
- content.put("objectname", args[7]);
- content.put("ObjectName", args[8]);
- content.toFile(args[9] + '/' + args[10]);//导出的路径,由参数传递。
- }
- //else 其他情况处理部分,这里省略。
- } catch (AppException ae) {
- ae.printStackTrace();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
至此,这个简单的代码生成器的代码就结束了。很显然它还很弱小,充其量也只是半自动。离完善的代码生成器还差很远。拿出来希望对大家有点用处,另外也希望得到各位的指导,大家讨论一下代码生成器的话题。