在Seam中,使用<s:convertEntity/>或者<s:convertEnum/>会方便很多,Seam会自动将用户选中的Entity实例放到对应选择标签的value中。
这几天在JBoss Seam集成GWT,试了很多时间,总结出一些问题:
JBoss Seam版本:2.1.0.SP1
Google Web Toolkit版本:1.5.3
- gwt-servlet.jar版本问题:Seam中自带的gwt-servlet.jar版本可能和你使用的GWT版本不一致,如果你编译GWT程序的版本与Seam中gwt-servlet.jar版本不一致,可能会出现一些奇怪的问题,最好的办法是将Seam中的gwt-servlet.jar换成GWT中带的jar。
- Seam Resource Servlet的配置和GWT程序中Service Entry Point的设置:Seam中的默认url pattern是/seam/resource/*,所以Seam会把对/seam/resource/gwt/*的请求发送给org.jboss.seam.remoting.gwt.GWT14Service处理,GWTService才会根据GWT客户端发送的请求类和方法来进行调用。需要注意的是,根据跟踪Seam的GWTService发现,客户端发送的请求类似于“5|0|6|http://127.0.0.1:8080/SeamGWT/|5BA8A5B3E35F40698BB0BF65F390BCF2|com.tiandinet.gwt.hello.client.HelloService|sayHello|java.lang.String|your name|1|2|3|4|1|5|6|”,而Seam的GWTService.getResource会根据com.tiandinet.gwt.hello.client.HelloService名称查找Seam组件,此组件即为GWT中的远程服务接口的Seam实现,所以在设置此实现类的@Name属性时,需要将其设置为GWT中远程服务接口的类名。
所以,对于Service Entry Point的设置,只要URL能匹配到/seam/resource/gwt/即可,而Seam Reference示例中的String endpointURL = GWT.getModuleBaseURL() + “seam/resource/gwt”;可能不一定正确,因为根据GWT编译后页面路径在Seam应用中所处的位置不同,GWT.getModuleBaseURL()返回的路径可能就不能匹配到/seam/resource/gwt。 - Seam Resource Servlet映射
/web=org.jboss.seam.ui.resource.WebResource
/captcha=org.jboss.seam.captcha.CaptchaImage
/remoting=org.jboss.seam.remoting.Remoting
/gwt=org.jboss.seam.remoting.gwt.GWT14Service
/graphicImage=org.jboss.seam.ui.graphicImage.GraphicImageResource
在Java中,有两种初始化块:静态初始化块和非静态初始化块。
静态初始化块:使用static定义,当类装载到系统时执行一次。若在静态初始化块中想初始化变量,那仅能初始化类变量,即static修饰的数据成员。
非静态初始化块:在每个对象生成时都会被执行一次,可以初始化类的实例变量。
非静态初始化块会在构造函数执行时,且在构造函数主体代码执行之前被运行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | package com.tiandinet.studyjava; public class TestInitiateBlock { { System.out.println("In non-static initialization block!"); }; static { System.out.println("In static initialization block!"); }; public TestInitiateBlock() { System.out.println("In Constructor1!"); } public void show() { System.out.println("In show()!"); } /** * @param args */ public static void main(String[] args) { TestInitiateBlock ti = new TestInitiateBlock(); ti.show(); } } |
运行结果:
In static initialization block!
In non-static initialization block!
In Constructor1!
In show()!
在原项目中,对文件上传的处理并不是使用Spring的MultipartResolver,而是使用自定义的MultiPartFilter和HttpServletRequestWrapper结合来进行处理。
MultiPartFilter是一个定义在web.xml中的<filter>,原理是通过判断HttpServletRequest中的contentType是否包含”multipart/form-data”,若代码中包含此字符品,则表明是一个File Upload请求,就使用JakartaMultiPartRequest来对HttpServletRequest对其进行包装。
其代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class MultiPartFilter extends GenericFilterBean { public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; String content_type = request.getContentType(); if (content_type != null && content_type.indexOf("multipart/form-data") != -1) { JakartaMultiPartRequest jakartaMultiPartRequest = new JakartaMultiPartRequest(request, getFilterConfig().getInitParameter("saveDir"), Integer.valueOf( getFilterConfig().getInitParameter("maxUploadSize")).intValue()); request = jakartaMultiPartRequest; } filterChain.doFilter(request, servletResponse); } } |
JakartaMultiPartRequest类继承自HttpServletRequestWrapper,在其构造方法中,我们对Request进行分析,并且保存用户上传的文件,将文件名等信息放到Request的attribute中,从而在后续的代码中可以对上传的文件进行处理。
但在后来客户进出的对产品图片处理的新需求中,因为JakartaMultiPartRequest的问题,没有办法满足我们的要求,所以考虑到使用Spring的MultipartResolver来进行上传处理,但之前的代码不能再进行改动,否则所有处理文件上传的Controller必须更改,代价很大。但如果在Spring中进行配置MultipartResolver的配置后,发现我们旧的文件上传代码都失效了,无法上传文件。 (阅读全文……)
折腾了两天,终于在JBoss Seam中搞定了JSF Converter。
在这个程序中,产品(Product)和分类(Category)是多对多的关系,关系维护方为产品,在创建产品时,允许选择多个分类,因为Product.categories是一个List属性,同时产品选择<select />标签也是由从Action中查询出来的所有Category的List,所以在页面上,需要在页面渲染、用户提交后的category进行转换。
显示时,我们以Category.id为<option />的value值,而Category.title为label。
Entity如下(省略setter和getter):
Category.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | @Entity @Table(name = "category") public class Category extends BaseEntity { @Id @GeneratedValue @Column(name = "id", unique = true, nullable = false, length = 10) private Integer id; @Column(name = "title") @NotNull @Length(min = 4, max = 64, message = "{invalid.length}") private String title; @ManyToMany(mappedBy = "categories") private List<product> products = new ArrayList</product><product>(); } </product> |
环境:Spring 2.5, ActiveMQ 5.1
项目的后台要求在更改密码后发送邮件通知用户,为了避免发送邮件时程序对用户操作的阻塞,之前中文版中使用了线程来发送邮件,而在英文版中,我决定使用JMS来异步发送邮件,让用户更改密码的操作和发送邮件的操作更进一步解耦,也在实际环境中试试JMS。
我们的环境是Spring 2.5, Tomcat 5.5,使用ActiveMQ来实现JMS传送和接收。
首先,我们在Spring中加入ActiveMQ Broker的配置:
1 2 3 4 5 6 7 | <bean id="broker" class="org.apache.activemq.xbean.BrokerFactoryBean"> <property name="config" value="classpath:activemq.xml" /> <property name="start" value="true" /> </bean> |
我们在此处配置了BrokerFactoryBean,此Bean实现在Spring中配置嵌入式Broker,并且支持XBean方式的配置。Broker的配置文件由config属性指定,此处定义配置文件位于classpath中的activemq.xml。