该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2006-10-27
至于为什么要用service layer模式,论坛上已有讨论http://www.iteye.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; 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2006-10-27
接下来是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的声明段,重要部分已经做了注释。 |
|
返回顶楼 | |
发表时间:2006-10-27
核心代码部分,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(); } } } 至此,这个简单的代码生成器的代码就结束了。很显然它还很弱小,充其量也只是半自动。离完善的代码生成器还差很远。拿出来希望对大家有点用处,另外也希望得到各位的指导,大家讨论一下代码生成器的话题。 |
|
返回顶楼 | |
发表时间:2006-10-27
没人做过类似的东西吗?想把这个工具升级一下,讨论一下希望能得到新的思路。
|
|
返回顶楼 | |
发表时间:2006-10-27
生成java代码的话用Java5的APT机制比较正统一些, 是和编译结合在一起的, APT会自动调用Annotation Processor, 对生成的新的Java代码会自动编译, 如果还有Annotation就在再迭代, 最后所有的原始代码和生成的代码都编译好以后才结束.
不过ANT目前还不官方支持APT, 结合有点烦琐. |
|
返回顶楼 | |
发表时间:2006-10-28
complystill 写道 生成java代码的话用Java5的APT机制比较正统一些, 是和编译结合在一起的, APT会自动调用Annotation Processor, 对生成的新的Java代码会自动编译, 如果还有Annotation就在再迭代, 最后所有的原始代码和生成的代码都编译好以后才结束.
不过ANT目前还不官方支持APT, 结合有点烦琐. java5 的APT机制,没有了解过。搜索过也是很少资料,看你的描述它的编译和迭代功能还是比较吸引的。能不能给点资料来参考一下呢。^^ |
|
返回顶楼 | |
发表时间:2006-10-28
代码生成的主要几种实现方式(来自Jack Herrington《Code Generation in action》)
1、模板技术:代码生成通常意味着创建具有复杂结构文本类型的文件。为了维护生成器的简单性和完整性,你可以使用文本模板工具,这样你可以把逻辑的定义和逻辑的代码格式相分离,这是最理想的情况。 模板一直以来都是CodeGeneration的主要实现手段。C++使用模板来实现泛型机制。而众多的建模工具和IDE工具都使用模板来重用代码。在Apache组织的旗下,有一个名为Velocity的项目,它的目的就是提供一种易用的模板技术。Velocity使用了一种脚本语言,叫做Velocity Template Language (VTL)。目前,Velocity主要用于生成Web站点的动态内容。此外,还有更出色的FreeMaker,它比Velocity更全面,可以说Velocity是轻量级的模板技术,而Freemaker是一个强大重量的模板引擎,相比velocity而言,其强大的过程调用、递归和闭包回调功能让freemaker可以完成几乎所有我们所想的功能。此外还有很多的模板技术,这里就不一一介绍了。 2、面向属性编程 英文缩写虽然同样都是AOP,但面向属性编程(Attribute-Oriented Programming )和面向切面编程(Aspect-Oriented Programming )可不是一回事。面向属性编程是通过在代码中添加元数据(属性)的方式来自动产生代码,添加功能。而这方面最优秀的软件莫过于xDoclet。 XDoclet 是一个通用的代码生成实用程序,是一个扩展的Javadoc Doclet引擎(现已与Javadoc Doclet独立),XDoclet是EJBDoclet的后继者,而EJBDoclet是由Rickard Oberg发起的。它允许您使用象 JavaDoc 标记之类的东西来向诸如类、方法和字段之类的语言特征添加元数据。随后,它利用这些额外的元数据来生成诸如部署描述符和源代码之类的相关文件。可以让你创建自己的javadoc @tags进而利用XDoclet中的Templet enging基于这些@tags生成源代码或其他文件。 3、MDA(Model Driven Architecture模型驱动架构) MDA能够将特定的模型转换为特定平台的代码。AndraMDA就是其中的代表者。在具体实现上,AndraMDA主要采用了模板技术,同时,它也利用了xDoclet技术。所以,AndraMDA是在前两项技术的基础上实现CodeGeneration的。 ------------- 其他还有一些,但是不常用。模板技术无疑是用的最广泛的,比如说IDE中就有大量的模板。 |
|
返回顶楼 | |
发表时间:2006-10-29
思想还是不错的!如果能把模版技术与xml技术用得如火纯青,我想业务代码生成器这样的工具也是指日可待de!
|
|
返回顶楼 | |
发表时间:2006-10-29
引用 对于复杂流程的代码生成, 更高级一点的方式是使用antlr一类的语言识别和解释程序, 适合生成流程很复杂的代码, 如mda, dsl的应用, antlr的语句难写也难看懂一点, 如果仅仅是生成crud, 用template, xdoclet就好了
Jack Herrington选择了ruby,在2000年写《code generation》一书的时候他就看中了ruby的作为脚本语言的轻便。选择ruby大肆流行,不得不说jack有眼力。 有个想法,能不能把这个思想延伸到页面的生成。在有几个项目的开发中,页面处理花费了大部分时间,远远超过后台代码的编写时间。而且如果页面上的代码有所不同,体现出来就会给用户很大很直接的不同;如果后台代码实现不同,但是功能实现成功,用户是无法知道的。 页面的组件化,现在也做了很多。比如jsf中就有一个很好的分页组件。如果将常用控件做成模版,引用时传递你需要显示的数据,那么页面处理的时间将大大缩短。模版做成组件,组件搭建前台。 应该很多公司都致力于类似这种工具的开发吧。 |
|
返回顶楼 | |
发表时间:2006-10-29
pedestrian_I 写道 complystill 写道 生成java代码的话用Java5的APT机制比较正统一些, 是和编译结合在一起的, APT会自动调用Annotation Processor, 对生成的新的Java代码会自动编译, 如果还有Annotation就在再迭代, 最后所有的原始代码和生成的代码都编译好以后才结束.
不过ANT目前还不官方支持APT, 结合有点烦琐. java5 的APT机制,没有了解过。搜索过也是很少资料,看你的描述它的编译和迭代功能还是比较吸引的。能不能给点资料来参考一下呢。^^ TOB (http://tob.ableverse.org) 就是用的APT机制, 不过TOB本身目前还没有开源计划, 教程也还没写好. WoW (http://www.webofweb.net) 是基于TOB开发的, 开源的, 如果想体验一下, 可以到 http://wow.dev.java.net 下载源码自己编译一回看. 不过现在Eclipse的APT支持还有bug, 必须通过JDK的APT来生成代码, 而JDK1.5的javac也有bug, 生成出来的java代码还必须通过Eclipse来编译. 比较郁闷. WoW的项目代码包里有个 BUILDING 文件, 按照那里的步骤可以成功完成编译. |
|
返回顶楼 | |