随笔-4  评论-14  文章-0  trackbacks-0
  2008年4月2日
 

一、新建项目
先在eclipse中新建一个maven项目,如下图:

填写id、version等,点finish。项目就建好了。下面是项目建好之后的样子:

二、配置环境

1、添加依赖包

选中项目,右键——Maven2——Add Dependency,如下图:

打开添加依赖包界面后添加包:struts2.0.11、spring2.0.6、hibernate-annotations3.2、hibernate-entitymanager3.2、hibernate3.2、servlet2.4、javax-persistence、mysql-connector-java5.0.4、spring-aop2.0.6、aspectjweaver1.5.3、struts2-spring-plugin2.0.11、junit、spring-mock2.0.6。

2、配置web.xml文件

在WEB-INF下新建文件web.xml
输入如下内容:

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_9" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Struts 2</display-name>
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.FilterDispatcher
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>

用过struts1.x的人会发现这里没有了servlet,而是一个filter,是的,这是struts2和struts1的一个不同之处。

在src/main/resource下新建文件struts.xml,这里是和struts1.x不一样的地方。
输入如下内容:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.devMode" value="true" />
<package name="Struts2_MESSAGE" extends="struts-default"
namespace="/message">
</package>
</struts>

简单解释一下,constant的作用是自定义一些struts2的属性,在struts2的包里带了一个struts.properties文件,里边设定了很多默认的struts2的运行参数,但是这些有时候是需要改变一下的,因此就提供了constant来覆盖struts.properties中属性。而package的作用可以简单的看做把应用分模块。package有个属性叫namespace,上面我们配置为/message,假设这个package中还有一个名叫hello的action,那么我们访问这个action的url就是:http://域名/message.hello.action。

本文的示例程序是一个简单的留言添删改查,所以取名叫message。

3、配置spring

在WEB-INF下新建文件applicationContext.xml,输入如下内容:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
</beans>

在web.xml文件中加入如下内容:

<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>

 用过spring的人应该知道这个listener的作用是启动spring容器。

4、配置jpa和spring的jpa支持

在src/main/resource下新建文件夹META-INF,然后在这个文件夹中新建文件persistence.xml,输入如下内容:

<?xml version="1.0" encoding="utf-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="app-unit"
transaction-type="RESOURCE_LOCAL">
</persistence-unit>
</persistence>

可能有人会发现,怎么jpa的配置文件只有这么点了,这其实是因为我们用的spring的jpa支持,数据库配置信息都放到spring的配置文件里了,后面就会看到。

然后打开前面新建的文件applicationContext.xml,在beans节点内添加如下内容:

<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory"
ref="entityManagerFactory" />
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://localhost/message?createDatabaseIfNotExist=true" />
<property name="username" value="root" />
<property name="password" value="123" />
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="MYSQL" />
<property name="generateDdl" value="false" />
<property name="showSql" value="true" />
</bean>
</property>
</bean>

看,数据库配置信息出现了

这里的username和password需要根据你的实际情况修改。

5、集成struts2和spring

打开struts.xml文件,添加如下内容:

<constant name="struts.objectFactory" value="spring" />
<constant name="struts.objectFactory.spring.autoWire" value="type" />

 到这里,配置基本完成。

三、新建域模型

我们先创建一个域模型Message,包括name、title、ip、content、inputTime等字段,代码如下:

package com.struts.sample.domain;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Message {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
private String name;
private String title;
private String ip;
private String content;
private Date inputTime;
getter and setter...
}

@Entity是jpa的注解,表示这个类与一个表对应,如果没有知名表明,那么就和message这个表对应,系统启动是jpa会自动创建这个表。
@Id也是jpa注解,标识这个字段是主键。
@GeneratedValue(strategy = GenerationType.TABLE)是指定一个主键生成策略。

四、Service层的实现

接口就不帖出来了,直接把实现类写出来。

package com.struts.sample.service;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import com.struts.sample.domain.Message;
public class MessageServiceImpl implements MessageService {
@PersistenceContext EntityManager em;
public void setEm(EntityManager em) {
this.em = em;
}
public List<Message> find(final String queryString, final int rowStartIdx, final int rowCount,
final Object... values){
Query query = em.createQuery(queryString);
if (values != null)
for (int i = 0; i < values.length; i++)
query.setParameter(i + 1, values[i]);
if(rowStartIdx>0){
query.setFirstResult(rowCount);
}
if(rowCount>0){
query.setMaxResults(rowCount);
}
return query.getResultList();
}
public void save(Message msg){
this.em.persist(msg);
}
public void del(Long id){
this.em.remove(this.get(id));
}
public Message get(Long id){
return this.em.find(Message.class, id);
}
public void update(Message msg){
this.em.merge(msg);
}
public Message getBy(String field, Object value) {
String queryString = "select msg from Message msg where msg."+field+"=:value";
Query query = this.em.createQuery(queryString).setParameter("value", value);
List<Message> msgs = query.getResultList();
if(msgs.size()>1){
throw new java.lang.IllegalStateException(
"worning  --more than one object find!!");
}else if(msgs.size()==1){
return msgs.get(0);
}else{
return null;
}
}
}

注意这一句@PersistenceContext EntityManager em;容器启动的时候会去创建messageService,创建的时候会发现这个注解:@PersistenceContext,然后容器会把EntityManager注入到这里。
接下来我们需要让spring知道我们有这样一个bean需要它来加载。打开配置文件applicationContext.xml,加上如下内容:

<bean id="messageService" class="com.struts.sample.service.MessageServiceImpl" />

现在我们来写测试用例,spring为我们测试jpa提供了一个基类:org.springframework.test.jpa.AbstractJpaTests。
首先,我们在src/test/resource目录下新建一个文件applicationContext.xml,内容与src/main/resource/applicationContext.xml的内容一样。
接下来我们写一个测试用例AbstractJpaTests ,代码如下:

package com.struts.sample.service;
import java.util.Date;
import org.springframework.test.jpa.AbstractJpaTests;
import com.struts.sample.domain.Message;
public class MessageServiceImplTest extends AbstractJpaTests {
@Override
protected String[] getConfigLocations() {
return new String[]{"classpath:applicationContext.xml"};
}
private MessageService service;
public void setService(MessageService service) {
this.service = service;
}
public void testSave(){
Message msg = new Message();
msg.setContent("xxxxxxxxxxxxxxxxx");
msg.setName("tianyi");
msg.setInputTime(new Date());
msg.setTitle("www.easyjf.com");
msg.setIp("123.123.132.123");
service.save(msg);
assertNotNull(msg.getId());
Message msg2 = service.getBy("name", "tianyi");
assertNotNull(msg);
}
}

运行这个测试程序,当你看到绿色的条条,说明你测试成功了。

五、Action层的实现

Service层已经实现并测试通过,接下来我们该写Action了。Struts2支持POJO的action,不需要继承或者实现任何类或者接口,可以很方便的测试。这个例子中我们就采用POJO式的action。代码如下:

package com.struts.sample.action;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.struts.sample.domain.Message;
import com.struts.sample.service.MessageService;
public class MessageAction {
private Message message;
private Long id;
private MessageService service;
private List<Message> msgs = new ArrayList<Message>();
private Integer pages = 0;
private Integer pageSize = 0;
public List<Message> getMsgs() {
return msgs;
}
public void setMsgs(List<Message> msgs) {
this.msgs = msgs;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getPages() {
return pages;
}
public void setPages(Integer pages) {
this.pages = pages;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public Message getMessage() {
return message;
}
public void setMessage(Message message) {
this.message = message;
}
public String List(){
this.msgs = this.service.find("select msg from Message msg", pages, pageSize, null);
return "success";
}
public String Save(){
message.setInputTime(new Date());
if(message.getId()!=null){
this.service.update(message);
}else{
this.service.save(message);
}
return this.List();
}
public String Del(){
if(id==null){
return "error";
}else{
this.service.del(id);
}
return this.List();
}
public String Edit(){
if(id==null){
return "error";
}else{
this.message = this.service.get(id);
}
return "success";
}
public void setService(MessageService service) {
this.service = service;
}
}

代码很简单,但是却实现了添删改查的功能,struts2确实比struts1好用多了。Struts2中可以将一个POJO作为action,就像上面的action。在用户访问到这个action的时候struts2会根据前台提交进来的数据自动将必要的字段进行注入。
以save为例,当我们访问到这个方法的时候,前台表单域会有message.title、message.name、message.content等变量,struts2会根据这些前台变量的名字将他们注入到MessageAction中的message中。而如果我们是执行的修改操作,那么前台还会传递一个id到服务端,struts2又会自动将这个值注入到id中。
需要注意的是,如果你希望某个字段被注入,那么你应该提供一个setter,如果你同时希望这个字段能在页面中被访问,那么你还应该为它提供一个getter。

接下来我们来配置struts2,前面我们只是将struts2运行所需的环境配置起来了,现在我们需要配置新添加的Action。
打开struts.xml文件,在package节点中添加如下内容:

<action name="message_*" class="com.struts.sample.action.MessageAction" method="{1}">
<result>List.jsp</result>
</action>

这个配置文件大家可能有点不明白,怎么还有“*”、“{1}”这种东西。现在我简单解释一下。struts2的action配置中支持通配符,message_*大家应该能明白,“*”就代表任意的东西,访问的时候可以用message_aaa、message_bbb等,这些请求都会被交给MessageAction来处理,但是会不会有正确的结果还要看后面的method="{1}"这个东西。method属性使得一个action中的一个方法就可以处理一个请求,这意味着我们不需要写大量的action,而可以把一些相关的处理放到一个action中。现在我们来说说这个“{1}”,实际上“{1}”是代表前面name中的通配符的位置,struts会将第一个“*”代表的那一串字符串截取下来,作为method的值。在这个例子中,假设我们用message_aaa.action来访问的话,那么struts会调用MessageAction的aaa方法。struts2的这种通配符可以支持多个,如果有两个,那么"{1}"就代表第一个"*"所匹配的字符串,"{2}"就代表第二个"*"匹配的字符串,以此类推。

后台的东西到这里就差不多了,剩下的就是页面了。

六、页面

是一个很简单的示例,只有一个个页面。在webapp目录下新建目录message,在message目录下新建文件List.jsp,输入如下内容:

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>message</title>
</head>
<body>
<s:iterator value="msgs">
<table style="border:1px solid #cccccc; margin-top:10px;" width="700">
<tr>
<td>标题:<s:property value="title" /></td>
<td>发表时间:<s:property value="inputTime" /></td>
</tr>
<tr>
<td colspan="2" height="100" style="padding:10px;">留言内容:
<div style="border:1px dotted #dddddd">
<p>  <s:property value="content" /></p>
</div></td>
</tr>
<tr>
<td>作者:<s:property value="name" /></td>
<td>操作:<a href='<s:url action="message_Edit">
<s:param name="id" value="id" /></s:url>'>Edit</a>
<a href='<s:url action="message_Del">
<s:param name="id" value="id" /></s:url>'>
Delete
</a></td>
</tr>
</table>
<hl/>
</s:iterator>
<s:form action="message_Save" >
<s:hidden name="message.id" />
<s:textfield name="message.title" label="标题" />
<s:textarea name="message.content" label="内容" />
<s:textfield name="message.name" label="作者" />
<s:submit label="提交" />
</s:form>
</body>
</html>

到这里基本完工了,我们运行一下,看看结果。
打开浏览器输入http://localhost:8080/message/message_List.action,居然是错误界面,错误信息如下:

这是为什么呢,原来我们没有为service配置事务。打开applicationContext.xml,添加如下内容:

<aop:config>
<aop:advisor
pointcut="execution(* com.struts.sample.service.*.*(..))"
advice-ref="txAdvice" />
</aop:config>
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="insert*" />
<tx:method name="update*" />
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>

运行试试,应该没有问题了吧!运行界面如下:

 

 们添加一条记录看看,我随便填了一条,点submit:

这里,我们的第一个示例就宣告完成了,如果有什么错误,还请大家指正。

源码下载:struts-sample.rar

本文为原创,同时在easyjf官方博客发表:http://www.easyjf.com/blog/html/20080402/1474562.html。欢迎转载,转载请保留出处。

posted @ 2008-04-02 10:44 天 一 阅读(3129) | 评论 (6)编辑 收藏
  2008年1月11日
     摘要: EasyJWeb的Ajax综合示例应用于1月10号上线,这是团队2008年第一次源代码发布。该示例包括ExtJS集成、远程脚本调、Ajax表单提交等多种实例,基于EJS构架开发,持久层使用JPA及泛型DAO、业务逻辑层使用Spring2进行Bean的管理,表示层使用EasyJWeb的MVC及ExtJS等实现,示例演示了一般应用开发中的几种Ajax应用方式,欢迎大家下载交流。  阅读全文
posted @ 2008-01-11 10:55 天 一 阅读(1411) | 评论 (7)编辑 收藏
  2008年1月7日
     摘要: 这是easyjf去年做的一个基于Ajax的网上聊天室示例应用,该系统涉及到多线程、支持多聊天室、支持聊天室管理、用户管理、聊天日志记录等多个功能。由于这个系统是很早以前做的,很多地方存在不足,这周末心情好,把这个应用翻了出来,把系统进行了重新调整,使用EJS(EasyJWeb+JPA+Spring)构架,使用EasyJWeb的自动代码生成及ajax支持等功能开发。在此推荐给对于JPA、Spring2、EasyJWeb及Ajax技术感兴趣的朋友们,欢迎一起交流。
  阅读全文
posted @ 2008-01-07 17:23 天 一 阅读(1307) | 评论 (1)编辑 收藏
  2007年12月28日
EasyJWeb中提供了一些注解,可以给开发带来很多方便。这些注解包括有数据验证用的@FormPO、@Validator等;IOC注入用的@Inject、@InjectDisable等;action配置用的@Action等。
今天我们来说说action配置时要用的注解@Action,使用这些注解可以大量减少配置文件的编写。
首先,我们来看看不使用这些注解的时候是怎么做的,以一个hello world程序为例。
首先是action:
public class HelloAction extends AbstractCmdAction
{
    
private IHelloService servie;

    
public IHelloService setService(IHelloService service){
        
this.service = service;
    }


    
public Page doHello(WebForm form, Module module){
        System.out.println(service.sayHello());
        
return null;
    }

}

service:
public interface IHelloService{
    String sayHello();
}

service实现:
public class HelloServiceImpl implements IHelloService
{
    
public String sayHello(){
        
return "hello";
    }

}

如果要使这个程序正常运行,我们还需要配置service和action,easyjweb中默认集成spring,service是在spring的配置文件中配置:
    <bean id="helloService"    class="com.hello.service.impl.HelloServiceImpl">
    
</bean>

现在来配置action,在easyjweb的配置文件中这样配置:
        <module name="hello" path="/hello" form="" scope="request"
            action
="com.hello.mvc.HelloAction" defaultPage="list"
            inject
="byType">

        
</module>
这里说说这个inject="byType",这样配置之后在HelloAction中的属性会自动根据类型来注入。inject还有byName等属性,意思就是根据名字来注入。如果这里不加这个inject,那么配置文件就应该是这样写:
        <module name="hello" path="/hello" form="" scope="request"
            action
="com.hello.mvc.HelloAction" defaultPage="list"
            inject
="byType">
     
<property name="service" ref="helloService" />
        
</module>
property的name属性对应HelloAction中的service,名字要保持一致。ref对应上面在spring中配置的helloService的id。
到这里,这个程序就可以运行了。
一个简单的hello程序就用了这么多配置文件,这让人难以忍受。现在我们就来说说怎么使用EasyJWeb的注解来简化配置。
我们修改一下HelloAction的代码,给这个类加上一个@Action注解:
@Action(path="hello")
public class HelloAction extends AbstractCmdAction
{
    
private IHelloService servie;

    
public IHelloService setService(IHelloService service){
        
this.service = service;
    }


    
public Page doHello(WebForm form, Module module){
        System.out.println(service.sayHello());
        
return null;
    }

}

这样一来,我们就不需要easyjweb中配置的module了,当使用hello.ejf访问的时候框架会自动把请求交给HelloAction处理。
现在来简单介绍一下@Action这个注解。
@Action的各个属性说明如下:
name用来指Action的名称,也是在容器中的Bean名称,我们一般不使用这个属性。
path用来指定模块的path值,也即所映射的url,如果不设值该值将会按照缺省的方式处理。
alias用来指定这个模块的path别名,也就是可以使用其它的一个或多个名称来访问这个模块。
inject用来指定Action中所有业务组件注入方式,默认值为按类型注入。如果为byName则表示按名称注入,auto表示自动按名称或类别注入,none则表示不注入。
disInject用来标识不自动注入的属性。
autoInject用来标识允许自动注入的属性。
autoToken表示该模块是否需要开取自动防重复提交功能;
validate表示该模块是否开取自动验证功能,默认情况不开启自动验证;
view表示该模板的视图存放子目录。
scope用来指定这个Action在容器中的创建方式及生命周期,默认值为request,表示每次请求创建一次该对象,若为session则表示个用户会话创建一个对象,若为singleton表示整个容器中只创建一次该实例。
messageResource表示多国语言属性文件的存放子目录。
通常我们只用到path、view、inject这几个属性,由于inject的默认值为"AutoJnjectByType",是最常用的,因此通常也不需要显式指定这个属性值。而disInject是用来标识不允许注入的属性的,当我们的action出现了一些不需要注入的属性时,如logger,我们就需要使用这个属性来指定哪些是不需要注入的。autoToken则是标识是否开启防重复提交功能的。

posted @ 2007-12-28 13:36 天 一 阅读(1185) | 评论 (0)编辑 收藏
仅列出标题