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对象。
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的业务逻辑组件接收容器的注入,将导致代码的可读性降低。
在这种策略下,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容器的自动装配,代码的可读性较差。