第七步、编写Action和JSP。 在SpringSide 3.1.4.3中,使用的是Struts 2及其Convention插件,已经不是前面使用的CodeBehind插件了,关于Convention插件,这里要再说几句,该插件的大部分功能和 CodeBehind相同,唯一让人有点迷惑的就是该插件到哪里寻找Action类的问题,它会根据 struts.convention.package.locators属性的值来决定,在该项目中,其值为“web”,之需要查阅一下 struts.xml文件即可知。这说明,Convention会寻找所有包含“web”这个单词的包,并在该包及其子包中寻找Action类。这也正是 Action层的包名为personal.youxia.web的原因。

关于SpringSide 3种的Struts的探讨,大家可以看看我之前写的一篇文章SpringSide 3 中的 Struts 2

ArticleAction的实现思路如下,修改index.jsp,使其重定向到article.action,该Action默认调用其list方法 显示所有文章,并返回article.jsp作为其视图。在该视图上,有添加文章的连接,点击该连接则访问article!input.action,这 时会调用ArticleAction的input方法,并返回article-input.jsp作为其视图,在该视图中输入文章的内容,点击保存,调用 article!save.action,这时会调用ArticleAction的save方法以保存数据,如果要删除文章,则调用 article!delete.action,这时会调用ArticleAction的delete方法。在调用以上方法的过程中,会自动调用 prepare系列的方法。

因此,该步骤涉及到三个JSP文件和一个Action类,它们分别是
index.jsp
article.jsp
article-input.jsp
ArticleAction.java

index.jsp的修改很简单,只是让项目一启动后就去访问ArticleAction,而不是默认的UserAction。index.jsp的代码如下:

<%  response.sendRedirect( " article.action " );  %>

这时,重点进入到ArticleAction中,创建该Action,其代码的框架如下:

package  personal.youxia.web;

import  personal.youxia.entity.entities.Article;

public   class  ArticleAction  extends  CrudActionSupport < Article >   {

    @Override
     public  String delete()  throws  Exception  {
         //  TODO Auto-generated method stub
         return   null ;
    }

    @Override
     public  String list()  throws  Exception  {
         //  TODO Auto-generated method stub
         return   null ;
    }

    @Override
     protected   void  prepareModel()  throws  Exception  {
         //  TODO Auto-generated method stub

    }

    @Override
     public  String save()  throws  Exception  {
         //  TODO Auto-generated method stub
         return   null ;
    }

     public  Article getModel()  {
         //  TODO Auto-generated method stub
         return   null ;
    }

}

可以看到,该Action从CrudActionSupport类继承,而CrudActionSupport又继承自ActionSupport,并实 现了ModelDriven和Preparable接口,这样Struts 2的ModelDriven拦截器和Preparable拦截器就会对我们自己的Action发生作用。CrudActionSupport中的 excute方法默认的实现是调用list方法,所以访问article.action就等于访问ArticleAction的list方法,该方法的目 的是为了列出所有的文章,所以在该方法中使用了ArticleDao的分页查询,查询结果放在一个page对象中。在Struts 2中,已经没有了ActionForm的概念,可以直接把Action对象传递到视图中,为了能够在视图中访问page对象,只需要把page对象作为 ArticleAction的一个属性即可。先在ArticleAction.java中加入几行代码:

    @Autowired
     private  ArticleManager articleManager;

public   void  setArticleManager(ArticleManager articleManager) {
         this .articleManager  =  articleManager;
    }
    
     private  Page < Article >  page  =   new  Page < Article > ( 10 );
    
     public  Page < Article >  getPage() {
         return  page;
    }

可以看到该代码的作用是为了注入ArticleManager和初始化Page对象,此时list方法的代码就非常简单,如下:

@Override
     public  String list()  throws  Exception {
        page  =  articleManager.getAll(page);
         return  SUCCESS;
    }

由于该方法只是简单获取一个页面的Acticle,所以代码很简单,使用articleManager.getAll方法即可。如果要实现复杂的条件查 询,就需要创建一个包含PropertyFilter对象的列表,然后使用articleManager.search方法进行查询,为了简化 PropertyFilter对象列表的创建,白衣提供了HibernateWebUtils.buildPropertyFilters()静态方法供 大家使用。

list方法返回的是SUCCESS,因此返回给用户的视图页面为article.jsp,该页面应该存放在WEB-INF目录的content目录中, 这也是Convention插件的一个特性,这样用户就没有办法直接访问到视图页面了。在该页面中,可以通过访问page对象来显示数据,如下:

<% @ page language = " java "  contentType = " text/html; charset=UTF-8 "
    pageEncoding = " UTF-8 " %>
<% @ include file = " /common/taglibs.jsp " %>
<! DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd" >
< html >
< head >
< meta  http-equiv ="Content-Type"  content ="text/html; charset=UTF-8" >
< title > Insert title here </ title >
</ head >
< body >
< table >
     < tr >< td >< a  href ="article!input.action" > 添加文章 </ a ></ td ></ tr >
     < s:iterator  value ="page.result" >
         < tr >
             < td > ${subject} </ td >
             < td >< a  href ="article!delete.action?id=${id}" > 删除 </ a ></ td >
         </ tr >
         < tr >
             < td > ${content} </ td >
         </ tr >
     </ s:iterator >
</ table >
</ body >
</ html >

如果数据库中有初始数据的话,该项目运行效果如下图:

到目前为止,还没有涉及到getModel()、prepareModel()、以及prepare系列的方法,但是,一旦需要添加或者删除文章,这一系 列的方法就有作用了。在Struts 2中,由于没有了ActionForm的概念,所有的页面传入参数都会被注入到Action中,如果不想在Action中搞太多的getter和 setter,最有效的方法就是提供一个Model对象,这时候拦截器会把页面参数注入到Model中,而在目前的项目中,没有比Entity类更适合做 Model对象的了。通过观察CrudActionSupport基类,可以发现只有在执行save和input方法之前,才会执行 prepareModel方法,该方法可以保证getModel方法返回的对象不是一个空指针,而调用delete方法之前Model对象没有初始化,但 是delete方法只需要一个id作为参数,因此,可以在Action中增加一个id属性来满足要求。这时候,有改动的几行代码如下:

     private  Long id;
     private  Article article;

public   void  setId(Long id) {
         this .id  =  id;
    }

@Override
     protected   void  prepareModel()  throws  Exception {
         if  (id  !=   null ) {
            article  =  articleManager.get(id);
        }  else  {
            article  =   new  Article();
        }
    }
     public  Article getModel() {
         return  article;
    }

@Override
     public  String delete()  throws  Exception {
        articleManager.delete(id);
         return  RELOAD;
    }

这里需要特别关注的是delete方法返回的值,为RELOAD,这是一个在基类中定义好了的字符串。返回该字符串的目的,是为了在delete方法执行 完之后,不返回任何视图页面,而是以redirect的方式再次调用article.action,以便显示删除文章后的结果。因此,需要在 ArticleAction中使用@Result注解,如下:

@Results( { @Result(name  =  CrudActionSupport.RELOAD, location  =   " article.action " , type  =   " redirect " ) })

经过如上修改,这时候再运行应用,就发现能够删除文章了。

再来实现添加文章的功能,从上面的article.jsp中可以看出,添加文章的链接为article!input.action,此时,会运行 ArticleAction的input方法,该方法只是简单返回article-input.jsp视图文件作为用户输入文章的接口,article- input.jsp的代码如下:

<% @ page language = " java "  contentType = " text/html; charset=UTF-8 "
    pageEncoding = " UTF-8 " %>
<! DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd" >
< html >
< head >
< meta  http-equiv ="Content-Type"  content ="text/html; charset=UTF-8" >
< title > Insert title here </ title >
</ head >
< body >
< form  id ="inputForm"  action ="article!save.action"  method ="post" >
< table  class ="inputView" >
     < tr >
         < td > 主题: </ td >
         < td >< input  type ="text"  name ="subject"  size ="40"  id ="subject"   /></ td >
     </ tr >
     < tr >
         < td > 内容: </ td >
         < td >< textarea  name ="content"  id ="subject" ></ textarea ></ td >
     </ tr >
     < tr >
         < td  colspan ="2" >
             < input  type ="submit"  value ="提交"   /> &nbsp;  
             < input  type ="button"  value ="取消"  onclick ="history.back()" />
         </ td >
     </ tr >
</ table >
</ form >
</ body >
</ html >

而ArticleAction中只需要修改如下几行,由于ModelDriven拦截器已经把网页中传入的数据注入到了article对象中,所以save方法中只需要执行简单的保存操作即可:

    @Override
     public  String input()  throws  Exception {
         return  INPUT;
    }
    
    @Override
     public  String save()  throws  Exception {
        articleManager.save(article);
         return  RELOAD;
    }

至于实现文章的修改功能,那也是通过input方法和save方法实现的,只不过此时网页参数中会包含一个有效的id,而prepare系列的方法会根据 该id先从数据库中提取数据,然后显示在article-input.jsp中,用户修改后,再调用save方法保存到数据库中。为减少本博文长度,该功 能此处不做示范。

通过上面的步骤可以发现,使用SpringSide 3中推荐的CRUD一体的模式,可以有效减少Action的数量和JSP文件的数量,每实现一个增删查改功能,只需要一个Action和两个JSP,但是,程序员一定要对其中的数据流向有充足的认识,才能理清它们之间的关系,不至于晕头转向。

到这里大家会发现,ArticleAction谁都可以访问,一点都不安全,所以第八步我会探讨如何让ArticleAction和SpringSecurity一起工作,至于第九步,当然是把项目从单数据库环境更改到多数据库环境了。具体内容,且看下回分解!

第八步、使用Spring Security保护Web资源 。在SpringSide 3项目中,已经整合进了SpringSecurity,实现了符合RBAC规范的权限管理系统,并把数据保存到了数据库中。我以前的博文SpringSide 3 中的安全框架 中对SpringSecurity有一个初步的探讨,我认为我写的东西对入门来说是很有帮助的,入门以后再深入就简单了,在评论中我又补充了几点,其中就 提到如果要把资源权限配置内容放到数据库中,就要从objectDefinitionSource着手。事实上,在最新的SpringSide 3版本中,就是通过定义一个databaseDefinitionSource来实现从数据库中读取资源和权限之间的关系,而 databaseDefinitionSource引用resourceDetailService,而该Service调用 personal.youxia.service.security.ResourceDetailServiceImpl。就是这样一层套一层的关系, 但是最终只需要用户实现personal.youxia.service.security.ResourceDetailServiceImpl即可。 在SpringSide 3项目中,实现该Service的工作都可以省略,因为江南白衣已经做好了。而我们要做的,就是在他提供的基础上进行扩展。

在项目中,已经定义好了users、roles、authorities和resource,如果需要扩展其中任意一项,只需要向对应的数据表添加记录即 可。预定义的role有“管理员”和“用户”两种,我认为在该示例中已经没有增加角色的必要了,而authorities是肯定要增加的,我想让只有“用 户”能够添加文章,只有“管理员”能够删除文章,所以在authorities表中增加如下两行:

insert   into  AUTHORITIES (NAME,DISPLAY_NAME)  values ( ' A_ADD_ARTICLE ' , ' 添加文章 ' );
insert   into  AUTHORITIES (NAME,DISPLAY_NAME)  values ( ' A_DELETE_ARTICLE ' , ' 删除文章 ' );

建立authorities表和roles表的联系,用户可以添加文章,管理员当然也能够添加文章,而只有管理员能够删除文章,所以在数据库中添加如下三行:

insert   into  ROLES_AUTHORITIES  values ( 1 , 5 );
insert   into  ROLES_AUTHORITIES  values ( 1 , 6 );
insert   into  ROLES_AUTHORITIES  values ( 2 , 5 );

再来看看需要保护的资源,它们应该分别为article.action、article!input.action、article!save.action、article!delete.action,其中只有后面三个需要保护,因此在数据库中添加如下三行:

insert   into  RESOURCES (RESOURCE_TYPE,VALUE,ORDER_NUM)  values ( ' url ' , ' /article!input* ' , 7.0 );
insert   into  RESOURCES (RESOURCE_TYPE,VALUE,ORDER_NUM)  values ( ' url ' , ' /article!save* ' , 8.0 );
insert   into  RESOURCES (RESOURCE_TYPE,VALUE,ORDER_NUM)  values ( ' url ' , ' /article!delete* ' , 9.0 );

最后,需要建立授权和资源之间的联系,如下:

insert   into  RESOURCES_AUTHORITIES  values ( 5 , 7 );
insert   into  RESOURCES_AUTHORITIES  values ( 5 , 8 );
insert   into  RESOURCES_AUTHORITIES  values ( 6 , 9 );

这时,再运行项目,会发现没有登录的用户只能察看文章,而点击增加文章或删除文章的链接,就会跳到Login界面,或显示你没有访问该页面的权限。

对于项目中自带的用户、角色、授权和资源管理,我是这样的看法:最开始接触SpringSide 3项目的时候,我觉得该功能是个鸡肋,甚至想过把这些功能删除掉,弄一个干净的项目从头做;经过一段时间的思考后,我的观念变了,我觉得这个功能非常有 用,是一个很好的基础,而我们自己的功能,都可以从这里进行扩展;这里的扩展,大部分都只需要在数据库中添加数行记录即可,正如上面的演示,唯一不能达到 要求的,可能是有的人觉得users表字段太少,而实际项目中我们要记录用户的信息远远不止这么少,其实这个问题也好解决,只需要创建一个 user_details表即可,或者叫user_profiles,再按照之前的步骤创建针对user_details表的增删查改功能;总之,尽量不 要去更改江南白衣已经实现了的数据库结构和程序代码。

SpringSecurity针对资源的保护,不仅仅是只能在数据库中配置,其实SpringSecurity更提供了一些有用的标签,可以在视图文件中灵活使用。具体内容,大家请参考SpringSecurity文档。

 第九步、将项目迁移到多数据库环境。 其实只要了解前面的八步,简单的项目就可以搞定了,但是对于致力于高并发高负载的分布式应用,则离不开多数据源和分布式事务管理,Web Service和AJAX的跨域访问也是做分布式应用的有力武器。在我前面的博文中,我已经探讨过了多数据源配置的各种问题:
SpringSide 3 中的多数据源配置的问题
在SpringSide 3 中使用多个数据库的方法

在这里,我选择了第三种方法,就是在Spring中整合Atomikos。下载Atomikos 3.5.5版,把如下transactions-essentials-alljar文件和jta.properties文件拷入到项目的WEB-INF/lib目录下。

创建第二个数据库,名称为MultiDatasourceExampleIndex,其中包含一个Article表,如下:

创建Entity类ArticleIndex.java,创建Dao类ArticleIndexDao.java,创建Manager类 ArticleIndexManager.java,这几个过程和前面的过程没有什么区别,所以我就不列代码出来了。为了减少编写Action的工作,我 将添加ActionIndex的动作放到了ArticleAction中,即在添加Article的同时添加ArticleIndex。

修改applicationContext.xml文件,配置双份的dataSource、双份的sessionFactory,并使用JTATransactionManager,如下:

<? 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:jee ="http://www.springframework.org/schema/jee"  xmlns:tx ="http://www.springframework.org/schema/tx"
    xmlns:context ="http://www.springframework.org/schema/context"
    xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"
    default-lazy-init ="true" >

< description > Spring公共配置文件  </ description >

<!--  定义受环境影响易变的变量  -->
     < bean  class ="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" >
         < property  name ="systemPropertiesModeName"  value ="SYSTEM_PROPERTIES_MODE_OVERRIDE"   />
         < property  name ="ignoreResourceNotFound"  value ="true"   />
         < property  name ="locations" >
             < list >
                 <!--  标准配置  -->
                 < value > classpath*:/application.properties </ value >
                 <!--  本地开发环境配置  -->
                 < value > classpath*:/application.local.properties </ value >
                 <!--  服务器生产环境配置  -->
                 <!--  <value>file:/var/myapp/application.server.properties</value>  -->
             </ list >
         </ property >
     </ bean >

<!--  使用annotation 自动注册bean,并保证@Required,@Autowired的属性被注入  -->
     < context:component-scan  base-package ="personal.youxia"   />

< bean  id ="dataSourceContent"  class ="com.atomikos.jdbc.AtomikosDataSourceBean"  init-method ="init"  destroy-method ="close" >       
         < property  name ="uniqueResourceName" >       
             < value > jdbc/dataSourceContent </ value >       
         </ property >       
         < property  name ="xaDataSourceClassName" >       
             < value > com.mysql.jdbc.jdbc2.optional.MysqlXADataSource </ value >       
         </ property >       
         < property  name ="xaProperties" >       
             < props >     
                 < prop  key ="serverName" > localhost </ prop >     
                 < prop  key ="portNumber" > 3306 </ prop >     
                 < prop  key ="databaseName" > MultiDatasourceExample </ prop >     
                 < prop  key ="user" > youxia </ prop >     
                 < prop  key ="password" >****** </ prop >     
             </ props >           
         </ property >           
         < property  name ="poolSize" >       
             < value > 3 </ value >       
         </ property >        
     </ bean >
     < bean  id ="dataSourceIndex"  class ="com.atomikos.jdbc.AtomikosDataSourceBean"  init-method ="init"  destroy-method ="close" >       
         < property  name ="uniqueResourceName" >       
             < value > jdbc/dataSourceIndex </ value >       
         </ property >       
         < property  name ="xaDataSourceClassName" >       
             < value > com.mysql.jdbc.jdbc2.optional.MysqlXADataSource </ value >       
         </ property >       
         < property  name ="xaProperties" >       
             < props >     
                 < prop  key ="serverName" > localhost </ prop >     
                 < prop  key ="portNumber" > 3306 </ prop >     
                 < prop  key ="databaseName" > MultiDatasourceExample </ prop >     
                 < prop  key ="user" > youxia </ prop >     
                 < prop  key ="password" >****** </ prop >     
             </ props >      
         </ property >            
         < property  name ="poolSize" >       
             < value > 3 </ value >       
         </ property >          
     </ bean >

<!--  Hibernate配置  -->
     < bean  id ="sessionFactoryContent"  class ="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" >
         < property  name ="dataSource"  ref ="dataSourceContent"   />
         < property  name ="namingStrategy" >
             < bean  class ="org.hibernate.cfg.ImprovedNamingStrategy"   />
         </ property >
         < property  name ="hibernateProperties" >
             < props >
                 < prop  key ="hibernate.dialect" > org.hibernate.dialect.MySQL5InnoDBDialect </ prop >
                 < prop  key ="hibernate.show_sql" > ${hibernate.show_sql} </ prop >
                 < prop  key ="hibernate.format_sql" > ${hibernate.format_sql} </ prop >
                 < prop  key ="hibernate.cache.provider_class" > org.hibernate.cache.EhCacheProvider
                 </ prop >
                 < prop  key ="hibernate.cache.provider_configuration_file_resource_path" > ${hibernate.ehcache_config_file} </ prop >
             </ props >
         </ property >
         < property  name ="packagesToScan"  value ="personal.youxia.entity.*"   />
     </ bean >
     < bean  id ="sessionFactoryIndex"  class ="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" >
         < property  name ="dataSource"  ref ="dataSourceIndex"   />
         < property  name ="namingStrategy" >
             < bean  class ="org.hibernate.cfg.ImprovedNamingStrategy"   />
         </ property >
         < property  name ="hibernateProperties" >
             < props >
                 < prop  key ="hibernate.dialect" > org.hibernate.dialect.MySQL5InnoDBDialect </ prop >
                 < prop  key ="hibernate.show_sql" > ${hibernate.show_sql} </ prop >
                 < prop  key ="hibernate.format_sql" > ${hibernate.format_sql} </ prop >
                 < prop  key ="hibernate.cache.provider_class" > org.hibernate.cache.EhCacheProvider
                 </ prop >
                 < prop  key ="hibernate.cache.provider_configuration_file_resource_path" > ${hibernate.ehcache_config_file} </ prop >
             </ props >
         </ property >
         < property  name ="packagesToScan"  value ="personal.youxia.entity.*"   />
     </ bean >
    
     <!--  事务管理器配置,多数据源JTA事务 -->
      < bean  id ="atomikosTransactionManager"  class ="com.atomikos.icatch.jta.UserTransactionManager"  init-method ="init"  destroy-method ="close" >    
         < property  name ="forceShutdown" >< value > true </ value ></ property >    
     </ bean >    
       
     < bean  id ="atomikosUserTransaction"  class ="com.atomikos.icatch.jta.UserTransactionImp" >    
         < property  name ="transactionTimeout"  value ="300" />     
     </ bean >    
     < bean  id ="transactionManager"  class ="org.springframework.transaction.jta.JtaTransactionManager" >
         < property  name ="transactionManager"  ref ="atomikosTransactionManager"   />
         < property  name ="userTransaction"  ref ="atomikosUserTransaction" />
     </ bean >

<!--  使用annotation定义事务  -->
     < tx:annotation-driven  transaction-manager ="transactionManager"   />
</ beans >

修改web.xml,配置双份的OpenSessionInViewFilter,如下: 1321

<? xml version="1.0" encoding="UTF-8" ?>
< web-app  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 > MultiDatasourceExample </ display-name >
     <!--  Spring ApplicationContext配置文件的路径,可使用通配符,多个路径用,号分隔
        此参数用于后面的Spring Context Loader  -->
     < context-param >
         < param-name > contextConfigLocation </ param-name >
         < param-value > classpath*:/applicationContext*.xml </ param-value >
     </ context-param >

     <!--  Character Encoding filter  -->
     < filter >
         < filter-name > encodingFilter </ filter-name >
         < filter-class > org.springframework.web.filter.CharacterEncodingFilter </ filter-class >
         < init-param >
             < param-name > encoding </ param-name >
             < param-value > UTF-8 </ param-value >
         </ init-param >
         < init-param >
             < param-name > forceEncoding </ param-name >
             < param-value > true </ param-value >
         </ init-param >
     </ filter >

     <!--  SpringSide's Hibernate Open Session In View filter -->
     < filter >
         < filter-name > hibernateOpenSessionInViewFilterContent </ filter-name >
         < filter-class > org.springside.modules.orm.hibernate.OpenSessionInViewFilter </ filter-class >
         < init-param >
             < param-name > excludeSuffixs </ param-name >
             < param-value > js,css,jpg,gif </ param-value >
         </ init-param >
         < init-param >       
                < param-name > sessionFactoryBeanName </ param-name >
             < param-value > sessionFactoryContent </ param-value >    
         </ init-param >     
     </ filter >
     < filter >
         < filter-name > hibernateOpenSessionInViewFilterIndex </ filter-name >
         < filter-class > org.springside.modules.orm.hibernate.OpenSessionInViewFilter </ filter-class >
         < init-param >
             < param-name > excludeSuffixs </ param-name >
             < param-value > js,css,jpg,gif </ param-value >
         </ init-param >
         < init-param >       
                < param-name > sessionFactoryBeanName </ param-name >
             < param-value > sessionFactoryIndex </ param-value >    
         </ init-param >     
     </ filter >

     <!--  SpringSecurity filter -->
     < filter >
         < filter-name > springSecurityFilterChain </ filter-name >
         < filter-class > org.springframework.web.filter.DelegatingFilterProxy </ filter-class >
     </ filter >

     <!--  Struts2 filter  -->
     < filter >
         < filter-name > struts2Filter </ filter-name >
         < filter-class > org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter </ filter-class >
     </ filter >

     < filter-mapping >
         < filter-name > encodingFilter </ filter-name >
         < url-pattern > /* </ url-pattern >
     </ filter-mapping >

     < filter-mapping >
         < filter-name > hibernateOpenSessionInViewFilterContent </ filter-name >
         < url-pattern > /* </ url-pattern >
     </ filter-mapping >
     < filter-mapping >
         < filter-name > hibernateOpenSessionInViewFilterIndex </ filter-name >
         < url-pattern > /* </ url-pattern >
     </ filter-mapping >

     < filter-mapping >
         < filter-name > springSecurityFilterChain </ filter-name >
         < url-pattern > /* </ url-pattern >
     </ filter-mapping >

     < filter-mapping >
         < filter-name > struts2Filter </ filter-name >
         < url-pattern > /* </ url-pattern >
     </ filter-mapping >

     <!-- Spring的ApplicationContext 载入  -->
     < listener >
         < listener-class > org.springframework.web.context.ContextLoaderListener </ listener-class >
     </ listener >

     <!--  Spring 刷新Introspector防止内存泄露  -->
     < listener >
         < listener-class > org.springframework.web.util.IntrospectorCleanupListener </ listener-class >
     </ listener >

     <!--  session超时定义,单位为分钟  -->
     < session-config >
         < session-timeout > 20 </ session-timeout >
     </ session-config >

     <!--  出错页面定义  -->
     < error-page >
         < exception-type > java.lang.Throwable </ exception-type >
         < location > /common/500.jsp </ location >
     </ error-page >
     < error-page >
         < error-code > 500 </ error-code >
         < location > /common/500.jsp </ location >
     </ error-page >
     < error-page >
         < error-code > 404 </ error-code >
         < location > /common/404.jsp </ location >
     </ error-page >
     < error-page >
         < error-code > 403 </ error-code >
         < location > /common/403.jsp </ location >
     </ error-page >
</ web-app >

在每一个Dao类里面使用@Resource注解指定使用哪一个SessionFactory。代码我就不列出来了,运行项目进行测试,成功。

到此,我们的征途圆满结束。但是SpringSide 3包含的特性远远不止这些,在showcase中,江南白衣不断在演示一些新的技术,这些技术如果用得恰当,会有效提高我们项目的开发速度和项目的运行效率,在以后的日子里,我会逐步为大家揭开这些神秘的面纱。

这里是该示例项目的源代码MultiDatasourceExample.rar ,欢迎大家点击下载,使用Eclipse 3.4导入后,即可以编辑和运行。 由于jar文件太占空间,这个源代码里面是不提供jar文件的,幸好我只给项目增加了mysql-connector.5.0.18.jar、 transaction-essential-all.jar两个和jta.properties一个,其余的都是标准的,大家可以从别的项目中拷贝过 来。在运行项目之前,大家一定要记得手动创建数据库,并修改配置文件里面的数据库名和密码。

转载于:https://www.cnblogs.com/cuker919/archive/2011/04/20/4878572.html

使用SpringSide 3.1.4.3开发Web项目的全过程(中下)相关推荐

  1. 使用Spring Boot开发Web项目

    前面两篇博客中我们简单介绍了spring Boot项目的创建.并且也带小伙伴们来DIY了一个Spring Boot自动配置功能,那么这些东西说到底最终还是要回归到Web上才能体现出它的更大的价值,so ...

  2. weblogic下开发web项目时修改java文件不用重启的绿色方法,不用修改weblogic的配置文件、不用jar...

    2019独角兽企业重金招聘Python工程师标准>>> weblogic下开发web项目时修改java文件不用重启的绿色方法,不用修改weblogic的配置文件.不用jar      ...

  3. html资源文件放在哪里,09 Spring Boot开发web项目之静态资源放哪里?

    Spring Boot开发web项目之静态资源放哪里? 先了解自动装配autoconfiguration 这些内容是spring boot天然集成好的框架 找到WebMvcAutoConfigrati ...

  4. Django 3.2 开发web项目

    Django 3.2 开发web项目 开发环境 Django安装 Django文档 Django创建 Django启动 Django目录介绍 Django创建自己的应用 CODE IS ALL YOU ...

  5. 使用eclipse开发web项目运行时出现中文乱码问题

    使用eclipse开发web项目运行时出现中文乱码问题 检查Tomcat sever.xml中的编码设置是否为 utf-8 ,不是的话改一下: <?xml version="1.0&q ...

  6. eclipse开发web项目

    对比idea开发web项目 ###1,使用eclipse开发web项目(jsp项目) 1,先将eclipse与tomcat关联一下 window->preferences->server- ...

  7. Eclipse开发Web项目入门篇

    前言:以前一直使用myeclipse 8 开发web,今天特意使用eclipse,所以在这小记一下,一来有需要的可以参考下,二来随手做个笔记备用 (转载请说明出处:http://blog.csdn.n ...

  8. ktor框架用到了netty吗_如何使用 Ktor 快速开发 Web 项目

    一. Ktor 介绍 Ktor 是一个高性能的.基于 Kotlin 的 Web 开发框架,支持 Kotlin Coroutines.DSL 等特性. Ktor 是一个由 Kotlin 团队打造的 We ...

  9. Bootstrap4+MySQL前后端综合实训-Day06-AM【eclipse详细配置Tomcat、开发web项目、servlet、连接MySQL8.0数据库、用户登录界面的编写与验证、分页查询】

    [Bootstrap4前端框架+MySQL数据库]前后端综合实训[10天课程 博客汇总表 详细笔记][附:实训所有代码] 目   录 eclipse重置视图 MySQL数据库--建数据库.建数据库 s ...

  10. Spring Boot 开发web 项目

    可参考博文: 搭建Spring Boot 项目 使用idea解决新建jsp文件而找不到jsp文件模版的新建选项 (一)快速搭建Web 项目 博主使用的是IDEA ,下面是项目目录结构:在用IDEA 创 ...

最新文章

  1. 更新ADT到Android L的方法
  2. 2019需要关注的几大AI趋势
  3. PB控制性能TreeView
  4. ASP.Net中省市级联有关
  5. Lesson 13.3 梯度不平稳性与Glorot条件
  6. 基于SpringBoot2.x、SpringCloud和SpringCloudAlibaba的企业级微服务敏捷开发系统架构
  7. Go内存管理之代码的逃逸分析
  8. DIV+CSS相对IE6、IE7和IE8的兼容问题
  9. 《嵌入式》复习资料公共版
  10. Ubuntu安装Beyond Compare(crack)
  11. 黑苹果 无线网卡相关配置
  12. java speex_JAVA版-微信语音.speex转.wav(示例代码)
  13. 不联网服务器系统时间,电脑时间不准确联网自动调整步骤
  14. win10计算机本地组策略编辑器,win10本地组策略编辑器找不到怎么办_win10电脑没有gpedit.msc的解决办法...
  15. 设置hr标签的粗细及颜色
  16. 马云给公司年轻员工的一封信
  17. 郁金香java_郁金香搜索引擎的方案
  18. 优化理论12---- Rosen的梯度投影法 、投影矩阵
  19. CD系列芯片功能大全
  20. Delphi 2007 体验

热门文章

  1. LINUX安装7Zip
  2. Python3使用PIL
  3. led led c语言程序设计,单片机C语言程序设计:8X8LED 点阵显示数字
  4. java sqlite管理系统_java-SQLite操作系统抽象层?
  5. mvc实例详解java_MVC模式在Java Web应用程序中的实例分析
  6. java 全局唯一id_JAVA生成全局唯一ID 使用 java.util.UUID
  7. 虚拟机 Ubuntu安装gcc和g++
  8. android最贵的手机,2019翻盖手机排行榜_2019年最贵的手机有哪些?最贵手机排行榜...
  9. java多肽跟重载_Java多态vs重写vs重载
  10. 生成发布包_制作R包指南