首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > Ruby Rails >

施用Spring Roo ,感受ROR式的开发

2012-11-10 
使用Spring Roo ,感受ROR式的开发Roo是一种 Spring 开发的辅助工具,使用命令行操作来生成自动化项目,操作

使用Spring Roo ,感受ROR式的开发
Roo是一种 Spring 开发的辅助工具,使用命令行操作来生成自动化项目,操作非常类似于rails

我这里使用spring tool suit来开发一个demo项目

首先新建个spring template project,然后选择template类型为Roo Wep App (based on Roo 1.0.0.RC1)
这样,便会通过roo的命令自动生成一个标准的maven web项目,里面含有最基本的Spring 配置

建好项目后,我们可以在project上运行roo shell
通过hint命令,我们可以很快的了解各种操作提示

首先,设置数据库

install jpa -provider HIBERNATE -database MYSQL

roo会帮你在配置文件applicationContext.xml中配置好了相应的数据源配置,并且在 pom.xml中已经添加jpa和数据库驱动等相关的依赖。

然后新建一个domain
new persistent class jpa -name ~.domain.Employee -testAutomatically


这时roo会帮我们生成Employee类,这时Employee类还没有任何字段,我们可以通过roo shell往里面加入一些字段

add field string -fieldName name -notNull -sizeMax 200add field date jpa -fieldName birth -type java.util.Date......


最终生成的Employee如下:
@Entity@RooEntity@RooJavaBean@RooToStringpublic class Employee {    @NotNull    @Size(max = 200)    private String name;    @Temporal(TemporalType.TIMESTAMP)    private Date birth;}


你会发现,怎么没有get set呢?我们反编译看下编译后的class代码
// Decompiled by DJ v3.5.5.77 Copyright 2003 Atanas Neshkov  Date: 2009-8-8 21:37:43// Home Page : http://members.fortunecity.com/neshkov/dj.html  - Check often for new version!// Decompiler options: packimports(3) // Source File Name:   Employee.javapackage com.javaeye.domain;import java.util.Date;import java.util.List;import javax.persistence.EntityManager;import org.aspectj.runtime.reflect.Factory;import org.springframework.beans.factory.annotation.Configurable;import org.springframework.beans.factory.aspectj.*;// Referenced classes of package com.javaeye.domain://            Employee_Roo_ToString, Employee_Roo_Entity, Employee_Roo_JavaBeanpublic class Employee    implements ConfigurableObject{    public Employee()    {        org.aspectj.lang.JoinPoint joinpoint1 = Factory.makeJP(ajc$tjp_1, this, this);        org.aspectj.lang.JoinPoint joinpoint = Factory.makeJP(ajc$tjp_0, this, this);        if(this != null && getClass().isAnnotationPresent(org/springframework/beans/factory/annotation/Configurable) && AnnotationBeanConfigurerAspect.ajc$if_1((Configurable)getClass().getAnnotation(org/springframework/beans/factory/annotation/Configurable)))            AnnotationBeanConfigurerAspect.aspectOf().ajc$before$org_springframework_beans_factory_aspectj_AbstractDependencyInjectionAspect$1$e854fa65(this);        if(this != null && getClass().isAnnotationPresent(org/springframework/beans/factory/annotation/Configurable) && (this == null || !getClass().isAnnotationPresent(org/springframework/beans/factory/annotation/Configurable) || !AnnotationBeanConfigurerAspect.ajc$if_1((Configurable)getClass().getAnnotation(org/springframework/beans/factory/annotation/Configurable))) && AbstractDependencyInjectionAspect.ajc$if_0(joinpoint))            AnnotationBeanConfigurerAspect.aspectOf().ajc$afterReturning$org_springframework_beans_factory_aspectj_AbstractDependencyInjectionAspect$2$1ea6722c(this);        Employee_Roo_Entity.ajc$interFieldInit$com_javaeye_domain_Employee_Roo_Entity$com_javaeye_domain_Employee$entityManager(this);        Employee_Roo_Entity.ajc$interFieldInit$com_javaeye_domain_Employee_Roo_Entity$com_javaeye_domain_Employee$id(this);        Employee_Roo_Entity.ajc$interFieldInit$com_javaeye_domain_Employee_Roo_Entity$com_javaeye_domain_Employee$version(this);        if(!AnnotationBeanConfigurerAspect.ajc$if_1((Configurable)getClass().getAnnotation(org/springframework/beans/factory/annotation/Configurable)) && AbstractDependencyInjectionAspect.ajc$if_0(joinpoint1))            AnnotationBeanConfigurerAspect.aspectOf().ajc$afterReturning$org_springframework_beans_factory_aspectj_AbstractDependencyInjectionAspect$2$1ea6722c(this);    }    public static String ajc$privFieldGet$com_javaeye_domain_Employee_Roo_JavaBean$com_javaeye_domain_Employee$name(Employee employee)    {        return employee.name;    }    public static void ajc$privFieldSet$com_javaeye_domain_Employee_Roo_JavaBean$com_javaeye_domain_Employee$name(Employee employee, String s)    {        employee.name = s;    }    public static Date ajc$privFieldGet$com_javaeye_domain_Employee_Roo_JavaBean$com_javaeye_domain_Employee$birth(Employee employee)    {        return employee.birth;    }    public static void ajc$privFieldSet$com_javaeye_domain_Employee_Roo_JavaBean$com_javaeye_domain_Employee$birth(Employee employee, Date date)    {        employee.birth = date;    }    public static long countEmployees()    {        return Employee_Roo_Entity.ajc$interMethod$com_javaeye_domain_Employee_Roo_Entity$com_javaeye_domain_Employee$countEmployees();    }    public static EntityManager entityManager()    {        return Employee_Roo_Entity.ajc$interMethod$com_javaeye_domain_Employee_Roo_Entity$com_javaeye_domain_Employee$entityManager();    }    public static List findAllEmployees()    {        return Employee_Roo_Entity.ajc$interMethod$com_javaeye_domain_Employee_Roo_Entity$com_javaeye_domain_Employee$findAllEmployees();    }    public static Employee findEmployee(Long long1)    {        return Employee_Roo_Entity.ajc$interMethod$com_javaeye_domain_Employee_Roo_Entity$com_javaeye_domain_Employee$findEmployee(long1);    }    public static List findEmployeeEntries(int i, int j)    {        return Employee_Roo_Entity.ajc$interMethod$com_javaeye_domain_Employee_Roo_Entity$com_javaeye_domain_Employee$findEmployeeEntries(i, j);    }    public void flush()    {Employee_Roo_Entity.ajc$interMethod$com_javaeye_domain_Employee_Roo_Entity$com_javaeye_domain_Employee$flush(this);    }    public Date getBirth()    {        return Employee_Roo_JavaBean.ajc$interMethod$com_javaeye_domain_Employee_Roo_JavaBean$com_javaeye_domain_Employee$getBirth(this);    }    public Long getId()    {        return Employee_Roo_Entity.ajc$interMethod$com_javaeye_domain_Employee_Roo_Entity$com_javaeye_domain_Employee$getId(this);    }    public String getName()    {        return Employee_Roo_JavaBean.ajc$interMethod$com_javaeye_domain_Employee_Roo_JavaBean$com_javaeye_domain_Employee$getName(this);    }    public Integer getVersion()    {        return Employee_Roo_Entity.ajc$interMethod$com_javaeye_domain_Employee_Roo_Entity$com_javaeye_domain_Employee$getVersion(this);    }    public void merge()    {   Employee_Roo_Entity.ajc$interMethod$com_javaeye_domain_Employee_Roo_Entity$com_javaeye_domain_Employee$merge(this);    }    public void persist()    {     Employee_Roo_Entity.ajc$interMethod$com_javaeye_domain_Employee_Roo_Entity$com_javaeye_domain_Employee$persist(this);    }    public void remove()    {        Employee_Roo_Entity.ajc$interMethod$com_javaeye_domain_Employee_Roo_Entity$com_javaeye_domain_Employee$remove(this);    }    public void setBirth(Date date)    {        Employee_Roo_JavaBean.ajc$interMethod$com_javaeye_domain_Employee_Roo_JavaBean$com_javaeye_domain_Employee$setBirth(this, date);    }    public void setId(Long long1)    {        Employee_Roo_Entity.ajc$interMethod$com_javaeye_domain_Employee_Roo_Entity$com_javaeye_domain_Employee$setId(this, long1);    }    public void setName(String s)    {        Employee_Roo_JavaBean.ajc$interMethod$com_javaeye_domain_Employee_Roo_JavaBean$com_javaeye_domain_Employee$setName(this, s);    }    public void setVersion(Integer integer)    {        Employee_Roo_Entity.ajc$interMethod$com_javaeye_domain_Employee_Roo_Entity$com_javaeye_domain_Employee$setVersion(this, integer);    }    public String toString()    {        return Employee_Roo_ToString.ajc$interMethod$com_javaeye_domain_Employee_Roo_ToString$com_javaeye_domain_Employee$toString(this);    }    private String name;    private Date birth;    public transient EntityManager ajc$interField$com_javaeye_domain$entityManager;    public Long ajc$interField$com_javaeye_domain_Employee_Roo_Entity$id;    public Integer ajc$interField$com_javaeye_domain_Employee_Roo_Entity$version;    private static final org.aspectj.lang.JoinPoint.StaticPart ajc$tjp_0; /* synthetic field */    private static final org.aspectj.lang.JoinPoint.StaticPart ajc$tjp_1; /* synthetic field */    static     {        Factory factory = new Factory("Employee.java", Class.forName("com.javaeye.domain.Employee"));        ajc$tjp_0 = factory.makeSJP("initialization", factory.makeConstructorSig("1", "org.springframework.beans.factory.aspectj.ConfigurableObject", "", "", ""), 17);        ajc$tjp_1 = factory.makeSJP("initialization", factory.makeConstructorSig("1", "com.javaeye.domain.Employee", "", "", ""), 17);    }}


经过反编译生成的class发现,通过domain类上面的roo注释,自动在类编译时加入get set,甚至我们发现了findAllEmployees,persist,remove,哈,这不是我们梦寐以求的充血模型吗?

原来,roo在为我们创建domain的时候自动为同一个domain创建了4个aspect(.aj files),并在编译期动态为domain切入代码.
4个aspect分别是:
domainName_Roo_Configurable.aj(配置)
domainName_Roo_Entity.aj(加入orm持久)
domainName_Roo_JavaBean.aj(为字段生成get set)
domainName_Roo_ToString.aj(重写toString)


可见,使用roo我们彻底摆脱了在daomain里对各个属性写get set,并且使domain摇身一变为充血。

下一步,就是建Controller了,同样是一条语句
new controller automatic -formBackingObject ~.domain.Employee -name ~.web.CommentController


你会发现,EmployeeController,spring mvc的配置以及curd的jsp页面全部生成了

EmployeeController代码如下:
@RooWebScaffold(automaticallyMaintainView = true, formBackingObject = Employee.class)@RequestMapping("/employee/**")@Controllerpublic class EmployeeController {}


在这里出现了一行@RooWebScaffold注解,做过rails的人会想,这不是rails里面的scaffold吗?没错,通过@RooWebScaffold注解,EmployeeController自动获得了curd的所有功能。

同样是生成controllerName_Roo_Controller.aj在编译期织入代码

我们来看看生成的EmployeeController_Roo_Controller.aj:

package com.javaeye.web;privileged aspect EmployeeController_Roo_Controller {        @org.springframework.web.bind.annotation.RequestMapping(value = "/employee", method = org.springframework.web.bind.annotation.RequestMethod.POST)        public java.lang.String EmployeeController.create(@org.springframework.web.bind.annotation.ModelAttribute("employee") com.javaeye.domain.Employee employee, org.springframework.validation.BindingResult result) {            if (employee == null) throw new IllegalArgumentException("A employee is required");                for(javax.validation.ConstraintViolation<com.javaeye.domain.Employee> constraint : javax.validation.Validation.buildDefaultValidatorFactory().getValidator().validate(employee)) {                    result.rejectValue(constraint.getPropertyPath(), null, constraint.getMessage());                    }                if (result.hasErrors()) {                    return "employee/create";                    }                employee.persist();                return "redirect:/employee/" + employee.getId();            }            @org.springframework.web.bind.annotation.RequestMapping(value = "/employee/form", method = org.springframework.web.bind.annotation.RequestMethod.GET)        public java.lang.String EmployeeController.createForm(org.springframework.ui.ModelMap modelMap) {            modelMap.addAttribute("employee", new com.javaeye.domain.Employee());                return "employee/create";            }            @org.springframework.web.bind.annotation.RequestMapping(value = "/employee/{id}", method = org.springframework.web.bind.annotation.RequestMethod.GET)        public java.lang.String EmployeeController.show(@org.springframework.web.bind.annotation.PathVariable("id") java.lang.Long id, org.springframework.ui.ModelMap modelMap) {            if (id == null) throw new IllegalArgumentException("An Identifier is required");                modelMap.addAttribute("employee", com.javaeye.domain.Employee.findEmployee(id));                return "employee/show";            }            @org.springframework.web.bind.annotation.RequestMapping(value = "/employee", method = org.springframework.web.bind.annotation.RequestMethod.GET)        public java.lang.String EmployeeController.list(org.springframework.ui.ModelMap modelMap) {            modelMap.addAttribute("employees", com.javaeye.domain.Employee.findAllEmployees());                return "employee/list";            }            @org.springframework.web.bind.annotation.RequestMapping(method = org.springframework.web.bind.annotation.RequestMethod.PUT)        public java.lang.String EmployeeController.update(@org.springframework.web.bind.annotation.ModelAttribute("employee") com.javaeye.domain.Employee employee, org.springframework.validation.BindingResult result) {            if (employee == null) throw new IllegalArgumentException("A employee is required");                for(javax.validation.ConstraintViolation<com.javaeye.domain.Employee> constraint : javax.validation.Validation.buildDefaultValidatorFactory().getValidator().validate(employee)) {                    result.rejectValue(constraint.getPropertyPath(), null, constraint.getMessage());                    }                if (result.hasErrors()) {                    return "employee/update";                    }                employee.merge();                return "redirect:/employee/" + employee.getId();            }            @org.springframework.web.bind.annotation.RequestMapping(value = "/employee/{id}/form", method = org.springframework.web.bind.annotation.RequestMethod.GET)        public java.lang.String EmployeeController.updateForm(@org.springframework.web.bind.annotation.PathVariable("id") java.lang.Long id, org.springframework.ui.ModelMap modelMap) {            if (id == null) throw new IllegalArgumentException("An Identifier is required");                modelMap.addAttribute("employee", com.javaeye.domain.Employee.findEmployee(id));                return "employee/update";            }            @org.springframework.web.bind.annotation.RequestMapping(value = "/employee/{id}", method = org.springframework.web.bind.annotation.RequestMethod.DELETE)        public java.lang.String EmployeeController.delete(@org.springframework.web.bind.annotation.PathVariable("id") java.lang.Long id) {            if (id == null) throw new IllegalArgumentException("An Identifier is required");                com.javaeye.domain.Employee.findEmployee(id).remove();                return "redirect:/employee";            }            @org.springframework.web.bind.annotation.InitBinder        public void EmployeeController.initBinder(org.springframework.web.bind.WebDataBinder binder) {            binder.registerCustomEditor(java.util.Date.class, new org.springframework.beans.propertyeditors.CustomDateEditor(new java.text.SimpleDateFormat("yyyy-M-d"), false));            }        }

这样,Controller里不用写一行代码,就自动拥有了curd,而且,最新的roo使用了spring3.0,还支持了rest

好了,就这么简单,一个domain,一个Controller,我们就可以发布到tomcat中运行了。
不到5分钟,我们就可以生成了一个可运行的项目,是不是很有ror式的感觉啊?赶快来试试吧!







3 楼 zspzlxn 2009-11-06   太强了。。。领教了。。这种手法还是第一次看到 4 楼 zspzlxn 2009-11-06   一切皆有可能
5 楼 grandboy 2010-01-14   我昨天晚上试一下这个工具,如果能支持数据库就好了,要不然一条一条命令来加Field,还是挺麻烦的。希望它下一版本能加上这个功能。 6 楼 Arden 2010-01-14   关键是开发的时候修改东西要不要重启Web服务器!! 7 楼 liujunsong 2010-01-14   批评两句.雕虫小极而已.

也许这么做开始的时候能省点力气.可是等到一年以后你来维护这些代码的时候,是不是还能象现在这么高兴呢?想想这个问题吧,如果答案是否定的,那么就趁早放弃吧. 8 楼 herowzz 2010-01-15   liujunsong 写道批评两句.雕虫小极而已.

也许这么做开始的时候能省点力气.可是等到一年以后你来维护这些代码的时候,是不是还能象现在这么高兴呢?想想这个问题吧,如果答案是否定的,那么就趁早放弃吧.

那请赐教一下,跟您自己手写的代码相比,哪里不好维护了? 9 楼 grandboy 2010-01-15   liujunsong 写道批评两句.雕虫小极而已.

也许这么做开始的时候能省点力气.可是等到一年以后你来维护这些代码的时候,是不是还能象现在这么高兴呢?想想这个问题吧,如果答案是否定的,那么就趁早放弃吧.

这个东西现在还不成熟,只是1.0 release. 我不知道ror的开发过程是什么样的,请有这方面经验的讲一下。我是想用它先做一个雏形,把一些基础代码都生成,在此基础上进行修改,增加功能。可能我用得不好,好像不太适合我这种需求。 10 楼 魔力猫咪 2010-01-15   感觉是Grails把Groovy的外壳剥了就是roo了。 11 楼 herowzz 2010-01-15   在雏形的基础上进行修改,增加完全没有问题
用AspectJ插件可以很简单的将aj文件push in到类文件里
还可以直接对aj文件进行修改 12 楼 herowzz 2010-01-15   魔力猫咪 写道感觉是Grails把Groovy的外壳剥了就是roo了。
恩,确实,第一次使用roo的时候就有种grails的感觉,不过roo基于spring之上,使用java原生语言,与动态语言相比的性能优势,必然会获得很多怀旧玩家的支持。。。 13 楼 魔力猫咪 2010-01-15   “怀旧玩家的支持”?又不是老游戏换个引擎出XX版。
14 楼 herowzz 2010-01-15   其实东西还是spring的那套东西,引擎就是aspect
不过使用这个引擎之后,立马就变为了充血模型,开发方式都改变了
可见spring的这招很妙,可能有人会觉得小儿科,但是为什么都是人家发明了新东西,新思想,而我们却在这里除了会说“雕虫小技”,还会什么? 15 楼 herowzz 2010-01-15   Ben Alex 写道
ROO框架使用Aspect来描述像@Configurable这样的注解。在Roo框架中使用AOP背后的理由是什么?

至于为什么我们使用aspect作为Java代码生成的基础,背后有许多动机,我在我的系列博文的第三部分谈到了这一内容。但是实质上我提供了一些不同的备选技术原型,包括JSR 269、构建时生成源代码、IDE插件、开发时产生字节码、运行时产生字节码以及高级反射方法如扩展Spring Framework AOP、DSL等等。

我优先要考虑的事情是确保:
就算用户停止使用这一工具,他们的项目照样能够进行;这直接就排除了采取任何运行时动作的可能性。
在运行时,用户的项目必须不以牺牲性能为代价;这直接排除了大多数反射方法的可能性。
开发者要能够使用他们已有的Java知识、技巧和经历。于是工具必须支持常规Java编程体验、支持开发者的普通编程形式、使他们可以使用自己常用的IDE,访问熟悉的工具如debugger和代码助手。该工具必须能使开发者运用已知、已理解及所期望的东西。这样就排除了任何特定的字节码方法以及大多数运行时方法。
该工具必须能自动工作,并且不需要明确调用,也不需要系统集成或建造特定IDE。它必须绝对支持透明的、即时的round-tripping。这就排除了JSR 269以及构创建于系统等方法,比如类似于XDoclet的工具。
其次要考虑:
用户的项目必须工作于Java 5及更高版本上。这就排除了JSR 269,将其排除的原因还有很多(比如原型时期脆弱的IDE支持)。
该工具应该容易让最终用户进行扩展。当然,由于我们使用了AspectJ技术,这是件非常容易的事。它还不鼓励使用任何特定IDE的技术,这可能比Roo附加组件需要更复杂的开发和部署过程。
该工具对增加附加组件的支持应该是长期的。使用AspectJ ITD提供的分离概念可以很容易的实现这一点。
该工具应该非常轻量级:下载、学习、运转都应非常迅速。这对一个拥有最少依赖的基于shell的方法非常有利。Roo 下载文件小于3M!
实质上AspectJ ITDs增加了一个自动维护的元数据模型,它是我们能找到的唯一解决方案,可以满足所有需求。当然,如果你愿意牺牲一些表达方面的要求的话,自然会有其他方法解决这一问题。


16 楼 tanbamboo 2010-01-15   通过Aspect-J来做这些,我觉得相当不错的,起码是预编织的,比动态的性能肯定要好。
另外通过annotation方式做scaffold的话,也比rails的方式方便,起码后期改动要方便。
不知道如果要override CRUD中某个方法,是不是很方便。
有空尝试一下。

Grails小小的试用了一下,个人感觉不好,与rails的差距比较大,尤其是命令脚步执行那个慢啊。 17 楼 Kymair 2010-01-19   liujunsong 写道批评两句.雕虫小极而已.

也许这么做开始的时候能省点力气.可是等到一年以后你来维护这些代码的时候,是不是还能象现在这么高兴呢?想想这个问题吧,如果答案是否定的,那么就趁早放弃吧.

其实这是一种程序员常有的误区

问题是,你如果比较好的掌握了它,你根本就不需要再去看生成后的代码,除非是库本身有BUG。这种可能性不排除,也许你会说,到时候调试查错困难,但是相对于它给你减少的代码和工作量从而提高的效率,从而减少的潜在BUG,比因它自己可能的BUG给你带来的阻碍,应该更多吧?
软件开发就是这种方向,复杂性越来越高,只能抽象等级也越来越高。 18 楼 herowzz 2010-01-19   Kymair 写道liujunsong 写道批评两句.雕虫小极而已.

也许这么做开始的时候能省点力气.可是等到一年以后你来维护这些代码的时候,是不是还能象现在这么高兴呢?想想这个问题吧,如果答案是否定的,那么就趁早放弃吧.

其实这是一种程序员常有的误区

问题是,你如果比较好的掌握了它,你根本就不需要再去看生成后的代码,除非是库本身有BUG。这种可能性不排除,也许你会说,到时候调试查错困难,但是相对于它给你减少的代码和工作量从而提高的效率,从而减少的潜在BUG,比因它自己可能的BUG给你带来的阻碍,应该更多吧?
软件开发就是这种方向,复杂性越来越高,只能抽象等级也越来越高。

调试查错也不可能困难,生成的代码都在你手上,你想调试就调试,想改就改
除非一种可能,就是那位仁兄根本就看不懂,不会。如果连spring都不懂,不会的话,那讨论的重点就不在roo上了,而是spring适不适合他了... 19 楼 xuyangcn 2010-04-29   刚刚试了一下,发现对于基本的CRUD确实不错,但是我试图整合JQuery的Plugin不成功,没有出错信息。无语啊。 20 楼 binarier 2010-05-12   这里有一点刚接触时都会忽略的,就是加字段不需要一个个地add field,只要修改Entity,在ROO保持打开的情况下,那些aspectJ 的 ITD会实时生成的。 21 楼 Angel_Night 2010-07-06   herowzz 写道其实东西还是spring的那套东西,引擎就是aspect
不过使用这个引擎之后,立马就变为了充血模型,开发方式都改变了
可见spring的这招很妙,可能有人会觉得小儿科,但是为什么都是人家发明了新东西,新思想,而我们却在这里除了会说“雕虫小技”,还会什么?

ajax刚出来的时候很多人认为雕虫小技都不算吧。。
22 楼 fjjiaboming 2011-12-27   很强大.
思想啊.

热点排行