ROO中的ApplicationConversionServiceFactoryBean

问题提出

ROO项目中,第一次用web mvc命令新建一个controller后, ROO会在该controller所在的包中新建一个ApplicationConversionServiceFactoryBean类,这个类是干什么的呢?

线索

打开ApplicationConversionServiceFactoryBean.java文件,可得如下代码:

从这段代码出发,我们一步步搞清楚这个ApplicationConversionServiceFactoryBean

FormattingConversionServiceFactoryBean

ApplicationConversionServiceFactoryBean继承于FormattingConversionServiceFactoryBean,打开FormattingConversionServiceFactoryBean.java文件,可见如下代码:

先从它实现的接口出发,google了一下FactoryBeanFormattingConversionServiceEmbeddedValueResolverAwareInitializingBean这几个关键词:

FactoryBean

FactoryBean接口声明如下所示:

这里可以大致了解其用途:

Spring中有两种类型的Bean,一种是普通Bean,另一种是工厂Bean,即FactoryBean。工厂Bean跟普通Bean不同,其返回的对象不是指定类的一个实例,其返回的是该工厂Bean的getObject方法所返回的对象。
使用场景:1、通过外部对类是否是单例进行控制,该类自己无法感知 2、对类的创建之前进行初始化的操作,在afterPropertiesSet()中完成。

例如如下FactoryBean实现:

注册到容器:

若nextDayDateDisplayer类如下所示:

尽管注册时用的是NextDayDateFactoryBean类,但dateOfNextDay属性的类型是DateTime而不是NextDayDateFactoryBean。这就是说:

容器返回的是FactoryBean所”生产”的对象类型,而非FactoryBean实现本身。

所以,FactoryBean提供了一种高级的配置机制,令复杂的bean配置成为可能。

FormattingConversionService

之前写过一篇博文提到了spring mvc中数据类型转换、验证和格式化的流程,如图所示:

流程

从图中可以看出,Spring3引入了格式化转换器(Formatter SPI) 和格式化服务API(FormattingConversionService)来实现格式化(从Obejct到String),例如Date对象到String对象。该类实现了四个接口,分别是:EmbeddedValueResolverAware, ConversionService, ConverterRegistryFormatterRegistry

Formatter

先说一下Formatter接口,如下所示:

其中Printer和Parser接口分别定义了String print(T object, Locale locale);T parse(String text, Locale locale) throws ParseException;方法。

我们用的时候可以这样用

可以看出,Formatter接口包装了Object和String类型之间的转换,主要应用在把view层用户输入或选择的数据转换成controller层的实体类型。

Converter

Converter接口定义的方法如下所示:

若要实现多种类型之间的转换,可以用GenericConverter接口:

从上述可以看出,于Formatter不同,Converter负责任意类型之间的转换。

ConverterRegistry & FormatterRegistry

这两个接口主要用于用于注册格式化转换器

可以看出,ConverterRegistry 和 FormatterRegistry 声明了格式转换服务的注册接口。什么是“注册”?个人认为可以理解成spring在读取配置时,要将formatter等托管给某个统一的服务接口(facade模式?),这个服务接口由某个类来实现。而ConverterRegistry和FormatterRegistry尽管没有声明服务接口,但是给予了spring给该托管类注入formatter的能力。

ConversionService

参考api文档

A service interface for type conversion. This is the entry point into the convert system. Call convert(Object, Class) to perform a thread-safe type conversion using this system.

FormattingConversionService继承自ConversionService。而ConversionService接口声明了从任意类到任意类之间转换的接口:

故FormattingConversionService代表从String到Object或者从Object的转换,即格式化转换。

综上,再来看看FormattingConversionService。其一般使用方法如下

从该类的使用方法可以看出,ConversionService接口给了它转换类型的能力,ConverterRegistry和FormatterRegistry给了它被管理(注入)的能力。FormattingConversionService将不同的Formatter包装起来,对外提供统一的类型格式化服务接口。整个convert框架如下图所示:

框架

InitializingBean

google到这里

InitializingBean
Spirng的InitializingBean为bean提供了定义初始化方法的方式。InitializingBean是一个接口,它仅仅包含一个方法:afterPropertiesSet()。
Spring在设置完一个bean所有的合作者(依赖)后,会检查bean是否实现了InitializingBean接口,如果实现就调用bean的afterPropertiesSet方法。

可以看出InitializingBean接口的意义在于提供了一种bean装配完毕以后的自定义初始化入口。

EmbeddedValueResolverAware

请看这里

Interface to be implemented by any object that wishes to be notified of a StringValueResolver for the resolution of embedded definition values.
This is an alternative to a full ConfigurableBeanFactory dependency via the ApplicationContextAware/BeanFactoryAware interfaces.

留意到其中的StringValueResolver,google了一下,找到了这里

Simple strategy interface for resolving a String value. Used by ConfigurableBeanFactory.

这是什么意思呢?StringValueResolver是一个接口,resolve可以理解成“解析”,而spring在根据配置装配bean的时候,需要将配置中的String类型转换成其它类型,这个转换可以由实现来StringValueResolver接口的类来完成。

Aware的意思是“意识到的”,因此EmbeddedValueResolverAware接口的意思大概是某个bean实现了该接口,则在其被装配时,如果StringValueResolver被调用,则该resolver会通过void setEmbeddedValueResolver(StringValueResolver resolver)方法传入bean内部,用户可以在这里取得resolver。

综上,FormattingConversionServiceFactoryBean可以这样理解:

  1. 它是一个FactoryBean,在初始化时需要复杂的配置。
  2. 它“生成”一个FormattingConversionService实例,负责数据的格式化。
  3. 可以为它添加不同的formatter,令其支持不同的数据的格式化转换。
  4. afterPropertiesSet()方法在其初始化工作完成后被调用。

FormattingConversionServiceFactoryBean的实现

我们打开org.springframework.format.support.FormattingConversionServiceFactoryBean.java文件,可以观察到:

可以看出,FormattingConversionServiceFactoryBean提供了installFormatters()方法,使子类可以在父类完全生成后添加Formatter或者Converter。

ApplicationConversionServiceFactoryBean使用方法

默认的ApplicationConversionServiceFactoryBean实现中,只有一个空实现的方法:

从上文可以得知,installFormatters方法是FormattingConversionServiceFactoryBean为实现方便地增添formatter功能而暴露给子类的一个空方法,其在FormattingConversionServiceFactoryBean装配完成后被调用。打开ApplicationConversionServiceFactoryBean_Roo_ConversionService.aj文件,可见一系列如下代码:

这些方法都是由ROO自动根据项目中的Entity的设置自动生成的,返回包括从ID到Object,Object到String等一系列Converter。文件的最后有还有两个方法:

由此可以看出ApplicationConversionServiceFactoryBean的调用顺序为:

可以看出,getStringToSensorLayoutConverter()等一系列方法返回从某类到某类的converter,通过registry接口加入到service中,而installFormatters()方法暴露了一个“添加点”。所以,在自定义converter工厂方法后,要在installFormatters中调用registry.addConverter()完成注册。

ROO如何与ApplicationConversionServiceFactoryBean结合?

按ctrl+H,输入ApplicationConversionServiceFactoryBean进行全局文件搜索,在webmvc-config.xml文件中可见如下bean定义:

又见如下定义:

根据注解可以大概了解到这个的作用是令Spring MVC支持@Controller注解和注册了一系列Formatters和Validators用于@**Format和@vaild标签。那么这个究竟是什么意思呢?

这里提到,实际上声明了两个bean:DefaultAnnotationHandlerMappingAnnotationMethodHandlerAdapter

DefaultAnnotationHandlerMapping

这幅图 可以了解到,HandlerMapping负责url到controller的转发,而DefaultAnnotationHandlerMapping则负责

Implementation of the HandlerMapping interface that maps handlers based on portlet modes expressed through the RequestMapping annotation at the type or method level.

可以得知,DefaultAnnotationHandlerMapping实现了url到通过@RequestMapping注解标志的controller的转发功能。

AnnotationMethodHandlerAdapter

先说一下HandlerAdapterHandlerAdapterHandlerMapping的一个代理类,DispatcherServlet通过该代理来访问所有装配好的Handlers。 而AnnotationMethodHandlerAdapter从这里可以了解到:

Implementation of the HandlerAdapter interface that maps handler methods based on HTTP paths, HTTP methods and request parameters expressed through the RequestMapping annotation.

Supports request parameter binding through the RequestParam annotation. Also supports the ModelAttribute annotation for exposing model attribute values to the view, as well as InitBinder for binder initialization methods and SessionAttributes for automatic session management of specific attributes.

AnnotationMethodHandlerAdapter实现了handler转发请求过程中的参数绑定,@RequestParam、@ModelAttribute注解的解析。

综上,可以知道,<mvc:annotation-driven conversion-service="applicationConversionService"/>的意思是,实现了把请求转发到@controller注解标注的controller类,并解析了@RequestParam,@Valid等等标签,最后该标签的conversion-service属性指定了在转发请求中采用名为applicationConversionService的bean作为数据格式化,数据转换service。

总结

最后总结一下ApplicationConversionServiceFactoryBean的作用和调用流程:spring容器根据webmvc-config.xml进行初始化,根据<mvc:annotation-driven conversion-service="applicationConversionService"/>标签初始化了整个URLMapping和HTTP请求参数绑定、验证等工作,然后我们可以通过@RequestMapping来注册转发路径和通过@RequestParam等注解处理HTTP参数。该标签同时指定了ApplicationConversionServiceFactoryBean返回的ApplicationConversionService作为这些请求的数据格式化和数据转化的服务类,该服务类负责请求参数(通常为字符串)到实体的转换,所以我们能在controller的某个method里面直接取得实体类。我们可以通过修改或自定义Convertor来实现不同类型之间的转换,这种转换被注册到ApplicationConversionServiceFactoryBean之中,通常用来自定义实体类在view层的显示方式(转换成怎么样的字符串),也可以实现自定义form参数到实体类的转换方式。

ROO 中的 tiles 和 viewResolver

问题描述

在ROO Shell里新建了一个controller,roo会自动生成一系列的jspx和view.xml文件,这是怎么一回事呢?jspx和xml文件之间的关系是什么?自定义jsp时应遵循什么规则?

线索

随意打开WEB-INF/views/,会看到每一个子文件夹(roo自动建立的)下面都有若干个**.jspx文件和一个view.xml文件。打开后者,可以看到下面类似代码:

可知道这是一个xml文件,留意到其被<tiles-definitions>``</tiles-definitions>标签包围,取关键词“tiles”google一下,定位到这里

Apache Tiles™ is a templating framework built to simplify the development of web application user interfaces.

Tiles allows authors to define page fragments which can be assembled into a complete page at runtime. These fragments, or tiles, can be used as simple includes in order to reduce the duplication of common page elements or embedded within other tiles to develop a series of reusable templates. These templates streamline the development of a consistent look and feel across an entire application.

可得治,Tiles是一个显示层的模板框架,其定义的模板组件(tiles)可以在运行时渲染成一个整体的页面来显示。

再次进行搜寻,定位到IBM的一个教程,尽管年代久远,但是是一篇好文章。

tiles

说到tiles,要先了解三个概念:

tile定义

打开WEB-INF/layouts/layouts.xml文件,可看到类似代码:

代码中定义了两个布局模板(default.jspx),和一些arrtibute(header、menu、footer等)。可以看做是在tiles的作用范围内定义了若干个全局变量。其中name属性定义了变量名,value属性为变量的值。

模板

打开WEB-INF/layouts/default.jspx文件,可以看到:

该模板通过标签在页面的合适范围内插入了该tile组件所代表的内容,即在上述tiles定义文件中的attribute的属性值。

当访问default.jspx时,Tiles框架会根据模板中的tiles标签的定义对页面进行组装渲染,例如可以在项目中找到footer.jspx, header.jspx, menu.jspx这些文件,它们运行时组合成我们看到的roo的默认界面。

但是,注意到模板文件中的<tiles:insertAttribute name="body"/>一句插入了一个name为“body”的定义,但是缺找不到body.jspx,这是为什么呢?

在项目中按ctrl+H进行全局查找,以关键词“layouts.xml”查找到在WEB-INF/spring/webmvc-config.xml文件中有如下两个bean定义:

可以看到注释<!-- Scan views directory for Tiles configurations -->的意思是TilesConfigurer会自动扫描满足/WEB-INF/views/**/views.xml路径下的tiles定义文件,随意打开一个views.xml,可以看到如下定义:

这里出现了“body”属性。分析该标签的属性,可以得知该标签继承于(extends)default组件,并且name为sensors/list。我们访问sensors/list路径时,正是显示了以/WEB-INF/views/sensors/list.jspx为的页面。因此猜测:存在一种机制,实现了从URL到tiles的访问。tiles通过URL来决定页面的渲染方式(组件的装配方式)。留意到上述代码中的<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver" id="tilesViewResolver">一句,发现了“UrlBasedViewResolver”**这条线索,由此继续google。

viewResolver

我们在浏览器里输入一条URL,例如http://localhost:8080/demo, 页面显示出网站主页。其中究竟发生了什么?从url到jsp,spring mvc遵循着怎么样的一种mapping机制?

首先关于spring mvc的基础知识在此可以了解到,其核心架构图如下所示:

spring mvc 核心架构图

留意一下从1到7的整个过程,dispatcher负责控制流调度,controller负责业务流调度,而调度的反馈结果通过ModelAndView交给ViewResolver来解析和渲染。不同的ViewResolver有不同的解析方法。

可以看到,tlies框架实现了UrlBasedViewResolver类,名为TilesView。

这里提到:

A ViewResolver is a strategy and factory object used to map logical view names to actual view resources. The interface for ViewResolver is shown below. The single method resolveViewName(..) maps a logical view name together with the request locale to a View instance.

ViewResolver是spring定义的一个接口,如下所示:

默认的ViewResolver实现是InternalResourceViewResolver,其声明如下:

如上,若访问http://localhost/abc,则路径/WEB-INF/views/abc.jsp文件会被解析、渲染并返回。

综上,可以猜测TilesView实现了resolveViewName方法,根据url匹配<definition>标签中的name属性值,然后对根据模板进行装配,最后用Model进行页面渲染,最后将结果返回到浏览器。

另外,在这里可以找到详细的ViewResolver实现和继承关系的树状图

总结

在默认配置的ROO项目里,访问一个url,到浏览器显示出目标页面,大概遵循下面一个可见的流程:

用户在浏览器输入url->某个controller->tiles根据definition解析某个view->用model渲染view->返回结果给用户

Spring.addDecoration

问题描述

在用做项目时,经常在***|||*.tagx**中看到如下的代码:

Spring.addDecorationSpring.ElementDecoration这两句代码是什么意思呢?Spring对象是何时引入的呢?如何使用?

Spring JavaScript

google了一下关键词,发现了这个网站,标题是:Spring Javascript Decorations-Spring JavaScript Quick Reference注意到其中的Spring JavaScript一词,又去google了一下,发现了这样的一个概念:

Spring Javascript (spring-js) is a lightweight abstraction over common JavaScript toolkits such as Dojo. It aims to provide a common client-side programming model for progressively enhancing a web page with rich widget behavior and Ajax remoting.

Use of the Spring JS API is demonstrated in the the Spring MVC + Web Flow version of the Spring Travel reference application. In addition, the JSF components provided as part of the Spring Faces library build on Spring.js.

Spring JS is designed such that an implementation of its API can be built for any of the popular Javascript toolkits. ** The initial implementation of Spring.js builds on the Dojo toolkit.**

大概的意思是,Spring 提供了一个javascript 的 toolkits 接口供外部库参考,意在为开发者提供一个统一的控件库的接口,其中dojo的控件实现了这个接口。

使用方法

这里介绍了如何引入spring-js

Using Spring Javascript in a page requires including the underlying toolkit as normal, the Spring.js base interface file, and the Spring-(library implementation).js file for the underlying toolkit.

打开roo项目默认生成的spring mvc页面: tags/util/load-scripts.tagx 可发现如下html标签

因为load-scripts.tagxlayouts/default.jspx中被引用:

所以我们在每一个合法的jspx中都能引用到dojo的类库

Spring Javascript Decorations

A central concept in Spring Javascript is the notion of applying decorations to existing DOM nodes.

可知,Spring Javascript Decorations主要是用于给node添加控件样式。下面这个例子中:

留意一下elementDecoration的构造函数的参数形式,再看一下dojox中手动生成一个widget的代码:

可以看出dojox的widget的条用方式和spring js的调用方式可以对应得上,Spring.ElementDecoration中的widgetAttrs对应dijit中的{params}elementId就是dijittargetID(这里忽略了这个参数),widgetType指定了widget的类型。

所以:

的意思是将dijit.form.FilteringSelect的样式应用到id为_${sec_field}_idnode上。

其他

我们还可以在create.tagx``find.tagx``update.tagx中找到这样的html代码:

其中

一句的意思是:

This decorates the “Proceed” button with a special onclick event handler that fires the client side validators and does not allow the form to submit until they pass successfully.

也就是为表单打开了验证功能。

除此之外,可以为node增添Ajax功能:

详情请看这里

ROO中的*.aj文件

问题提出

当我们用ROO命令生成Entity或者Controller的时候,系统会自动生成若干个*.aj文件,这些是什么文件呢?为什么ROO会采取这样的技术?如何使用?

AOP

ROO的文档Application Architecture中有对*.aj文件进行简单的介绍和使用说明:

AspectJ is a powerful and mature aspect oriented programming (AOP) framework that underpins many large-scale systems.

有关AOP的介绍可以看这里,注意核心关注点横切关注点的概念:

举个例子来说,一个信用卡处理系统的核心关注点是借贷/存入处理,而系统级的关注点则是日志、事务完整性、授权、安全及性能问题等,许多关注点——即横切关注点(crosscutting concerns)——会在多个模块中出现。 对于电子商务系统而言,每个需要权限验证的方法都是一个单独的join point。由于权限验证将在每个方法执行前执行,所以对于这一系列join point,只需要定义一个point cut。当系统执行到join point处时,将根据定义去查找对应的point cut,然后执行这个横切关注点需要实现的逻辑,即advice。而point cut和advice,就组合成了一个权限管理aspect。

实例图如下所示:

实例图 t

AspectJ

知道了AOP的概念后,再来认识一下AspectJ。首先,一个AOP的实现框架,需要满足如下几个技术特性:

  1. join point(连接点):一个抽象的概念,即可以让point cut切进来的地方,通常跟AOP框架的实现有关,例如某个类的某个方法。
  2. point cut(切入点):被横切的连接点,用来捕获方法调用等事件。
  3. advice(通知):执行的具体逻辑。
  4. aspect(方面):类似于一个操作的集合体,声明了多个point cut以及该point cut触发后调用的advice。
  5. introduce(引入):又称为mixin。为对象引入附加属性或方法的特性。

AspectJ作为AOP一个经典的实现,它是如何体现上述特性的呢?先来看一段示例代码

注意到代码中的注释部分,可以看到在业务流程中夹杂着重复的验证、事务开启和关闭操作。随着业务的复杂度增加,该类型的“横切”代码将会遍布整个项目,使模块难以修改。 结合前面对AOP的特性的介绍,可以观察到示例代码中有两个joint point,分别是deposit()withdraw()两个方法。两个aspect,分别负责验证和事务管理。利用AspectJ,可以如下处理:

和:

如上面的代码所示,定义了AuthAspectTransactionAspect两个切面。

其中pointcut bankMethods() : execution (* Bank.deposit(…)) || execution (* Bank. withdraw (…));一句声明了point cut,即符合* Bank.deposit(…)) || execution (* Bank. withdraw (…)条件的方法在执行时,触发advice。Object around():一句定义了point cut的类型为around类型。在advice的结束时检查账户的合法性,然后返回proceed()对象,返回核心关注点继续执行。

再看一个例子,来自这里

上述例子中,有:

  1. 一个 inter-type field:private boolean Server.disabled = false;
  2. 两个methods:reportFault()fixServer(Server s)
  3. 一个point cut定义:pointcut services(Server s): target(s) && call(public * *(..));
  4. 两个advice:before(Server s): services(s)after(Server s) throwing (FaultException e): services(s)

下面就每个关键词进行解析:

inter-type field

例子中,private boolean Server.disabled = false;为Server类添加了一个私有的属性disable,并且初始值为false。与之类似,如果你想为Server添加doSomething()方法,可以这样写:

那么编译器就会在编译时为Server添加doSomething()方法,静态织入完成。

reportFault()和fixServer(Server s)

此Aspect中定义了两个方法,与普通Java方法无异。

pointcut

pointcut时刻监测着某些(符合表达式的)的joint point,例如方法,构造函数,异常处理,属性的访问和赋值等等。

如例子中的:

声明了一个名为services的pointcut,它会根据冒号(:)后面的表达式是否为真,来判断该pointcut是否触发。这个pointcut负责监测任意函数值任意函数名的Server类的public方法,其中target(s)的意思是被织入的对象为Server类(在冒号左边声明了)。

advice

advice的调用有一个很抽象的描述:

A piece of advice brings together a pointcut and a body of code to define aspect implementation that runs at join points picked out by the pointcut.

大意是advice是由pointcut和一段代码共同定义的在pointcut被join points触发时执行的逻辑

例子中,before(Server s): services(s)的意思是在join point触发前执行这一段advice,并且Server类作为参数s传入advice之中。after(Server s) throwing (FaultException e): services(s)的意思是在join point触发后执行这一段advice,并且可能抛出FaultException。

这里有一个Pointcuts表达式中函数的列表,其中有:

target(Type or Id)
every join point when the target executing object is an instance of Type or Id’s type

除了before()after()之外,还有around()around()的意思如下所示

around advice traps the execution of the join point; it runs instead of the join point. The original action associated with the join point can be invoked through the special proceed call

也就是说,around类型的advice中的代码将会整体替换掉原逻辑代码,但可以通过调用proceed()方法来执行原来的逻辑代码。因为around advice的特性可以使业务动态地执行或者不执行,所以它适合用在需要判断条件的横切关注点上,例如用户权限验证功能。

ROO中的AspectJ

资料来自Spring Roo in Action。当我们在ROO shell中输入

时,ROO为我们新建了一系列的文件:

  1. Course.java
  2. Course_Roo_Configurable.aj
  3. Course_Roo_ToString.aj
  4. Course_Roo_Jpa_Entity.aj
  5. Course_Roo_JavaBean.aj
  6. Course_Roo_Jpa_ActiveRecord.aj

打开Course.java文件,可以看到代码如下:

注意到Course上方的三个注解。其中@RooJavaBean对应Course_Roo_JavaBean.aj切面文件,@RooToString对应Course_Roo_ToString.aj@RooJpaActiveRecord对应Course_Roo_Configurable.ajCourse_Roo_Jpa_Entity.ajCourse_Roo_Jpa_ActiveRecord.aj。我们可以不通过Roo Shell,手动地添加entity,然后按需要添加上述注解,Roo会自动地根据注解内容为我们添加或删除对应的aj文件。

总体架构如下所示:

架构

可以看出:

  1. Course_Roo_ToString.aj定义了entity的toString()方法。
  2. Course_Roo_Configurable.aj为entity添加了@Configurable标签(请看这里)。
  3. Course_Roo_Jpa_Entity.aj为entity提供了JPA支持。
  4. Course_Roo_JavaBean.aj定义了一系列的getter和setter方法。
  5. Course_Roo_Jpa_ActiveRecord.aj包装了一系列的CRUD方法。

ROO通过将与Entity相关的不同职能的方法封装在不同的aj文件中,在编码时帮助程序员实时管理aj文件,最后通过AspectJ在编译时织入,组成目标class文件。这样子有利于程序员吧注意力集中在核心业务上,不会消耗大多精力在getter、setter和CRUD上。当你想自定义aj文件中某个方法时,不需要修改aj文件,只需要在entity.java中以相同的方法签名进行添加即可,Roo会自动帮你在aj文件中删除掉对应的自动生成的方法。

dojo库resize控件使用方法

问题描述

项目中需要在网页上实现可以拖曳改变div大小的功能。由于项目用了roo,roo自带了dojo,所以打算用dojo自带的方法来实现它。

解决方案

首先在google上搜索 “dojo resize” ,搜索结果中发现这个。有具体例子,有api文档,是它了。

首先


做一点准备工作:

还有下面一个css文件:

值得一提的是,在roo自带的dojo中没有找到上述css文件,于是只好到官网下载了一个,放置在项目路径中。这个css文件的作用是:

This provides simple styling for your ResizeHandle, and a default handle icon.

然后


有两种使用方式,分别是:

Programmatic (编程式)

注意,new dojox.layout.ResizeHandle返回了一个handler控件(Widget/Dijit):

动态创建一个 Dijit 的时候,Dijit 动态构造语句的返回值就为该 Dijit 实体。将这个 Dijit 动态构造语句的返回值赋值给一个变量,则可以通过这个变量实现对新创建 Dijit 实体的操控。

需要插入到受控的node中,用到了placeAt()方法。

关于该方法,这里有一段中文描述:

这是一个常用的方法,dojo 控件都实现了这个方法。运用这个方法可以自由地放置控件的位置。该方法有两个参数:reference, 和 position。可以接受的 reference 参数类型有:String, DomNode 和 _Widget。其中 String 为引用 Dom 节点 (DomNode),或者 dojo 控件 (_Widget) 的 id。而被座位参数传入的 dojo 控件,必须是实现了 addChild 方法的类型的。可接受的 position 参数类型有 Int, 和 String。传入的 String 参数必须是”first”,”last”,”before”,”after”中的一个。

关于dojo的Widget,这里这里有一些描述:

widget的英文含义是装饰物或者小器具,有些技术文档将其译为小部件,在这里直接使用其英文名,以免引起不必要的混淆。Dojo提供widget框架的原因来自两方面:一是为了更好的用户体验;二是为了帮助开发人员快速开发Web应用。

Dojo 提供了两种方式给系统开发者去使用其所提供的 Dijit(Dijit 是 Dojo Widget 的简称)。第一种方法是通过直接在页面中静态的写入带有 Dijit 属性的标签去实现该 Dijit 的使用;第二种方法是通过 Javascript 语句,动态的在当前使用页面中生成 Dijit。 第一种方法被称为静态创建 Dijit,而第二种方法则被称为动态创建 Dijit。

动态创建 Dijit 又必须分为两个步骤。第一个步骤 :动态创建一个“替代层”,并将该层插入到当前页面 DOM 结构中 Dijit 应处的位置。第二个步骤 :调用该 Dijit 对应的动态构造语句,例如 new dijit.form.Button(params, srcNodeRef) 去创建该 Dijit。其中“params”是 Dijit 构造时相关的属性参数,“srcNodeRef”是上一步骤中创建的“替代层”。

Declarative (声明式)

关于声明式,也就是静态模式创建Dijit,这里也有描述:

首先回顾一下,静态创建 Dijit 的一个完整的过程。当页面加载完成以后,在引入了 Dojo 解析模块的基础上,Dojo 会自动将页面中所有的 Dijit 标签属性解析为标准的 HTML(动态创建的 Dijit,会在创建的过程中自动完成转换)。

其中“引入了 Dojo 解析模块”一句指的是:

静态创建 Dijit 的时候,页面加载完成以后会调用相应的 Dojo 解析模块将整个页面的 Dijit 标签属性解析为 HTML。

注意 ,要添加如下两行css代码:

最后

这里一句话总结一下为了解决这个问题学到的关于动态创建和静态创建的知识:

抛开动态创建和静态创建的表象从本质上说,创建一个 Dijit 对于动态和静态都需要以下相同的几项要素。

  • Dijit 将插入 DOM 结构中的位置。静态创建 Dijit 是通过直接写入到页面中,表明其所在 DOM 结构中的位置;而动态创建 Dijit 则需要通过一个“替代层”来实现 Dijit 插入到 DOM 树中的合适位置。
  • 表明要创建的 Dijit 类型。静态创建 Dijit 是通过使用 dojotype 标签属性来表明;而动态创建 Dijit 则是通过调用该 dijit 相应的动态构造语句来表明。
  • 在创建一个 Dijit 时,需设置其的相关属性。静态创建 Dijit 是通过标签属性来定义的;而动态创建 Dijit 则是通过在该 Dijit 相应构造语句中直接设定属性实现的。

强烈推荐【此处】文章集,学习dojo的好资料!另外【这里】几乎有每一个dojo模块的测试用例!

svn几点备忘

branch 和 merge

  1. branch到trunk see this:

    也許過了一段時間,原本的 /calc/trunk 開發主線可能已經有其他團隊成員陸續修正了一些 Bugs,但這時你的分支 /calc/branches/my-calc-branch 就可以直接套用 開發主線 ( /calc/trunk ) 的更新,除了避免重複的工作外 (重複除錯),也可以避免版本的衝突發生,因為兩個人改同一個已知的 Bug 可能會因為用不同方法除錯或命名的方式不一致而發生衝突。經常將 開發主線 ( /calc/trunk ) 的變更透過 svn merge 合併至 分支線路 ( /calc/branches/my-calc-branch ) 是一個非常好的習慣,這樣才不會讓你因為脫離 開發主線(trunk) 過久而導致將 分支線路 ( /calc/branches/my-calc-branch ) 合併回 開發主線 ( /calc/trunk ) 時發生許多版本衝突。

  2. trunk到branch see this:

    從 分支線路 ( /calc/branches/my-calc-branch ) 合併回 開發主線 ( /calc/trunk ) 通常選第 2 個,而特別選擇 [Reintegrate a branch] 這個選項是很重要的,因為這有以下好處:

    • 讓 Subversion 能知道 開發主線 ( /calc/trunk ) 是從哪個分支、哪些版本合併進來的
    • 有效節省 Subversion Repository (SVN儲存庫) 的空間,因為不用重複儲存分支的所有變更資訊
    • 可以產生 Revision graph 得知專案開發的分支狀況
  3. 注意一下 see this:

    • branch主要用于新功能的开发
    • 合并发生在本地working copy,只要你不提交就不会影响到repository
    • 合并前一定要先update、commit,保证不会out of day,并将本地的修改保存到repository
    • branch和trunk并行开发的过程中,要经常同步,将trunk的修改合并到branch,合并时选择”Merge a range of revision”
    • branch最后合并回trunk时,merge type选择”Reintegrate a branch”

注意:在TortoiseSVN下, 1.的情况在branch处按右键, 2.的情况下在trunk处按右键. 其他客户端在merge时要看清楚from和to的路径.

注意:若在Retergate a branch时出现: must be ancestrally related to 字样,则说明分支和主干没有祖先关系(分支不是该主干分出来的)。解决办法是在看一下branch的log,看看它是从那个版本分出来的,然后checkout出那个版本,接着进行正常的merge。merge成功后若trunk不是最新的,那么还要将其与trunk进行合并。

忽略文件

在 Eclipse 中点击菜单 window –> Preferences –> Team –> Ignored Resources

在Eclipse的导航视图中,选中尚未加入版本控制的文件或目录,右键 –> Team –> 添加至SVN:ignore

右击鼠标-> 选择 TortoiseSVN -> Setting (设置) -> General (常规设置) -> 在右侧 “Golbal ignore pattern”(全局忽略样式)内填入*.db *.bak -> 确定