SSH整合之配置文件篇
为web应用提供Struts2支持
1.编辑web应用的web.xml配置文件,配置Struts2的核心Filter来拦截用户请求。
由于Web应用是基于请求/响应架构的应用,所以不管哪个MVC Web框架,都需要在web.xml中配置该框架的核心Servlet或Filter,这样才可以让该框架介入Web应用中。
<!--定义struts2的核心Filter(过滤器)--> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <!--让Struts2的核心Filter拦截所有的请求--> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
2.如果需要以POST方式提交请求,则定义包含表单数据的JSP页面。如果仅仅只是以GET方式发送请求,则无须经过这一步。
3.定义处理用户请求的Action。
这一步也是所有MVC框架中必不可少的,因为这个Action就是MVC中的C,也就是控制器。该控制器负责调用Model里的方法来处理请求。
MVC框架的底层机制是:核心Servlet或Filter接收到用户请求后,通常会对用户请求进行简单的预处理,例如解析、封装参数等,然后通过反射来创建Action实例,并调用Action的指定方法。当Servlet或Filter拦截用户请求后,通过配置文件或利用约定来确定创建哪个Action的实例。
在MVC框架中,控制器实际上由2个部分共同组成,即拦截所有用户请求,处理请求的通用代码都由核心控制器完成,而实际的业务控制器则由Action处理。
4.配置Action
配置Action就是指定哪个请求对应用哪个Action进行处理,从而让核心控制器根据该配置来创建合适的Action实例。配置文件(struts.xml)片段为
<action name="login" class="com.PM.action.LoginAction"> ...
</action>
5.配置处理结果和物理视图之间的对应关系
当Action处理用户请求结束后,通常会返回一个处理结果(通常使用简单的字符串就可以了),我们可以认为该名称是逻辑视图名,这个逻辑视图名要和指定物理视图资源关联才有价值。所以我们还需要配置处理结果之间的对应关系。配置文件片段为:
<action name="login" class="com.PM.action.LoginAction"> <!--定义了3个逻辑视图和物理视图资源之间的映射--> <result name="input">/login.jsp</result> <result name="error">/error.jsp</result> <result name="success">/welcome.jsp</result> </action>
6.编写视图资源
为web应用提供Spring支持
使用Spring的Web应用,无须手动创建Spring容器,而是通过配置文件,声明式地创建Spring容器,直接在web.xml文件中配置创建Spring容器。为了让Spring容器随着Web应用的启动而启动,借助于ServletContextListener监听器即可完成,该监听器可以在Web应用启动时回调自定义方法—该方法可以启动Spring容器。
Spring提供了一个ContextLoaderListener,该监听器类实现了ServletContextListener接口。该类可以作为Listener使用,它会在创建时自动查找WEB-INF/下的applicationContext.xml文件,因此,如果只有一个配置文件,并且文件名为applicationContext.xml,则只需在web.xml文件中增加如下配置片段即可。
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
如果有多个配置文件需要载入,则考虑使用<context-param.../>元素来确定配置文件的文件名。ContextLoaderListener加载时,会查找contextConfigLocation的初始化参数。因此,配置<context-param.../>时应指定参数名为contextConfigLocation,配置文件web.xml增加如下配置片段。
<!-- 指定多个配置文件 --> <context-param> <!--参数名为contextConfigLocation --> <param-name>contextConfigLocation</context-param> <!-- 多个配置文件之间以“,”隔开 –> <param-value>/WEB-INF/daoContext.xml,/WEB-INF/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
如果没有使用 contextConfigLocation指定配置文件,则Spring自动查找applicationContext.xml配置文件;如果有contextConfigLocation,则利用该参数确定的配置文件。如果无法找到合适的配置文件,Spring将无法正常初始化。
Spring根据配置文件创建WebApplicationContext对象,并将其保存在Web应用的ServletContext中。大部分情况下,应用中的Bean无需感受到ApplicationContext的存在,只要利用ApplicationContext的IoC即可。
Hibernate的使用
Hibernatede 配置信息(如连接哪个数据库,以及连接数据库时所用的连接池、用户名、密码等详细信息)使用配置文件指定(hibernate.cfg.xml)。配置文件如下
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property> <property name="connection.ul">jdbc:sqlserver://localhost:1433;DatabaseName=PM</property> <property name="connection.username">sa</property> <property name="connection.password"></property> <!--指定连接池最大连接数--> <property name="hibernate.c3p0.max_size">40</property> <!--指定连接池最小连接数--> <property name="hibernate.c3p0.min_size">1</property> <!--指定数据库方言--> <property name="dialect">org.hibernate.dialect.SQLServerDialect</property> <!--根据需要自动创建数据表--> <property name="hbm2ddl.auto">update</property> <!--罗列所有的映射文件--> <mapping resource="com/PM/domain/News.hbm.xml"/> </session-factory> </hibernate-configuration>
Hibernate配置文件的默认文件名为hibernate.cfg.xml,当程序调用Configuration对象的configure()方法时,Hibernate将自动加载该文件。
完成保存Member的代码如下,如代码所示程序需要手动获取SessionFactory实例。
public class MemberManager { public void main(String[] args){ //实例化Configuration Configuration conf=new Configuration() //下面的方法默认加载hibernate.cfg.xml文件 .configure(); //以Configuration创建SessionFactory SessionFactory sf=conf.buildSessionFactory(); //创建Session Session sess=sf.openSession(); //开始事务 Transaction tx=sess.beginTransaction(); Member m=new Member(); m.setPassword("123"); //保存 sess.save(m); //提交事务 tx.commit(); //关闭Session sess.close(); sf.close(); } }
为了使用Hibernate进行持久化操作,通常有如下操作步骤:
- 开发持久化类,由POJO加映射文件组成。
- 获取Configuration。
- 获取SessionFactory。
- 获取Session,打开事务。
- 用面向对象的方式操作数据库。
- 关闭事务,关闭Session。
Spring整合Struts
1.MVC框架与Spring整合的思考
控制器应该如何获得业务逻辑组件?最容易想到的策略是,直接通过new关键字创建业务逻辑组件,然后调用业务逻辑组件的方法,根据业务逻辑方法的返回值确定结果。但是这是一种非常差的策略,因为
- 控制器直接创建业务逻辑组件,导致控制器和业务逻辑组件的耦合降低到代码层次,不利于高层次解耦。(解耦)
- 控制器不应该负责业务逻辑组件的创建,控制器只是业务逻辑组件的使用者,无须关系业务逻辑组件的实现。(职责明确)
- 每次创建新的业务逻辑组件导致性能下降。(性能)
答案是采用工厂模式,或者服务定位器模式。经典Java EE应用就是使用的服务定位器模式。对于轻量级的Java EE应用,工厂模式是更实际的策略。工厂可以保证该组件的实例只需一个就够了,可以避免重复实例化造成的系统开销。采用工厂模式,将控制器与业务逻辑组件的实现分离,从而提供更好的解耦。在采用工厂模式的访问策略中,所有的业务逻辑组件的创建由工厂负责,业务逻辑组件的运行也由工厂负责。而控制器只需定位工厂实例即可。
如果系统采用Spring框架,则Spring成为最大的工厂。Spring负责业务逻辑组件的创建和生成,并可管理业务逻辑组件的生命周期。可以如此理解:Spring是个性能非常优秀的工厂,可以生产出所有的实例,从业务逻辑组件,到持久层组件,甚至控制器组件。
为了Action访问Spring的业务逻辑组件,有两种策略:
- Spring容器负责管理控制器Action,并利用依赖注入为控制器注入业务逻辑组件。
- 利用Spring的自动装配,Action将会自动从Spring容器中获取所需的业务逻辑组件。
2.策略一:让Spring管理控制器
让Spring容器管理应用中的控制器,可以充分利用Spring的IoC特性,但需配置Struts2的控制器部署在Spring容器中,因此导致配置文件冗余。
我们把Action实例交由Spring容器来管理,而不是由Struts2产生的。那么,核心控制器如何知道调用Spring容器中的Action,而不是自行创建Action实例呢?这个工作由Struts2提供的Spring插件完成。struts2-spring-plugin-xxx.jar文件,这种JAR包就是Struts2整合Spring的插件,简称Spring插件,将这个JAR包复制到Web应用的WEB-INF/lib目录下。
Spring插件提供了一种伪Action,当我们在struts.xml文件中配置Action时,通常需要指定class属性,该属性就是用于创建Action实例的实现类。但Spring插件允许我们指定class属性时,不再指定Action的实际实现类,而是指定Spring容器中的BeanID,这样Struts2不再自己负责创建Action实例,而是直接通过Spring容器去获取Action对象。在这种整合策略下,处理用户请求的Action由Spring插件负责创建,但Spring插件创建Action实例时,并不是利用配置Action指定的class属性来创建该Action实例,而是从Spring容器中取出对应的Bean实例完成创建。
Spring和Struts2整合的关键所在是Spring容器为控制器注入业务逻辑组件。
配置文件struts.xml的相应片段如下:
<!--定义处理用户请求的Action,该Action的class属性不是实际处理类, 而是Spring容器中的Bean实例--> <action name="loginPro" class="loginAction"> <result name="error">/error.jsp</result> <result name="success">/welcome.jsp</result> </action>
配置文件applicationContext.xml的相应片段如下:
<!--定义一个业务逻辑组件,实现类为MyServiceImpl --> <bean id="myService" class="com.PM.service.impl.MyServiceImpl"/> <!--让Spring容器管理Action实例--> <bean id="loginAciton" class="com.PM.action.LoginAction" scope="prototype"> <!--依赖注入业务逻辑组件--> <property name="ms" ref="myService"/> </bean>
当Spring管理Struts2的Action时,一定要配置scope属性,因为Action里包含了请求的状态信息,所以必须为每个请求对应一个Action,所以不能将Action实例配置成单例模式。
这种策略充分利用了Spring的IoC特性,是一种较为优秀的解耦策略,这种策略也有一些不足之处。
- Spring管理Action,必须将所有的Action配置在Spring容器中,而struts.xml文件中还需要配置一个“伪Action”,从而导致配置文件冗余,臃肿。
- Action的业务逻辑组件接收容器的注入,将导致代码的可读性降低。
3.策略二:使用自动装配
在这种策略下,Action还是由Spring插件创建,Spring插件在创建Action实例时,利用Spring的自动装配策略,将对应的业务逻辑组件注入Action实例。这种整合策略配置文件简单,但控制器和业务逻辑组件耦合又提升到了代码层次,耦合较高。
所谓自动装配,即让Spring自动管理Bean与Bean之间的依赖关系,无须使用ref显示指定依赖Bean。Spring容器会自动检查XML配置文件内容,为主调Bean注入依赖Bean。自动装配可以减少配置文件的工作量,但会降低依赖关系的透明性和清晰性。此处的自动装配策略与Spring自身所提供的自动装配完全相同。
在这种装配策略下,我们还采用传统的方式来配置Struts2的Action,配置Action时一样指定其具体的实现类,配置文件struts.xml片段如下。因为使用了自动装配,Spring插件创建Action实例时,是根据Action的class属性指定实现类来创建Action实例的。
<!--定义处理用户请求的Action--> <action name="loginPro" class="com.PM.action.LoginAction"> <result name="error">/error.jsp</result> <result name="success">/welcome.jsp</result> </action>
此时Struts2的配置文件里配置的依然是Action的实现类,该配置文件与不整合Spring时的配置文件没有任何区别。整合Spring框架与不整合时当然存在区别,只是这个区别不是在配置文件中体现,而是在创建该Action实例时体现出来的。如果不整合Spring框架,则Struts2框架负责创建Action实例, 创建成功后就结束;如果整合Spring框架,则当Action实例创建完成后,Spring插件还会负责将该Action所需的业务逻辑组件注入给该Action实例。Action类代码片段如下:
//系统所有的业务逻辑组件 private MyService ms; //设置注入业务逻辑组件所必需的setter方法 public void setMs(MyService ms){ this.ms=ms; }
通过上面的setter方法,可以看出该Action所需的业务逻辑组件名为ms,因此我们必需在配置业务逻辑组件时,指定其id属性为ms。配置文件applicationContext.xml片段为:
<!--- 定义一个业务逻辑组件,实现类为MyServiceImpl--> <bean id="ms" class="com.PM.service.impl.MyServiceImpl"/>
因为在配置业务逻辑组件时,指定了该业务逻辑组件的id为ms,则Spring插件可以在创建Action实例时将该业务逻辑组件注入给Action实例。
在这种整合策略下,Spring插件负责为Action自动装配业务逻辑组件,从而可以简化配置文件的配置。这种方式也存在如下两个缺点。
- Action与业务逻辑组件的耦合降低到了代码层次,必须在配置文件中配置Action所需业务逻辑组件同名的业务逻辑组件,不利于高层次解耦。
- Action接受Spring容器的自动装配,代码的可读性较差。
Spring整合Hibernate
1.管理Hibernate的SessionFactory
当通过Hibernate进行持久层访问时,必须先获得SessionFactory对象,它是单个数据库映射关系编译后的内存镜像。大部分情况下,一个Java EE应用对应一个数据库,即对应一个SessionFactory对象。纯粹的Hibernate访问中,应用程序需要手动创建SessionFactory实例,在实际开发中,我们希望以一种声明式的方式管理SessionFactory实例,直接以配置文件来管理SessionFactory实例。Spring的IoC容器正好提供了这种管理方式,它不仅能以声明式的方式配置SessionFactory实例,也可以充分利用IoC容器的作用,为SessionFactory注入数据源引用。
<!-- 定义数据源Bean 使用c3p0数据源实现--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <!--指定连接数据库的驱动--> <property name="driverClass" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/> <property name="jdbcUrl" value="jdbc:sqlserver://localhost:1433;DatabaseName=PM;SelectMethod=cursor"/> <property name="user" value="sa"/> <property name="password" value=""/> <property name="maxPoolSize" value="40"/> <property name="minPoolSize" value="1"/> <!--指定连接数据库连接池的初始化连接数--> <property name="initialPoolSize" value="1"/> <!--指定连接数据库连接池的连接的最大空闲时间--> <property name="maxIdleTime" value="20"/> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!--用来列出所有的PO映射文件 --> <property name="mappingResources"> <list> <value>com/PM/domain/Admin.hbm.xml</value> <value>com/PM/domain/Member.hbm.xml</value> <value>com/PM/domain/Department.hbm.xml</value> </list> </property> <!--设置Hibernate属性 --> <property name="hibernateProperties"> <props> <!--配置连接数据库的方言--> <prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop> <!--设置当创建SessionFactory时,是否根据映射文件自动建立数据库表—> <prop key="hibernate.hbm2ddl.auto">create</prop> <!--是否将SQL语句转化成格式良好的SQL –> <prop key="hibernate.format_sql">true</prop> </props> </property> </bean>
一旦在Spring的IoC容器中配置了SessionFactory Bean,它将随应用的启动而加载,并可以充分利用IoC容器的功能,将SessionFactory Bean注入任何Bean,比如DAO组件,一旦DAO组件获得了SessionFactory Bean的引用,就可以完成时间的数据库访问。
当以声明式的方式来管理SessionFactory时,可以让应用在不同的数据源之间切换。如果应用需要更换数据库等持久层资源,只需对配置文件进行简单修改即可。声明式的方式管理SessionFactory,是为了提供更好的适应性:当持久层服务需要更改时,程序代码无需任何改变。
2.使用声明式事务
Spring的事务机制非常优秀,它允许我们在开发过程中无须理会任何事务逻辑,等到应用开发完成后使用声明式事务来进行统一的事务管理。只需要在配置文件中增加事务控制的片段,业务逻辑组件的方法将会具有事务性;而且Spring的声明式事务支持在不同事务策略之间自由切换。
配置Spring声明式事务时,通常推荐使用BeanNameAutoProxyCreator自动创建事务代理。通过这种自动事务代理的配置策略,增加业务逻辑组件,只需在BeanNameAutoProxyCreator Bean配置中增加一行即可,从而避免了增量式配置。
为业务逻辑组件添加事务执行如下几个步骤。
- 针对不同的事务策略配置对应的事务管理器。
- 使用<tx:advice.../>元素配置事务增强处理Bean,配置事务增强处理Bean时使用多个<method.../>元素为不同的方法指定相应的事务语义。
- 在<aop:config.../>元素中使用<aop:advisor>元素配置自动事务代理。
只需在Spring配置文件中增加如下配置片段,业务逻辑组件的方法将会具有事务性。
<!-- 配置Hibernate的局部事务管理器,使用HibernateTransactionManager类--> <!-- 该类实现PlatformTransactionManager接口,是针对Hibernate的特定实现类--> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"/> </bean> <!--配置事务增强处理Bean,指定事务管理器--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!--用于配置详细的事务语义--> <tx:attributes> <!-- 所有以'get'开头的方法是read-only--> <tx:method name="get*" read-only="true"/> <!-- 其他方法使用默认的事务设置--> <tx:method name="*"/> </tx:attributes> </tx:advice> <aop:config> <!--配置一个切入点 "*"匹配符表示一个任意类型的参数 "."匹配符表示零个或多个任意类型的参数--> <aop:pointcut id="servicePointcut" expression="execution(* com.PM.service.impl.*.*(..))"/>
<!--指定在servicePointcut切入点应用txAdvice事务增强处理--> <aop:advisor advice-ref="txAdvice" pointcut-ref="servicePointcut"/> </aop:config>
尽量使用声明式事务配置方式,而不要在代码中完成事务逻辑。