专题五:Spring + Hibernate 编程实例
本实例中,使用Spring作为WEB层和业务逻辑层的容器,Hibernate作为持久层,另外使用JAX-WS2.1做WebService部件,为瘦客户端提供服务。其结构如下图所示:

从这个总图可以看出,这个框架需要完成下列任务:
l         编写实体类和实体主键类
l         编写实体类的持久化操作类及其接口
l         编写业务逻辑类及其接口
l         编写Web界面操作类
l         编写WebService类(给瘦客户端)
l         编写相应的配置文件
本实例使用一个简单的地址本管理应用描述相关的内容。本实例设计工作在Tomcat和glassfish服务器上。建议使用glassfish服务器以获得后续的发展,毕竟glassfish是SUN发布的全功能的应用服务器,而非只有Web容器。
1          环境准备
1.1    安装系统软件
l         安装JDK 6.0 和JAVA EE 5;
l         安装netbeans 6.0做开发工具,使用eclipse也可以;
l         安装Tomcat 6.0做应用服务器;
l         安装GlassFish 2.0做应用服务器
l         安装数据库,这里使用 MS SQL Server Express 2005
1.2    准备组件
l         Spring 2.06
l         Hibernate 3.2
l         JAX-WS 2.1
l         MS SQL Server JDBC 1.2
l         其他需要的JAR包
1.3    创建应用
l         使用netbeans创建WebApplication,命名为 AddressBook;
l         选择Tomcat做服务器;
l         JAVA平台选择 Java EE 5;
l         创建数据库及数据表。
CREATE DATABASE addressbook;
CREATE TABLE db_address
{
email     char(20) not null primary key,
name      char(20) not null,
password char(20) not null,
workphone char(20) not null,
homephone char(20) not null,
mobile    char(20) not null,
};
1.4    添加基础配置
1.4.1        WEB-INF/web.xml
web.xml文件放置到WEB-INF目录;
l         配置Spring ApplicationContext文件;
l         配置Spring ApplicationContext加载器;
l         配置log4j的文件;
l         配置Spring过虑器防止汉字乱码。
    
        
            | 
             <?xml version="1.0" encoding="UTF-8"?> 
            <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> 
                <description>地址本管理应用范例</description> 
            <display-name>AddressBook</display-name> 
                <context-param> 
                    <description>Spring main config</description> 
                    <param-name>contextConfigLocation</param-name> 
                    <param-value>/WEB-INF/applicationContext.xml</param-value> 
            </context-param> 
             <context-param> 
                  <param-name>log4jConfigLocation</param-name> 
                  <param-value>/WEB-INF/log4j.properties</param-value> 
             </context-param> 
                <listener> 
                    <description>Spring applicationContext loader</description> 
                    <listener-class> 
            org.springframework.web.context.ContextLoaderListener 
            </listener-class> 
            </listener> 
              
                <filter> 
                    <filter-name>encodingFilter</filter-name> 
                    <filter-class> 
            org.springframework.web.filter.CharacterEncodingFilter 
            </filter-class> 
                    <init-param> 
                        <param-name>encoding</param-name> 
                        <param-value>GB18030</param-value> 
                    </init-param> 
                    <init-param> 
                        <param-name>forceEncoding</param-name> 
                        <param-value>true</param-value> 
                    </init-param> 
                </filter>  
                 
                <filter-mapping> 
                    <filter-name>encodingFilter</filter-name> 
                    <servlet-name>AddressBookWeb</servlet-name> 
                    <url-pattern>*.do</url-pattern> 
            </filter-mapping> 
            <session-config> 
                    <session-timeout> 
                        30 
                    </session-timeout> 
                </session-config> 
                <welcome-file-list> 
                    <welcome-file>index.jsp</welcome-file> 
                </welcome-file-list> 
            </web-app> 
             | 
        
    
1.4.2        WEB-INF/classes/applicationContext.xml
applicationContext.xml放置到Source目录,编译时被IDE自动放置到WEB-INF/classes目录。(放置到此处JUnit测试才能通过,否则报加载配置文件错)
l         配置数据库连接池(c3p0DataSource);
l         配置Hibernate Session管理器(sessionFactory);
l         配置数据库事务管理器(transactionManager);
l         配置Spring配置式事务代理(txProxyTemplate)。
    
        
            | 
             <?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" 
                   xsi:schemaLocation= 
                   "http://www.springframework.org/schema/beans 
                   http://www.springframework.org/schema/beans/spring-beans.xsd 
                   http://www.springframework.org/schema/aop 
            http://www.springframework.org/schema/aop/spring-aop.xsd"> 
            <bean id="c3p0DataSource"  
            class="com.mchange.v2.c3p0.ComboPooledDataSource"  
            destroy-method="close"> 
                    <property name="driverClass"> 
                        <value>com.microsoft.sqlserver.jdbc.SQLServerDriver</value> 
                    </property> 
                    <property name="jdbcUrl"> 
                        <value> 
            jdbc:sqlserver://localhost:1433;databaseName=AddressBook 
            </value> 
                    </property> 
                    <property name="user"> 
                        <value>sa</value> 
                    </property> 
                    <property name="password"> 
                        <value>welcome</value> 
                    </property> 
                    <property name="minPoolSize"> 
                        <value>10</value> 
                    </property> 
                    <property name="acquireIncrement"> 
                        <value>10</value> 
                    </property> 
                    <property name="maxPoolSize"> 
                        <value>200</value> 
                    </property> 
                </bean> 
            <bean id="sessionFactory"  
            class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
                    <property name="dataSource"> 
                        <ref bean="c3p0DataSource" /> 
                    </property> 
                    <property name="mappingDirectoryLocations"> 
                        <list> 
                            <!-- Hibernate Configure file location --> 
                            <value>classpath*:model</value> 
                        </list> 
                    </property> 
                    <property name="hibernateProperties"> 
                        <props> 
                            <prop key="hibernate.dialect"> 
            org.hibernate.dialect.SQLServerDialect 
            </prop> 
                            <prop key="hibernate.show_sql">false</prop> 
                            <prop key="hibernate.format_sql">true</prop>             
                            <prop key="hibernate.default_schema"></prop>     
                            <prop key="hibernate.cache.provider_class"> 
            org.hibernate.cache.EhCacheProvider 
            </prop>              
                        </props> 
                    </property> 
                </bean> 
            <bean id="transactionManager"  
            class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 
                    <property name="sessionFactory"> 
                        <ref local="sessionFactory"/> 
                    </property> 
                </bean> 
            <bean id="txProxyTemplate"  
            class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true"> 
                    <property name="transactionManager" ref="transactionManager"/> 
                    <property name="transactionAttributes"> 
                        <props>             
                            <prop key="select*">PROPAGATION_REQUIRED,readOnly</prop> 
                            <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop> 
                            <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop> 
                            <prop key="save*">PROPAGATION_REQUIRED</prop> 
                            <prop key="update*">PROPAGATION_REQUIRED</prop> 
                            <prop key="remove*">PROPAGATION_REQUIRED</prop> 
                            <prop key="del*">PROPAGATION_REQUIRED</prop> 
                        </props> 
                    </property> 
                </bean> 
            </beans> 
             | 
        
    
注意:
(1)以上系统级基础Bean均是单例的;
(2)txProxyTemplate 是一个抽象的定义,全部业务逻辑Bean的定义将继承其定义,从而获得Spring的配置式事务能力;
(3)此处指定Hibernate的配置文件要放置到WEB-INF/classes/model目录下。
1.4.3        WEB-INF/classes/log4j.properties
    
        
            | 
             # TraceLevel: OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL 
            # Appender: ConsoleAppender、FileAppender、DailyRollingAppender、RollingFileAppender、WriterAppender 
            # Layout: HTMLLayout、PatternLayout、SimpleLayout、TTCCLayout 
            # ConversionPattern: %m(message)、%p(TraceLevel)、%r(时间片-毫秒数)、%c(类名)、%t(线程号)、%n("n)、%d{DateFmt}(日期)、%l(全信息) 
            # log4j.appender.A2.File=dglog.txt 
            # 
            # log4j.appender.A2=org.apache.log4j.DailyRollingFileAppender  
            # log4j.appender.A2.file=dglog  
            # log4j.appender.A2.DatePattern='.'yyyy-MM-dd  
            # log4j.appender.A2.layout=org.apache.log4j.PatternLayout  
            # log4j.appender.A2.layout.ConversionPattern= %5r %-5p %c{2} - %m%n  
            # 
            # log4j.appender.R=org.apache.log4j.RollingFileAppender  
            # log4j.appender.R.File= dglog.log  
            # log4j.appender.R.MaxFileSize=100KB  
            # log4j.appender.R.MaxBackupIndex=30 
            # log4j.appender.R.layout=org.apache.log4j.PatternLayout  
            # log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n  
            # 
            log4j.rootLogger=ERROR,A1,A2 
            log4j.appender.A1=org.apache.log4j.ConsoleAppender 
            log4j.appender.A1.layout=org.apache.log4j.PatternLayout 
            log4j.appender.A1.layout.ConversionPattern=[%d{yyyy-MM-dd HH":mm":ss}] [%c %M] %-5p %c(line":%L) %x-%m%n 
            # 
            log4j.appender.A2=org.apache.log4j.RollingFileAppender 
            log4j.appender.A2.File=addressbook.log 
            log4j.appender.A2.MaxFileSize=1024KB 
            log4j.appender.A2.MaxBackupIndex=7 
            log4j.appender.A2.layout=org.apache.log4j.PatternLayout 
            log4j.appender.A2.layout.ConversionPattern=[%-5p] [%d{yyyy-MM-dd HH":mm":ss}] [%c %M] %c(line":%L) %x-%m%n 
             | 
        
    
1.5    测试
发布应用到服务器,检查服务器的日志信息,确认数据库连接是否成功,是否缺少JAR包,日志输出是否正常。
2          创建实体类及主键类
由于派生或替换实体类及主键类的可能性不大,因此对实体类及主键类不必定义接口。每个业务数据对象均需要设计实体类及主键类。此外,业务逻辑层与WEB层交换的model对象,如果无法使用数据库实体,则也应为其定义一个实体类(例如进行多表数据编辑。――多表数据输出则不必定义实体,只需要将多个实体对象放置到模型中即可)。
应注意的是,尽管实体的主键可能是单键,可以使用简单数据类型,但考虑到编码的一致性,建议统一使用主键类作为各实体的主键。
实体类与主键类通常放置到model包中,可以根据实体的数量和性质划分更细的包。
实体类及主键类是普通的JavaBean,只包含数据及其构造方法、Setter和Getter方法,且实现Serializable接口。实体类及主键类必须定义无参构造方法、equals方法、hashCode方法,主键类应定义一个带参构造方法。实体类及实体主键类必须对全部属性进行初始化,确保各属性的值合法,这可以避免很多意外的错误。
2.1    model.key.Address.AddressId
    
        
            | 
             /* 
             * To change this template, choose Tools | Templates 
             * and open the template in the editor. 
             */ 
            package model.key.Address; 
            import java.io.Serializable; 
            /** 
             * 
             * @author  
             */ 
            public class AddressId implements Serializable  
            { 
            private String id = ""; 
                public AddressId() 
                { 
                } 
                public AddressId(String id) 
                { 
                    this.id = id.trim(); 
                } 
                @Override 
                public boolean equals(Object obj) 
                { 
                    if (obj == null) 
                    { 
                        return false; 
                    } 
                    if (getClass() != obj.getClass()) 
                    { 
                        return false; 
                    } 
                    final AddressId other = (AddressId) obj; 
                    if (this.id == null || !this.id.equals(other.id)) 
                    { 
                        return false; 
                    } 
                    return true; 
                } 
                @Override 
                public int hashCode() 
                { 
                    int hash = 5; 
                    hash = 97 * hash + (this.id != null ? this.id.hashCode() : 0); 
                    return hash; 
                } 
                public String getId() 
                { 
                    return id.trim(); 
                } 
                public void setId(String id) 
                { 
                    this.id = id.trim(); 
                } 
            } 
             | 
        
    
2.2    model.entity.Address.Address
    
        
            | 
             /* 
             * To change this template, choose Tools | Templates 
             * and open the template in the editor. 
             */ 
            package model.entity.Address; 
            import java.io.Serializable; 
            import model.key.Address.AddressId; 
            /** 
             * 
             * @author  
             */ 
            public class Address implements Serializable  
            { 
                private AddressId id = new AddressId(""); 
                private String name = ""; 
                private String password = ""; 
                private String workphone = ""; 
                private String homephone = ""; 
            private String mobile = ""; 
                public Address() 
                { 
                } 
                @Override 
                public boolean equals(Object obj) 
                { 
                    if (obj == null) 
                    { 
                        return false; 
                    } 
                    if (getClass() != obj.getClass()) 
                    { 
                        return false; 
                    } 
                    final Address other = (Address) obj; 
                    if (this.id != other.id && (this.id == null || !this.id.equals(other.id))) 
                    { 
                        return false; 
                    } 
                    return true; 
                } 
                @Override 
                public int hashCode() 
                { 
                    int hash = 3; 
                    hash = 97 * hash + (this.id != null ? this.id.hashCode() : 0); 
                    return hash; 
                } 
                public AddressId getid() 
                { 
                    return id; 
                } 
                public void setid(AddressId id) 
                { 
                    this.id = id; 
                } 
                public String getHomephone() 
                { 
                    return homephone.trim(); 
                } 
                public void setHomephone(String homephone) 
                { 
                    this.homephone = homephone.trim(); 
                } 
                public String getMobile() 
                { 
                    return mobile.trim(); 
                } 
                public void setMobile(String mobile) 
                { 
                    this.mobile = mobile.trim(); 
                } 
                public String getName() 
                { 
                    return name.trim(); 
                } 
                public void setName(String name) 
                { 
                    this.name = name.trim(); 
                } 
                public String getPassword() 
                { 
                    return password.trim(); 
                } 
                public void setPassword(String password) 
                { 
                    this.password = password.trim(); 
                } 
                public String getWorkphone() 
                { 
                    return workphone.trim(); 
                } 
                public void setWorkphone(String workphone) 
                { 
                    this.workphone = workphone.trim(); 
                } 
            } 
             | 
        
    
注意:各属性值的trim()可以避免变长字段很多意外情况的发生。
2.3    model/Address.hbm.xml
实体类的Hibernate映射文件放置到model目录下(见1.4.2节的配置)。每个实体类一个文件。文件命令规则为实体类名.hbm.xml。
    
        
            | 
             <?xml version="1.0"?> 
            <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
            "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 
            <hibernate-mapping> 
                <class name="model.entity.Address.Address"  table="db_address"  
                       dynamic-insert="true" dynamic-update="true" lazy="true"> 
                    <composite-id name="id" class="model.key.Address.AddressId"> 
                        <key-property name="id" type="string"> 
                            <column name="email" length="20" /> 
                        </key-property> 
                    </composite-id> 
                    <property name="name" type="string"> 
                        <column name="name" length="20" not-null="true" /> 
                    </property> 
                    <property name="workphone" type="string"> 
                        <column name="workphone" length="20" not-null="true" /> 
                    </property> 
                    <property name="homephone" type="string"> 
                        <column name="homephone" length="20" not-null="true" /> 
                    </property> 
                    <property name="mobile" type="string"> 
                        <column name="mobile" length="20" not-null="true" /> 
                    </property> 
                    <property name="password" type="string"> 
                        <column name="password" length="20" not-null="true" /> 
                    </property>         
                </class> 
            </hibernate-mapping> 
             | 
        
    
2.4    WEB-INF/classes/applicationContext.xml
实体类通过其属性存储持久化数据,因此应是多例的。
    
        
            | 
                 <bean id="Address" class="model.entity.Address.Address" scope="prototype"> 
                    <property name="name" value= "name"/> 
                </bean> 
                <bean id="AddressId" class="model.key.Address.AddressId" scope="prototype"> 
                    <property name="id" value= "email"/> 
            </bean> 
            注意:增加属性值的目的是为JUnit测试。 
             | 
        
    
2.5    单元测试
Spring的spring-mock.jar提供了脱离Web容器的环境下测试Spring框架的功能,因此需将spring-mock.jar包加入测试包,并创建Spring测试类继承AbstractTransactionalSpringContextTests类,覆盖getConfigLocations()方法和runTest()方法。AbstractTransactionalSpringContextTests类提供了加载Spring环境的能力和数据库事务管理能力,它会在测试结束时自动回滚测试中的数据库事务,确保不会对数据形成影响。如果测试中需要提交数据库事务,可以调用setComplete()或setDefaultRollback(boolean defaultRollback)。
2.5.1        Case.SpringTest
    
        
            | 
             /* 
             * To change this template, choose Tools | Templates 
             * and open the template in the editor. 
             */ 
            package Case; 
            import model.entity.Address.Address; 
            import model.key.Address.AddressId; 
            import org.springframework.test.AbstractTransactionalSpringContextTests; 
            import static org.junit.Assert.*; 
            /** 
             * 
             * @author  
             */ 
            public class SpringTest extends AbstractTransactionalSpringContextTests 
            {  
                @Override 
                protected String[] getConfigLocations() 
                { 
                    return new String [] {"classpath*:applicationContext.xml"}; 
                } 
               public void TestEntity() 
                { 
                    System.out.println("TestEntity() start .."); 
                    System.out.println("AddressId .."); 
                    AddressId id = (AddressId) applicationContext.getBean("AddressId"); 
                    assertNotNull(id); 
                    assertEquals(id.getId(), "email"); 
                    System.out.println("Address .."); 
                    Address address = (Address) applicationContext.getBean("Address"); 
                    assertNotNull(address); 
                    assertEquals(address.getName(), "name"); 
                     
                    System.out.println("TestEntiry() end.");         
                }  
            } 
             | 
        
    
2.5.2        Test.JUnitTest
    
        
            | 
             /* 
             * To change this template, choose Tools | Templates 
             * and open the template in the editor. 
             */ 
            package Test; 
            import Case.SpringTest; 
            import org.junit.Test; 
            import static org.junit.Assert.*; 
            /** 
             * 
             * @author  
             */ 
            public class JUnitTest  
            { 
                @Test 
                public void main() throws Throwable  
                { 
                    System.out.println("Start Test ..."); 
                    SpringTest t = new SpringTest(); 
                    t.setName("TestEntiry"); 
                    t.runBare(); 
                } 
            } 
             | 
        
    
3          编写实体类的持久化操作类及其接口
为简化实体类的持久化操作类编写,我们创建持久化基础类完成大部分的持久化任务。各实体类的持久化类均继承持久化基础类。本例中的持久化任务是通过Hibernate完成的,因此持久化基础类以Spring提供的事务和HibernateDaoSupport作为基础的组件。网络上有比较成熟的持久化基础类,可以下载来使用。
为支持各个实体类,持久化基础类使用了JDK5.0增加的泛性类。
持久化各类均放置在dao包中。
3.1    dao.GenericDao
    
        
            | 
             /* 
             * To change this template, choose Tools | Templates 
             * and open the template in the editor. 
             */ 
            package dao; 
            import java.io.Serializable; 
            import java.util.List; 
            /** 
             * 
             * @author  
             */ 
            public interface GenericDao<T extends Serializable, PK extends Serializable> 
            { 
                public T Select(PK id); 
                public List<T> SelectAll(); 
                public void Insert(T entity); 
                public void Update(T entity); 
                public void Delete(PK id); 
            } 
             | 
        
    
3.2    dao.GenericHibernateDao
    
        
            | 
             /* 
             * To change this template, choose Tools | Templates 
             * and open the template in the editor. 
             */ 
            package dao; 
            import java.io.Serializable; 
            import java.lang.reflect.ParameterizedType; 
            import java.lang.reflect.Type; 
            import java.util.List; 
            import org.springframework.orm.hibernate3.support.HibernateDaoSupport; 
            /** 
             * 
             * @author  
             */ 
            public class GenericHibernateDao<T extends Serializable, PK extends Serializable> 
                    extends HibernateDaoSupport implements GenericDao<T,PK> 
            { 
                private Class<T> entityClass; 
                public GenericHibernateDao() 
                { 
                   this.entityClass = null; 
                    Class c = getClass(); 
                    Type t = c.getGenericSuperclass(); 
                    if(t instanceof ParameterizedType) 
                    { 
                        Type[] p = ((ParameterizedType) t).getActualTypeArguments(); 
                        this.entityClass = (Class) p[0]; 
                    }         
                } 
                public void Delete(PK id) 
                { 
                    getHibernateTemplate().delete(this.Select(id)); 
                } 
                public void Insert(T entity) 
                { 
                    getHibernateTemplate().save(entity); 
                } 
                public void Update(T entity) 
                { 
                    getHibernateTemplate().update(entity); 
                } 
                public T Select(Serializable id) 
                { 
                    return (T) getHibernateTemplate().load(entityClass, id); 
                } 
                public List<T> SelectAll() 
                { 
                    List<T> list; 
                    list = getHibernateTemplate().loadAll(entityClass); 
                    return list; 
                } 
            } 
             | 
        
    
3.3    dao.AddressDao
实体持久化类接口继承持久化基础类接口,将泛性转换为确定的实体类型。
    
        
            | 
             /* 
             * To change this template, choose Tools | Templates 
             * and open the template in the editor. 
             */ 
            package dao; 
            import model.entity.Address.Address; 
            import model.key.Address.AddressId; 
            /** 
             * 
             * @author  
             */ 
            public interface AddressDao extends GenericDao<Address, AddressId> 
            { 
            } 
             | 
        
    
3.4    dao.AddressDaoImp
实体持久化类继承持久化基础类,实现实体持久化类接口。
    
        
            | 
             /* 
             * To change this template, choose Tools | Templates 
             * and open the template in the editor. 
             */ 
            package dao; 
            import model.entity.Address.Address; 
            import model.key.Address.AddressId; 
            /** 
             * 
             * @author  
             */ 
            public class AddressDaoImp extends GenericHibernateDao<Address, AddressId> implements AddressDao  
            { 
            } 
             | 
        
    
3.5    WEB-INF/classes/applicationContext.xml
实体持久化类需在Spring的上下文配置中定义,以便业务逻辑类可以从Spring环境中获取并使用。由于实体持久化类没有存储任何状态数据,因此应使用单例模式。
    
        
            | 
                 <bean id="AddressDao" class="dao.AddressDaoImp"> 
                    <property name="sessionFactory"> 
                        <ref bean="sessionFactory"/> 
                    </property> 
                </bean> 
             | 
        
    
3.6    单元测试
持久化类的测试在实体类测试的基础上进行,需在Spring测试类中增加持久化测试的相关内容。注意如果Dao类还没有纳入事务管理而hibernate配置实体是延迟加载的,则执行select时会出现could not initialize proxy - the owning Session was closed错误,此时可以临时关闭实体类的延时加载选项或将Dao类纳入事务管理(见2.5.1)。
3.6.1        Case.SpringTest
    
        
            | 
                 public void TestDao() 
                { 
                    System.out.println("TestDao() start .."); 
                    System.out.println("测试AddressDao配置 .."); 
                    AddressDao dao = (AddressDao) applicationContext.getBean("AddressDao"); 
                    assertNotNull(dao); 
                    System.out.println("测试AddressDao.insert() .."); 
                    AddressId id = (AddressId) applicationContext.getBean("AddressId"); 
                    id.setId("_email"); 
                    Address address = (Address) applicationContext.getBean("Address"); 
                    address.setid(id); 
                    address.setName("_name"); 
                    address.setHomephone("_homephone"); 
                    address.setWorkphone("_workphone"); 
                    address.setMobile("_mobile"); 
                    address.setPassword("_password"); 
                    dao.Insert(address); 
                    System.out.println("测试AddressDao.select() .."); 
                    address = dao.Select(id); 
                    assertNotNull(address); 
                    assertEquals(address.getid().getId(), "_email"); 
                    assertEquals(address.getName(), "_name"); 
                    assertEquals(address.getHomephone(), "_homephone"); 
                    assertEquals(address.getWorkphone(), "_workphone"); 
                    assertEquals(address.getMobile(), "_mobile"); 
                    assertEquals(address.getPassword(), "_password"); 
                    System.out.println("测试AddressDao.update() .."); 
                    address.setName("_newname"); 
                    dao.Update(address); 
                    Address addressnew = dao.Select(id); 
                    assertNotNull(addressnew); 
                    assertEquals(address.getName(), addressnew.getName()); 
                    System.out.println("测试AddressDao.delete() .."); 
                    dao.Delete(id); 
                    System.out.println("TestDao() end.");         
                } 
             | 
        
    
注意:测试时Spring会自动执行回滚操作取消对数据库的更改。但如果需要在数据库存储测试的数据,结束测试前可以调用setComplete()提交数据库事务。
3.6.2        Test.JUnitTest
    
        
            | 
                 @Test 
                public void main() throws Throwable  
                { 
                    System.out.println("Start Test ..."); 
                    SpringTest t = new SpringTest(); 
                    t.setName("TestDao"); 
                    t.runBare(); 
                } 
             | 
        
    
4          编写业务逻辑类及其接口
业务逻辑类从界面获取数据,进行业务处理后存储到数据库;或接受界面的请求,从数据库获取数据提交给界面操作。业务逻辑和实体类、Dao类均存在交互。由于实体类是多例的,Dao类是单例的,为减少系统的开销,尽量将业务逻辑类设计为单例的,因此业务逻辑类可以使用Dao类、其他单例类作为属性,但不能使用实体类作为属性。实体类只能在方法中或参数中、Session环境中使用。
为避免重复设计通用化的业务功能部分,建议设计业务逻辑基础类作为其他业务逻辑类的基类,并定义相应的接口。基础类是抽象类,因此不必在Spring中进行配置(因为不会创建其实例)。
业务逻辑类放置到business包及其子包。
4.1    business.GenericService
    
        
            | 
             /* 
             * To change this template, choose Tools | Templates 
             * and open the template in the editor. 
             */ 
            package business; 
            /** 
             * 
             * @author  
             */ 
            public interface GenericService  
            { 
            } 
             | 
        
    
4.2    business.GenericServiceImp
    
        
            | 
             /* 
             * To change this template, choose Tools | Templates 
             * and open the template in the editor. 
             */ 
            package business; 
            /** 
             * 
             * @author  
             */ 
            public class GenericServiceImp implements GenericService 
            { 
            } 
             | 
        
    
4.3    business.Address.AddressService
    
        
            | 
             /* 
             * To change this template, choose Tools | Templates 
             * and open the template in the editor. 
             */ 
            package business.Address; 
            import business.GenericService; 
            import java.util.List; 
            import model.entity.Address.Address; 
            import model.key.Address.AddressId; 
            /** 
             * 
             * @author  
             */ 
            public interface AddressService extends GenericService 
            { 
                public void save(Address address); 
                public Address select(AddressId id); 
                public List<Address> selectAll(); 
                public void update(Address address); 
                public void delete(AddressId id); 
            } 
             | 
        
    
4.4    business.Address.AddressServiceImp
    
        
            | 
             /* 
             * To change this template, choose Tools | Templates 
             * and open the template in the editor. 
             */ 
            package business.Address; 
            import business.GenericServiceImp; 
            import dao.AddressDao; 
            import java.util.List; 
            import model.entity.Address.Address; 
            import model.key.Address.AddressId; 
            import org.hibernate.Hibernate; 
            /** 
             * 
             * @author  
             */ 
            public class AddressServiceImp extends GenericServiceImp implements AddressService 
            { 
                private AddressDao dao; 
                public void setDao(AddressDao dao) 
                { 
                    this.dao = dao; 
                } 
                public void save(Address address) 
                { 
                    dao.Insert(address); 
                } 
                public Address select(AddressId id) 
                { 
                    Address address = dao.Select(id); 
                    Hibernate.initialize(address); 
                    return address; 
                } 
                public List<Address> selectAll() 
                { 
                    List<Address> list = dao.SelectAll(); 
                    Hibernate.initialize(list); 
                    return list; 
                } 
                public void update(Address address) 
                { 
                    dao.Update(address); 
                } 
                public void delete(AddressId id) 
                { 
                    dao.Delete(id); 
                } 
            } 
             | 
        
    
注意:由于实体类配置为延迟加载的(见2.3),即从数据库获取时Hibernate为提高性能,仅返回了实体的代理对象,当应用访问代理对象的数据时,才真正从数据库检索数据。为避免延迟检索数据时出现could not initialize proxy - the owning Session was closed,本例中使用Hibernate. initialize()代理业务逻辑操作,模拟业务逻辑中的使用代理对象数据,以便该实例数据能够被Hibernate从数据库中加载。
4.5    WEB-INF/classes/applicationContext.xml
业务逻辑类我们将实施Spring的配置化事务,因此其配置应扩展txProxyTemplate的配置,同时应注意业务逻辑类的方法命名应匹配txProxyTemplate定义的方法名,否则Spring无法使用AOP机制插入事务处理。
    
        
            | 
                 <bean id="AddressServiceTarget" class="business.Address.AddressServiceImp"> 
                    <property name="dao"> 
                        <ref bean="AddressDao"/> 
                    </property> 
                </bean> 
                <bean id="AddressService" parent="txProxyTemplate"> 
                    <property name="target"> 
                        <ref bean="AddressServiceTarget"/> 
                    </property> 
                </bean>  
             | 
        
    
4.6    单元测试
4.6.1        Case.SpringTest
    
        
            | 
             public void TestService() 
                { 
                    System.out.println("TestService() start .."); 
                    System.out.println("测试AddressService配置 .."); 
                    AddressService svr = (AddressService) applicationContext.getBean("AddressService"); 
                    assertNotNull(svr); 
                    System.out.println("测试AddressService.save() .."); 
                    AddressId id = (AddressId) applicationContext.getBean("AddressId"); 
                    id.setId("_email"); 
                    Address address = (Address) applicationContext.getBean("Address"); 
                    address.setid(id); 
                    address.setName("_name"); 
                    address.setHomephone("_homephone"); 
                    address.setWorkphone("_workphone"); 
                    address.setMobile("_mobile"); 
                    address.setPassword("_password"); 
                    svr.save(address); 
                    System.out.println("测试AddressService.select() .."); 
                    address = svr.select(id); 
                    assertNotNull(address); 
                    assertEquals(address.getid().getId(), "_email"); 
                    assertEquals(address.getName(), "_name"); 
                    assertEquals(address.getHomephone(), "_homephone"); 
                    assertEquals(address.getWorkphone(), "_workphone"); 
                    assertEquals(address.getMobile(), "_mobile"); 
                    assertEquals(address.getPassword(), "_password"); 
                    System.out.println("测试AddressService.update() .."); 
                    address.setName("_newname"); 
                    svr.update(address); 
                    Address addressnew = svr.select(id); 
                    assertNotNull(addressnew); 
                    assertEquals(address.getName(), addressnew.getName()); 
                    // this.setComplete(); 
                    System.out.println("测试AddressService.delete() .."); 
                    svr.delete(id); 
                    System.out.println("TestService() end.");         
                } 
             | 
        
    
4.6.2        Test.JUnitTest
    
        
            | 
             @Test 
                public void main() throws Throwable  
                { 
                    System.out.println("Start Test ..."); 
                    SpringTest t = new SpringTest(); 
                    t.setName("TestService"); 
                    t.runBare(); 
                } 
             | 
        
    
5          编写Web界面操作类
本例中使用Spring作为Web界面部分,功能也很简单,仅提供Address实体的CRUD操作功能。Spring提供了一套创建Web MVC的类,本例中M使用实体类的实体,V使用JSP文件,输入型视图的C继承Spring的SimpleFormController,输出型视图的C实现Spring的Controller。
Spring的WEB MVC的关系图如下:
5.1    配置Spring Web MVC
Spring Web MVC需在Web.xml中配置Spring前端控制器Servlet及地址映射,同时增加Web MVC自己的上下文配置文件。
5.1.1        WEB-INF/web.xml
配置web.xml装载Spring WEB的控制器及其上下文配置,定义地址匹配,以及字符集编码转换。
    
        
            | 
                 <servlet> 
                    <servlet-name>AddressBookWeb</servlet-name> 
                    <servlet-class> 
            org.springframework.web.servlet.DispatcherServlet 
            </servlet-class> 
                    <init-param> 
                        <param-name>contextConfigLocation</param-name> 
                        <param-value>/WEB-INF/applicationContext-web.xml</param-value> 
                    </init-param> 
                </servlet> 
                <servlet-mapping> 
                    <servlet-name>AddressBookWeb</servlet-name> 
                    <url-pattern>*.do</url-pattern> 
            </servlet-mapping> 
                <filter> 
                    <filter-name>encodingFilter</filter-name> 
                    <filter-class> 
            org.springframework.web.filter.CharacterEncodingFilter 
            </filter-class> 
                    <init-param> 
                        <param-name>encoding</param-name> 
                        <param-value>GB18030</param-value> 
                    </init-param> 
                    <init-param> 
                        <param-name>forceEncoding</param-name> 
                        <param-value>true</param-value> 
                    </init-param> 
                </filter> 
                <filter-mapping> 
                    <filter-name>encodingFilter</filter-name> 
                    <servlet-name>AddressBookWeb</servlet-name> 
                    <url-pattern>*.do</url-pattern> 
                </filter-mapping> 
             | 
        
    
5.1.2        WEB-INF/applicationContext-web.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" 
                   xsi:schemaLocation= 
                   "http://www.springframework.org/schema/beans 
                   http://www.springframework.org/schema/beans/spring-beans.xsd 
                   http://www.springframework.org/schema/aop 
            http://www.springframework.org/schema/aop/spring-aop.xsd"> 
                <bean id="viewResolver"  
                      class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
                    <property name="viewClass">  
                        <value>org.springframework.web.servlet.view.JstlView</value>  
                    </property>  
                    <property name="prefix"> 
                        <value>/WEB-INF/jsp/</value> 
                    </property> 
                    <property name="suffix"> 
                        <value>.jsp</value> 
                    </property> 
                </bean> 
                <bean id="urlMapping"  
                      class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">  
                    <property name="mappings">  
                        <props>  
                            <prop key="/append.do">AppendAction</prop>  
                            <prop key="/select.do">SelectAction</prop>  
                            <prop key="/delete.do">DeleteAction</prop> 
                            <prop key="/update.do">UpdateAction</prop> 
                            <prop key="/doUpdate.do">doUpdateAction</prop> 
                        </props>  
                    </property>  
                </bean> 
                <!-- Application custom configure ---> 
                <bean id="AppendAction" class="web.inAction.AppendAction">  
                    <property name="sessionForm"><value>true</value></property> 
                    <property name="commandName"><value>Address</value></property> 
                    <property name="commandClass"> 
            <value>model.entity.Address.Address</value> 
            </property> 
                    <property name="formView"><value>append</value></property> 
                    <property name="successView"><value>show</value></property> 
                    <property name="svr"> 
                        <ref bean="AddressService" /> 
                    </property> 
            </bean> 
                <bean id="SelectAction" class="web.inAction.SelectAction">  
                    <property name="sessionForm"><value>true</value></property> 
                    <property name="commandName"><value>AddressId</value></property> 
                    <property name="commandClass"> 
            <value>model.key.Address.AddressId</value> 
            </property> 
                    <property name="formView"><value>select</value></property> 
                    <property name="successView"><value>show</value></property> 
                    <property name="svr"> 
                        <ref bean="AddressService" /> 
                    </property> 
            </bean>  
               <bean id="UpdateAction" class="web.inAction.UpdateAction">  
                    <property name="sessionForm"><value>true</value></property> 
                    <property name="commandName"><value>AddressId</value></property> 
                    <property name="commandClass"> 
            <value>model.key.Address.AddressId</value> 
            </property> 
                    <property name="formView"><value>update</value></property> 
                    <property name="successView"><value>doUpdate.do</value></property> 
                    <property name="svr"> 
                        <ref bean="AddressService" /> 
                    </property> 
                </bean> 
                <bean id="doUpdateAction" class="web.inAction.doUpdateAction">  
                    <property name="sessionForm"><value>true</value></property> 
                    <property name="commandName"><value>Address</value></property> 
                    <property name="commandClass"> 
            <value>model.entity.Address.Address</value> 
            </property> 
                    <property name="formView"><value>modify</value></property> 
                    <property name="successView"><value>show</value></property> 
                    <property name="svr"> 
                        <ref bean="AddressService" /> 
                    </property> 
            </bean> 
                <bean id="DeleteAction" class="web.inAction.DeleteAction">  
                    <property name="sessionForm"><value>true</value></property> 
                    <property name="commandName"><value>AddressId</value></property> 
                    <property name="commandClass"><value>model.key.Address.AddressId</value></property> 
                    <property name="formView"><value>delete</value></property> 
                    <property name="successView"><value>show</value></property> 
                    <property name="svr"> 
                        <ref bean="AddressService" /> 
                    </property> 
            </bean> 
            </beans> 
             | 
        
    
注意:此处注册的Action Bean应与后续开发的活动类配套,而且ActionBean仅使用业务逻辑层对象作属性。ActionBean应设计为单例的。由于Web访问的并发性,因此ActionBean应注意使用的model应是Session级别的。如果model中包含了非Session或多例的bean时,操作model的该段代码应实施多线程保护。设计ActionBean时,应避免在model中使用单例对象,除非确有必要。
如果需要在多个ActionBean中共享或交换数据,可以将该数据放置到请求的属性、参数或Session环境中,通过HttpServletRequest的属性getAttribute()或参数getParameter()或arg0.getSession().getAttribute()进行访问。
5.2    编写Web活动类
活动类放置到web包。
5.2.1        web.inAction.inGenericAction
    
        
            | 
             /* 
             * To change this template, choose Tools | Templates 
             * and open the template in the editor. 
             */ 
            package web.inAction; 
            import org.springframework.web.servlet.mvc.SimpleFormController; 
            /** 
             * 
             * @author  
             */ 
            public class inGenericAction extends SimpleFormController 
            { 
            } 
             | 
        
    
5.2.2        web.outAction.outGenericAction
    
        
            | 
             /* 
             * To change this template, choose Tools | Templates 
             * and open the template in the editor. 
             */ 
            package web.outAction; 
            import javax.servlet.http.HttpServletRequest; 
            import javax.servlet.http.HttpServletResponse; 
            import org.springframework.web.servlet.ModelAndView; 
            import org.springframework.web.servlet.mvc.Controller; 
            /** 
             * 
             * @author  
             */ 
            public class outGenericAction implements Controller  
            { 
                public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception 
                { 
                    throw new UnsupportedOperationException("Not supported yet."); 
                } 
            } 
             | 
        
    
5.2.3        web.inAction.SelectAction
SelectAction根据select.jsp输入的AddressId的email值,从数据库检索数据,存储在show.jsp的模型中,供show.jsp显示。如果没有指定值,则检索全部数据。
    
        
            | 
             /* 
             * To change this template, choose Tools | Templates 
             * and open the template in the editor. 
             */ 
            package web.inAction; 
            import business.Address.AddressService; 
            import java.util.HashMap; 
            import java.util.List; 
            import java.util.Vector; 
            import model.entity.Address.Address; 
            import model.key.Address.AddressId; 
            import org.springframework.web.servlet.ModelAndView; 
            /** 
             * 
             * @author  
             */ 
            public class SelectAction extends inGenericAction 
            { 
                private AddressService svr; 
                public void setSvr(AddressService svr) 
                { 
                    this.svr = svr; 
                } 
                @Override 
                protected ModelAndView onSubmit(Object arg0) 
                { 
                    List<Address> list; 
                    AddressId id = (AddressId) arg0; 
                    try 
                    { 
                        if(id.getId().equals(""))  
                        { 
                            list = svr.selectAll(); 
                        } 
                        else 
                        { 
                            list = new Vector<Address>(); 
                            Address address = svr.select(id); 
                            list.add(address); 
                        } 
                    } 
                    catch(Exception e) 
                    { 
                        list = new Vector<Address>(); 
                    } 
                    HashMap model = new HashMap();         
                    model.put("AddressList", list); 
                    return new ModelAndView(getSuccessView(), model); 
                } 
            } 
             | 
        
    
注意:此处是Action返回视图,而非重新定向,因此model是输出的一部分,供Spring生成输出视图时使用。
5.2.4        web.inAction.AppendAction
AppendAction根据append.jsp视图输入的数据,创建Address对象,调用业务逻辑类将其存储到数据库中,然后从数据库获取全部数据,提交给show.jsp视图显示。
Spring 视图与控制间可以交换一个命令对象,如果需要从界面输入多个对象的值时,可以创建一个界面输入专用的实体类,或者将部分对象的值通过没有绑定的单独变量存储(这些值成为HttpServletRequest的属性或参数),在提交处理中赋值给相应的对象。
    
        
            | 
             .jsp 
            <td>email:</td> 
            <td> 
                    <input type="text" name="_PK" value=""> 
                </td> 
            .java 
            String id = (String) arg0.getParameter("_PK"); 
             | 
        
    
本活动类定义的命令对象是Address。但Address类的主键是AddressId类,必须进行初始化,否则生成输入界面时报错。
    
        
            | 
             /* 
             * To change this template, choose Tools | Templates 
             * and open the template in the editor. 
             */ 
            package web.inAction; 
            import business.Address.AddressService; 
            import java.util.HashMap; 
            import java.util.List; 
            import java.util.Map; 
            import javax.servlet.http.HttpServletRequest; 
            import javax.servlet.http.HttpServletResponse; 
            import model.entity.Address.Address; 
            import model.key.Address.AddressId; 
            import org.springframework.validation.BindException; 
            import org.springframework.validation.Errors; 
            import org.springframework.web.servlet.ModelAndView; 
            /** 
             * 
             * @author  
             */ 
            public class AppendAction extends inGenericAction 
            { 
                private AddressService svr; 
                public void setSvr(AddressService svr) 
                { 
                    this.svr = svr; 
                } 
                @Override 
                protected Map referenceData(HttpServletRequest arg0, Object arg1, Errors arg2) throws Exception 
                { 
                    Address address = (Address) arg1; 
                    address.setid(new AddressId("")); 
                    address.setName(""); 
                    address.setHomephone(""); 
                    address.setWorkphone(""); 
                    address.setMobile(""); 
                    address.setPassword(""); 
                    return null; 
                }     
                @Override 
                protected ModelAndView onSubmit(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, BindException arg3) throws Exception 
                { 
                    Address address = (Address) arg2; 
                    svr.save(address); 
                    List<Address> list = svr.selectAll();         
                    HashMap model = new HashMap();         
                    model.put("AddressList", list); 
                    return new ModelAndView(getSuccessView(), model); 
                } 
            } 
             | 
        
    
5.2.5        web.inAction.UpdateAction
UpdateAction分两个步骤,第1步骤从界面获取要修改的email;第2步骤根据email从数据库检索数据供编辑。
本步骤将界面指定的email存储在新视图的模型中,然后重定向到编辑界面。此处使用session属性传递参数,还可以使用HttpServletRequest参数传递。
    
        
            | 
             /* 
             * To change this template, choose Tools | Templates 
             * and open the template in the editor. 
             */ 
            package web.inAction; 
            import business.Address.AddressService; 
            import java.util.HashMap; 
            import javax.servlet.http.HttpServletRequest; 
            import javax.servlet.http.HttpServletResponse; 
            import model.entity.Address.Address; 
            import model.key.Address.AddressId; 
            import org.springframework.validation.BindException; 
            import org.springframework.web.servlet.ModelAndView; 
            import org.springframework.web.servlet.view.RedirectView; 
            /** 
             * 
             * @author  
             */ 
            public class UpdateAction extends inGenericAction 
            { 
                private AddressService svr; 
                public void setSvr(AddressService svr) 
                { 
                    this.svr = svr; 
                } 
                @Override 
                protected ModelAndView onSubmit(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, BindException arg3) throws Exception 
                { 
                    AddressId id = (AddressId) arg2; 
                    HashMap model = new HashMap();  
                    arg0.getSession().setAttribute("AddressId", id); 
                    return new ModelAndView(new RedirectView(getSuccessView()), model); 
                } 
            } 
             | 
        
    
5.2.6        web.inAction.doUpdateAction
本步骤先从模型中得到email,然后根据email从数据库检索address数据供编辑,然后将编辑的数据存储到数据库。
    
        
            | 
             /* 
             * To change this template, choose Tools | Templates 
             * and open the template in the editor. 
             */ 
            package web.inAction; 
            import business.Address.AddressService; 
            import java.util.HashMap; 
            import java.util.List; 
            import java.util.Map; 
            import javax.servlet.http.HttpServletRequest; 
            import javax.servlet.http.HttpServletResponse; 
            import model.entity.Address.Address; 
            import model.key.Address.AddressId; 
            import model.key.Address.AddressId; 
            import model.key.Address.AddressId; 
            import org.springframework.validation.BindException; 
            import org.springframework.validation.Errors; 
            import org.springframework.web.servlet.ModelAndView; 
            /** 
             * 
             * @author  
             */ 
            public class doUpdateAction extends inGenericAction 
            { 
                private AddressService svr; 
                public void setSvr(AddressService svr) 
                { 
                    this.svr = svr; 
                } 
                @Override 
                protected Map referenceData(HttpServletRequest arg0, Object arg1, Errors arg2) throws Exception 
                { 
                    Address address = (Address) arg1; 
                    AddressId id = (AddressId) arg0.getSession().getAttribute("AddressId"); 
                    Address old = svr.select(id); 
                    address.setid(old.getid()); 
                    address.setName(old.getName()); 
                    address.setHomephone(old.getHomephone()); 
                    address.setWorkphone(old.getWorkphone()); 
                    address.setMobile(old.getMobile()); 
                    address.setPassword(old.getPassword()); 
                    return null; 
                } 
                @Override 
                protected ModelAndView onSubmit(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, BindException arg3) throws Exception 
                { 
                    Address address = (Address) arg2; 
                    svr.update(address); 
                    List<Address> list = svr.selectAll();         
                    HashMap model = new HashMap();         
                    model.put("AddressList", list); 
                    return new ModelAndView(getSuccessView(), model); 
                } 
            } 
             | 
        
    
5.2.7        web.inAction.DeleteAction
    
        
            | 
             /* 
             * To change this template, choose Tools | Templates 
             * and open the template in the editor. 
             */ 
            package web.inAction; 
            import business.Address.AddressService; 
            import java.util.HashMap; 
            import java.util.List; 
            import java.util.Vector; 
            import model.entity.Address.Address; 
            import model.key.Address.AddressId; 
            import org.springframework.web.servlet.ModelAndView; 
            /** 
             * 
             * @author  
             */ 
            public class DeleteAction extends inGenericAction 
            { 
                private AddressService svr; 
                public void setSvr(AddressService svr) 
                { 
                    this.svr = svr; 
                } 
                @Override 
                protected ModelAndView onSubmit(Object arg0) 
                { 
                    List<Address> list; 
                    AddressId id = (AddressId) arg0; 
                    try 
                    { 
                        if(!id.getId().equals(""))  
                        { 
                            svr.delete(id); 
                        } 
                        list = svr.selectAll(); 
                    } 
                    catch(Exception e) 
                    { 
                        list = new Vector<Address>(); 
                    } 
                    HashMap model = new HashMap();         
                    model.put("AddressList", list); 
                    return new ModelAndView(getSuccessView(), model); 
                } 
            } 
             | 
        
    
5.3    编写视图
本例使用JSP作为视图。非Spring管理的视图应放置到WebRoot的相对各目录下。由Spring管理的视图,应放置到WEB-INF/jsp目录下(参见5.1.2配置)。
5.3.1        WebRoot/index.jsp
index.jsp提供执行增加、修改、删除、检索的连接,应放置到WebRoot。
    
        
            | 
             <%@page contentType="text/html;charset=GB18030"%> 
            <html> 
                <head> 
                    <title>AddressBook Application</title> 
                </head> 
                <body> 
                    <h2 align="center">AddressBook Application</h2> 
                    <hr width="100%" size="2"> 
                    <a href="select.do">检索</a>   
                    <a href="append.do">添加</a>   
                    <a href="update.do">修改</a>   
                    <a href="delete.do">删除</a> 
                </body> 
            </html> 
             | 
        
    
5.3.2        WEB-INF/jsp/select.jsp
select.jsp提供检索条件输入。
    
        
            | 
             <%@page contentType="text/html" pageEncoding="GB18030"%> 
            <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>  
            <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>  
            <%@taglib prefix="spring" uri="http://www.springframework.org/tags" %> 
            <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %> 
            <html>  
                <head><title>Select Address</title></head>  
                <body><font size="6"><strong>  
                    Which email do you want to select?</strong></font> 
                    <form method="post"> 
                        <table width="440" height="27"> 
                            <tr> 
                                <td>email:</td> 
                                <td> 
                                    <spring:bind path="AddressId.id"> 
                                        <input type="text" name="${status.expression}" value="${status.value}"> 
                                    </spring:bind>  
                                    <input type="submit" alignment="center" value="Select">                    </td> 
                            </tr> 
                        </table> 
                    </form> 
                </body>  
            </html> 
             | 
        
    
path定义视图的模型。
注意:如果实体类没有初始化其各属性,且Action也没有调用referenceData()初始化该对象的各属性,则不能指定绑定路径为AddressId.id,也不能在<input>的name项使用${status.expression}。
5.3.3        WEB-INF/jsp/show.jsp
Show.jsp显示获取的Address数据。其数据均由其他Action类从数据库获取后填写到其模型中,供Show.jsp输出。此视图是所有活动的输出视图,只使用了JTSL标记库。
    
        
            | 
             <%@ page session="false"%> 
            <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>  
            <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> 
            <html>  
                <head><title>Selected Address List</title></head>  
                <body>  
                    <h2 align="center">Selected Address List</h2><hr width="100%" size="2"> 
                    <table width="733" border="1" height="56"> 
                        <tbody> 
                            <tr> 
                                <td><strong>Email</strong></td> 
                                <td><strong>Name</strong></td> 
                                <td><strong>HomePhone</strong></td> 
                                <td><strong>WorkPhone</strong></td> 
                                <td><strong>Mobile</strong></td> 
                                <td><strong>Password</strong></td> 
                            </tr> 
                            <c:forEach items="${AddressList}" var="address"> 
                                <tr> 
                                    <td><c:out value="${address.id.id}" /> </td> 
                                    <td><c:out value="${address.name}" /></td> 
                                    <td><c:out value="${address.homephone}" /></td> 
                                    <td><c:out value="${address.workphone}" /></td> 
                                    <td> <c:out value="${address.mobile}" /></td> 
                                    <td><c:out value="${address.password}" /> </td> 
                                </tr> 
                            </c:forEach> 
                        </tbody> 
                    </table> 
                </body>  
            </html> 
             | 
        
    
5.3.4        WEB-INF/jsp/append.jsp
    
        
            | 
             <%@page contentType="text/html;charset=GB18030"%> 
            <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>  
            <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>  
            <%@taglib prefix="spring" uri="http://www.springframework.org/tags" %> 
            <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %> 
            <html>  
                <head> 
                    <title>AddressBook Application</title> 
                </head> 
                <body><font size="6"> 
                    <strong>Append New Address</strong></font> 
                    <spring:hasBindErrors name="Address"> 
                        <br> 
                        <spring:bind path="Address.*">  
                            <font color="red"> 
                                <b>${status.errorMessage}</b> 
                            </font><br>  
                        </spring:bind> 
                        <br> 
                    </spring:hasBindErrors> 
                    <form method="post"> 
                        <table width="440" height="27"> 
                            <tr> 
                                <td>email:</td> 
                                <td> 
                                    <spring:bind path="Address.id.id"> 
                                        <input type="text" name="${status.expression}" value="${status.value}"> 
                                    </spring:bind>  
                                </td> 
                            </tr> 
                            <tr> 
                                <td>name:</td> 
                                <td> 
                                    <spring:bind path="Address.name"> 
                                        <input type="text" name="${status.expression}" value="${status.value}"> 
                                    </spring:bind>  
                                </td> 
                            </tr> 
                            <tr> 
                                <td>workphone:</td> 
                                <td> 
                                    <spring:bind path="Address.workphone"> 
                                        <input type="text" name="${status.expression}" value="${status.value}"> 
                                    </spring:bind>  
                                </td> 
                            </tr> 
                            <tr> 
                                <td>homephone:</td> 
                                <td> 
                                    <spring:bind path="Address.homephone"> 
                                        <input type="text" name="${status.expression}" value="${status.value}"> 
                                    </spring:bind>  
                                </td> 
                            </tr> 
                            <tr> 
                                <td>mobile:</td> 
                                <td> 
                                    <spring:bind path="Address.mobile"> 
                                        <input type="text" name="${status.expression}" value="${status.value}"> 
                                    </spring:bind>  
                                </td> 
                            </tr> 
                             
                            <tr> 
                                <td>password:</td> 
                                <td> 
                                    <spring:bind path="Address.password"> 
                                        <input type="text" name="${status.expression}" value="${status.value}"> 
                                    </spring:bind>  
                                </td> 
                            </tr>                 
                            <tr> 
                                <input type="submit" alignment="center" value="Append"> 
                            </tr> 
                             
                        </table>      
                    </form> 
                </body>  
            </html> 
             | 
        
    
5.3.5        WEB-INF/jsp/update.jsp
    
        
            | 
             <%@page contentType="text/html" pageEncoding="GB18030"%> 
            <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>  
            <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>  
            <%@taglib prefix="spring" uri="http://www.springframework.org/tags" %> 
            <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %> 
            <html>  
                <head><title>Select Address</title></head>  
                <body><font size="6"><strong>  
                    Which email do you want to update?</strong></font> 
                    <form method="post"> 
                        <table width="440" height="27"> 
                            <tr> 
                                <td>email:</td> 
                                <td> 
                                    <spring:bind path="AddressId"> 
                                        <input type="text" name="${status.expression}" value="${status.value}"> 
                                    </spring:bind>  
                                    <input type="submit" alignment="center" value="Update">                    </td> 
                            </tr> 
                        </table> 
                    </form> 
                </body>  
            </html> 
             | 
        
    
5.3.6        WEB-INF/jsp/modify.jsp
    
        
            | 
             <%@page contentType="text/html" pageEncoding="GB18030"%> 
            <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>  
            <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>  
            <%@taglib prefix="spring" uri="http://www.springframework.org/tags" %> 
            <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %> 
            <html>  
                <head><title>Modify Address</title></head>  
                <body><font size="6"> 
                    <strong>Modify Address</strong></font> 
                    <spring:hasBindErrors name="Address"> 
                        <br> 
                        <spring:bind path="Address.*">  
                            <font color="red"> 
                                <b>${status.errorMessage}</b> 
                            </font><br>  
                        </spring:bind> 
                        <br> 
                    </spring:hasBindErrors> 
                    <form method="post"> 
                        <table width="440" height="27"> 
                            <tr> 
                                <td>email:</td> 
                                <td> 
                                    <spring:bind path="Address.id.id"> 
                                        <input type="text" name="${status.expression}" value="${status.value}" disabled> 
                                    </spring:bind>  
                                </td> 
                            </tr> 
                            <tr> 
                                <td>name:</td> 
                                <td> 
                                    <spring:bind path="Address.name"> 
                                        <input type="text" name="${status.expression}" value="${status.value}"> 
                                    </spring:bind>  
                                </td> 
                            </tr> 
                            <tr> 
                                <td>workphone:</td> 
                                <td> 
                                    <spring:bind path="Address.workphone"> 
                                        <input type="text" name="${status.expression}" value="${status.value}"> 
                                    </spring:bind>  
                                </td> 
                            </tr> 
                            <tr> 
                                <td>homephone:</td> 
                                <td> 
                                    <spring:bind path="Address.homephone"> 
                                        <input type="text" name="${status.expression}" value="${status.value}"> 
                                    </spring:bind>  
                                </td> 
                            </tr> 
                            <tr> 
                                <td>mobile:</td> 
                                <td> 
                                    <spring:bind path="Address.mobile"> 
                                        <input type="text" name="${status.expression}" value="${status.value}"> 
                                    </spring:bind>  
                                </td> 
                            </tr> 
                            <tr> 
                                <td>password:</td> 
                                <td> 
                                    <spring:bind path="Address.password"> 
                                        <input type="text" name="${status.expression}" value="${status.value}"> 
                                    </spring:bind>  
                                </td> 
                            </tr>                 
                             
                            <tr> 
                                <input type="submit" alignment="center" value="Modify"> 
                            </tr> 
                        </table>      
                    </form> 
                </body>  
            </html> 
             | 
        
    
5.3.7        WEB-INF/jsp/delete.jsp
    
        
            | 
             <%@ page language="java" pageEncoding="GB18030"%> 
            <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>  
            <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>  
            <%@taglib prefix="spring" uri="http://www.springframework.org/tags" %> 
            <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %> 
            <html lang="true">  
                <head><title>Select Address</title></head>  
                <body><font size="6"><strong>  
                    Which email do you want to delete?</strong></font> 
                    <form method="post"> 
                        <table width="440" height="27"> 
                            <tr> 
                                <td>email:</td> 
                                <spring:bind path="AddressId.id"> 
                                    <input type="text" name="${status.expression}" value="${status.value}"> 
                                </spring:bind>  
                                <input type="submit" alignment="center" value="Delete"> 
                            </tr> 
                        </table> 
                    </form> 
                </body>  
            </html> 
             | 
        
    
6          编写WebService类(给瘦客户端)
本例使用JAX-WS作为WebService的发布工具。
由于WebService调用业务逻辑类完成业务处理,而业务逻辑类是由Spring管理的,因此必须将JAX-WS与Spring进行集成。(如果在WebService中脱离Spring管理直接创建业务逻辑类对象,由于业务逻辑类缺少Spring赋予的依赖关系,将导致创建的业务逻辑类对象无法工作或无法使用Spring环境的其他特性,如配置式事务)。
将JAX-WS与Spring进行集成,方法一就是在WebService中获取Spring的applicationContext,然后通过applicationContext访问Spring环境的各对象。方法二是使用jaxws-spring.jar工具包。该包使用com.sun.xml.ws.transport.http. servlet.WSSpringServlet替换JAX-WS默认的com.sun.xml.ws.transport.http. servlet .WSServlet,实现在Spring环境下创建WebService对象,从而使得WebService对象可以访问Spring环境的其他对象,包括业务逻辑类对象。jaxws-spring.jar需要xbean-spring.jar作为支持包。
方法一可以在Tomcat和glassfish中使用,但WebService对象本身是脱离Spring管理的,因此不能使用Spring的各种服务,只能通过applicationContext访问Spring环境中的对象。方法二WebService对象本身是受Spring管理的,因此可以使用Spring的各种服务,但在glassfish不支持此模式。本例中使用方法一即可满足要求。
6.1    方法一
6.1.1        Tomcat
使用netbeans创建AddressWebService,如果运行环境是Tomcat,则netbeans创建JAX-WS WebService时,会自动进行下列修改:
6.1.1.1      WEB-INF/web.xml
Web.xml增加JAX-WS的监听器、Servlet及地址映射。
    
        
            | 
                 <listener> 
                    <listener-class> 
            com.sun.xml.ws.transport.http.servlet.WSServletContextListener 
            </listener-class> 
            </listener> 
            <servlet> 
                    <servlet-name>AddressWebService</servlet-name> 
                    <servlet-class> 
            com.sun.xml.ws.transport.http.servlet.WSServlet 
            </servlet-class> 
                    <load-on-startup>1</load-on-startup> 
            </servlet> 
                <servlet-mapping> 
                    <servlet-name>AddressWebService</servlet-name> 
                    <url-pattern>/AddressWebService</url-pattern> 
                </servlet-mapping> 
             | 
        
    
6.1.1.2      WEB-INF/sun-jaxws.xml
JAX-WS使用本文件配置环境中存在的WebService,而该WebService具有的功能(WSDL),则是根据Java 5风格的注释信息自动生成的。
    
        
            | 
             <?xml version="1.0" encoding="UTF-8"?> 
            <endpoints version="2.0" xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime"> 
             <endpoint implementation="websrv.AddressWebService" name="AddressWebService" url-pattern="/AddressWebService"/> 
            </endpoints> 
             | 
        
    
6.1.1.3      websrv/AddressWebService
本例中我们创建下列服务:
l         public Address Select(AddressId id);
l         public void Delete(AddressId id);
l         public void Append(Address address);
l         public void Modify(Address address);
l         public List<Address> SelectAll().
    
        
            | 
             /* 
             * To change this template, choose Tools | Templates 
             * and open the template in the editor. 
             */ 
            package websrv; 
            import business.Address.AddressService; 
            import java.util.List; 
            import javax.annotation.Resource; 
            import javax.jws.Oneway; 
            import javax.jws.WebMethod; 
            import javax.jws.WebParam; 
            import javax.jws.WebService; 
            import javax.servlet.ServletContext; 
            import javax.xml.ws.WebServiceContext; 
            import javax.xml.ws.handler.MessageContext; 
            import model.entity.Address.Address; 
            import model.key.Address.AddressId; 
            import org.springframework.web.context.WebApplicationContext; 
            import org.springframework.web.context.support.WebApplicationContextUtils; 
            /** 
             * 
             * @author  
             */ 
            @WebService() 
            public class AddressWebService 
            { 
                @Resource 
                private WebServiceContext context;     
                /** 
                 * Select Address from database. 
                 * @param id 
                 * @return 
                 */ 
                @WebMethod(operationName = "Select") 
                public Address Select(@WebParam(name = "id") AddressId id) 
                { 
                    ServletContext servletContext = (ServletContext) context.getMessageContext().get(MessageContext.SERVLET_CONTEXT); 
                    WebApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext); 
                    AddressService svr = (AddressService) applicationContext.getBean("AddressService");  
                    Address address = svr.select(id); 
                    return address; 
                } 
                /** 
                 * Web service operation 
                 * @param address  
                 */ 
                @WebMethod(operationName = "Update") 
                public void Update(@WebParam(name = "address") Address address) 
                { 
                    ServletContext servletContext = (ServletContext) context.getMessageContext().get(MessageContext.SERVLET_CONTEXT); 
                    WebApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext); 
                    AddressService svr = (AddressService) applicationContext.getBean("AddressService");  
                    svr.update(address); 
                } 
                /** 
                 * Web service operation 
                 * @param address  
                 */ 
                @WebMethod(operationName = "Insert") 
                public void Insert(@WebParam(name = "address") Address address) 
                { 
                    ServletContext servletContext = (ServletContext) context.getMessageContext().get(MessageContext.SERVLET_CONTEXT); 
                    WebApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext); 
                    AddressService svr = (AddressService) applicationContext.getBean("AddressService");  
                    svr.save(address); 
                } 
                /** 
                 * Web service operation 
                 * @param id  
                 */ 
                @WebMethod(operationName = "Delete") 
                public void Delete(@WebParam(name = "id") AddressId id) 
                { 
                    ServletContext servletContext = (ServletContext) context.getMessageContext().get(MessageContext.SERVLET_CONTEXT); 
                    WebApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext); 
                    AddressService svr = (AddressService) applicationContext.getBean("AddressService");  
                    svr.delete(id); 
                } 
                /** 
                 * Web service operation 
                 * @return  
                 */ 
                @WebMethod(operationName = "SelectAll") 
                public List<Address> SelectAll() 
                { 
                    ServletContext servletContext = (ServletContext) context.getMessageContext().get(MessageContext.SERVLET_CONTEXT); 
                    WebApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext); 
                    AddressService svr = (AddressService) applicationContext.getBean("AddressService");  
                    return svr.selectAll(); 
                } 
            } 
             | 
        
    
@resource注释指示容器创建WebService对象时自动将指定的资源注入对象中。
6.1.2        glassfish
glassfish自身包含了JAX-WS包,部署在glassfish时不必使用JAX-WS包,而且不必加载JAX-WS的Servlet。调整如下:
(1)       删除JAX-WS包;
(2)       编辑WEB-INF/web.xml取消JAX-WS的监听器、Servlet、地址映射;
    
        
            | 
             <!-- 
                <listener> 
                    <listener-class> 
            com.sun.xml.ws.transport.http.servlet.WSServletContextListener 
            </listener-class> 
                </listener> 
              
                <servlet> 
                    <servlet-name>AddressWebService</servlet-name> 
                    <servlet-class> 
            com.sun.xml.ws.transport.http.servlet.WSServlet 
            </servlet-class> 
                    <load-on-startup>0</load-on-startup> 
                </servlet> 
              
                <servlet-mapping> 
                    <servlet-name>AddressWebService</servlet-name> 
                    <url-pattern>/AddressWebService</url-pattern> 
                </servlet-mapping> 
            --> 
             | 
        
    
6.2    方法二
方法二使用jaxws-spring.jar和xbean-spring.jar,将创建WebService对象任务委托给Spring,因此可以使用Spring的各种服务,但WebService的发布模式与glassfish冲突,因此无法在glassfish中使用。
6.2.1        WEB-INF/web.xml
使用com.sun.xml.ws.transport.http. servlet.WSSpringServlet替换JAX-WS默认的com.sun.xml.ws.transport.http. servlet .WSServlet。
    
        
            | 
                 <servlet> 
                    <servlet-name>AddressWebService</servlet-name> 
                    <servlet-class> 
            com.sun.xml.ws.transport.http.servlet.WSSpringServlet 
            </servlet-class> 
                    <load-on-startup>0</load-on-startup> 
                </servlet> 
                <servlet-mapping> 
                    <servlet-name>AddressWebService</servlet-name> 
                    <url-pattern>/AddressWebServiceService</url-pattern> 
                </servlet-mapping> 
             | 
        
    
注意:glassfish自动生成的服务名为AddressWebServiceService,为避免更换应用服务器导致客户端修改,因此此处修改Tomcat下的服务名保持与glassfish一致。此外,还需修改WEB-INF/sun-jaxws.xml与之配套。
    
        
            | 
             <endpoint  
            implementation="websrv.AddressWebService"  
            name="AddressWebService"  
            url-pattern="/AddressWebServiceService"/> 
             | 
        
    
6.2.2        WEB-INF/classes/applicationContext.xml
本文件注册WebService对象为bean,并由Spring注入业务逻辑层对象。
    
        
            | 
             <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:ws="http://jax-ws.dev.java.net/spring/core" 
                xmlns:wss="http://jax-ws.dev.java.net/spring/servlet" 
                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.xsd" 
                http://jax-ws.dev.java.net/spring/core 
                http://jax-ws.dev.java.net/spring/core.xsd 
                http://jax-ws.dev.java.net/spring/servlet 
                http://jax-ws.dev.java.net/spring/servlet.xsd"> 
            ...... 
            <wss:binding url="/AddressWebServiceService"> 
                <wss:service> 
                    <ws:service bean="#AddressWebService" /> 
                </wss:service> 
            </wss:binding> 
                 
            <bean id="AddressWebService" class="websrv.AddressWebService" > 
                <property name="svr"> 
                   <ref bean="AddressService" /> 
                </property> 
            </bean> 
             | 
        
    
6.2.2.1      websrv/AddressWebService
    
        
            | 
             /* 
             * To change this template, choose Tools | Templates 
             * and open the template in the editor. 
             */ 
            package websrv; 
            import business.Address.AddressService; 
            import java.util.List; 
            import javax.annotation.Resource; 
            import javax.jws.Oneway; 
            import javax.jws.WebMethod; 
            import javax.jws.WebParam; 
            import javax.jws.WebService; 
            import javax.servlet.ServletContext; 
            import javax.xml.ws.WebServiceContext; 
            import javax.xml.ws.handler.MessageContext; 
            import model.entity.Address.Address; 
            import model.key.Address.AddressId; 
            import org.springframework.web.context.WebApplicationContext; 
            import org.springframework.web.context.support.WebApplicationContextUtils; 
            /** 
             * 
             * @author  
             */ 
            @WebService() 
            public class AddressWebService 
            { 
                AddressService svr; 
                public void setSvr(AddressService svr) 
                { 
                    this.svr = svr; 
            } 
                /** 
                 * Select Address from database. 
                 * @param id 
                 * @return 
                 */ 
                @WebMethod(operationName = "Select") 
                public Address Select(@WebParam(name = "id") AddressId id) 
                { 
                    return svr.select(id); 
                } 
                /** 
                 * Web service operation 
                 * @param address  
                 */ 
                @WebMethod(operationName = "Update") 
                public void Update(@WebParam(name = "address") Address address) 
                { 
                    svr.update(address); 
                } 
                /** 
                 * Web service operation 
                 * @param address  
                 */ 
                @WebMethod(operationName = "Insert") 
                public void Insert(@WebParam(name = "address") Address address) 
                { 
                    svr.save(address); 
                } 
                /** 
                 * Web service operation 
                 * @param id  
                 */ 
                @WebMethod(operationName = "Delete") 
                public void Delete(@WebParam(name = "id") AddressId id) 
                { 
                    svr.delete(id); 
                } 
                /** 
                 * Web service operation 
                 * @return  
                 */ 
                @WebMethod(operationName = "SelectAll") 
                public List<Address> SelectAll() 
                { 
                    return svr.selectAll(); 
                } 
            } 
             | 
        
    
7          异常处理
Java包含两大类异常,即RuntimeException和Checked Exception异常。
Checked Exception是程序必须进行处理的异常。如果某方法调用抛出Checked Exception异常的方法,则此方法要么捕获该异常,要么声明自身可能抛出该异常。而RuntimeException异常则方法可以不必捕获或继续声明。换句话说,如果应用必须对某异常进行特殊的处理,则应将该异常定义为Checked Exception;而应用只需知道发生了异常,并不关心该异常是什么异常时,则应将该异常定义为RuntimeException。因此,业务需求规定的异常情况应定义为Checked Exception。
Spring的配置式事务环境规定,仅在Spring捕获到RuntimeException异常时才会触发事务回滚。通常情况下发生系统级或业务级异常时,均应回滚事务,显然Spring默认的模式不满足要求,需要进行调整。调整的做法是修改applicationContext.xml配置,指明当发生某Checked Exception异常时,Spring也应做回滚处理。(参见org.springframework. transaction.interceptor. TransactionAttribute)
    
        
            | 
             <bean id="txProxyTemplate"  
            class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true"> 
                    <property name="transactionManager" ref="transactionManager"/> 
                    <property name="transactionAttributes"> 
                        <props>             
                            <prop key="select*">PROPAGATION_REQUIRED,readOnly</prop> 
                            <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop> 
                            <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop> 
                            <prop key="save*">PROPAGATION_REQUIRED,-Exception</prop> 
                            <prop key="update*">PROPAGATION_REQUIRED,-Exception</prop> 
                            <prop key="remove*">PROPAGATION_REQUIRED,-Exception</prop> 
                            <prop key="del*">PROPAGATION_REQUIRED,-Exception</prop> 
                        </props> 
                    </property> 
                </bean> 
             | 
        
    
更改配置后,所有的异常均会导致Spring执行事务回滚处理。如果方法中希望发生某异常时能提交事务,则应在方法中捕获该异常,将其转换为非配置列表的其他异常,从而触发Spring提交事务(目前应没有这样的特殊要求)。
复杂的业务逻辑通常会有较多的业务级异常,这些异常的处理模式也基本相同,而且也需要将这些异常通知界面,以便操作员清楚发生了什么异常。如果完全使用返回码,则程序处处都需要对返回码进行处理,影响程序的质量和可靠性,因此应尽量使用抛出异常模式。
业务级异常有两种可选的方案:一是仅使用一种异常类,由异常对象的错误码判断发生了什么异常;二是为每种异常设计一个异常类,从而形成一个异常体系。前者适合需要特殊处理的异常较多的情况,后者适合个别异常需要特殊处理的情况。由于业务需求的不确定性,系统设计时很难确定需进行特别处理的异常,因此建议使用单异常类多错误码的模式,这样也可以简化程序的设计,仅需要捕获一种异常即可。
7.1    通用异常类
7.2    业务逻辑层异常处理
7.3    WEB层异常处理
7.4 WebService层异常处理
MVC
如果想在另一个页面中显示错误,则可以:
    
        
            | 
             @Override 
            protected ModelAndView onSubmit(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, BindException arg3) throws Exception 
            { 
                arg3.reject("AddressId", "…error message…"); // 创建错误对象 
            Map model = arg3.getModel(); 
                model.put("AddressId", arg2);              // 添加Command对象到模型 
                model.put("errors", arg3);                 // 添加错误到模型 
            return new ModelAndView(getFormView(), model); 
            } 
             | 
        
    
如果想在同一个页面中显示错误,则可以:
    
        
            | 
             @Override 
            protected ModelAndView onSubmit(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, BindException arg3) throws Exception 
            { 
            arg3.reject("AddressId.id", "没有此数据!"); 
                return showForm(arg0, arg1, arg3); 
            } 
             | 
        
    
JSP:
    
        
            | 
                     <spring:hasBindErrors name="AddressId"> 
                        <br> 
                        <spring:bind path="AddressId">  
                            <font color="red"> 
                                <b>${status.errorMessage}</b> 
                            </font><br>  
                        </spring:bind> 
                        <br> 
                    </spring:hasBindErrors> 
             | 
        
    
错误页:
8          总结
l         如果某个类可能变更实现方法,则应为其设计接口,以便调用者可以不必修改即可使用新的实现。因此Dao类与业务逻辑类应设计接口,而实体类、主键类、活动类不必设计接口。
l         带状态的bean应配置为多例(prototype),而无状态的bean应配置为单例(single),Web MVC环境下需在多个视图间共享的bean应配置为session,单个视图中使用的bean应配置为request。
l         session和request类型的bean应定义代理类;
l         实体类和主键类是多例的,其他非WEB MVC的类是单例的;
l         为每个数据库对象建立一个实体类和主键类;
l         实体类与主键类必须初始化所有的域;字符串域应trim()。
l         主键类必须提供一个带参的构造函数初始化主键;
l         继承框架类时应增加一个基础类隔离框架类与应用类;
l         实体类、主键类、Dao类不必处理异常;业务逻辑类应截获并处理所有的异常,并将全部异常包括(系统级的异常)转换为应用级异常然后再抛出,以避免影响Spring的配置式事务;Web应截获并处理全部异常,根据异常类型决定显示在当前页做提示,还是显示给专门的错误页面。WebService应将异常转换为返回码、错误码与错误信息,传递给客户端。
	posted on 2007-11-20 11:55 
飞鹰 阅读(4971) 
评论(1)  编辑  收藏