-
启动Spring容器
-
创建Spring容器有如下两种方式:
- 直接在
web.xml
文件中配置创建Spring容器。 - 利用第三方MVC框架的扩展点,创建Spring容器。
第一种方式:借助
ServletContextListener
监听器,该监听器可以在Web应用启动时回调自定义方法来启动Spring容器。
Spring提供了一个ContextLoaderListener
,该监听器实现了ServletContextListener
接口,它会在创建时自动加载WEB-INF
下的applicationContext.xml
文件。<listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener>
如果有多个配置文件需要载入,
ContextLoaderListener
加载时,会查找名为contextConfigLocation
的初始化参数。<!-- 指定多个配置文件 --> <context-param> <!-- 参数名为contextConfigLocation --> <param-name>contextConfigLocation</param-name> <!-- 多个配置文件之间以“,”隔开 --> <param-value> /WEB-INF/daoContext.xml, /WEB-INF/applicationContext.xml classpath:applicationContext.xml </param-value> </context-param>
- 直接在
-
-
MVC框架与Spring整合的思考
工厂模式,或服务定位器模式用来解耦。控制器和业务逻辑组件的耦合度降低。在这种场景下业务逻辑组件已经在某个容器中运行,并对外提供某种服务。控制器无需理会该业务逻辑组件的创建,直接调用该服务即可,但在调用之前,必须先找到该服务——这就是服务定位器的概念。
如果系统采用Spring框架,则Spring称为最大的工厂。Spring负责业务逻辑组件的创建和生成,并可管理业务逻辑组件的生命周期。
现在的问题是:控制器如何访问到Spring容器中的业务逻辑组件呢?为了让Action访问到Spring的业务逻辑组件,有两种策略:- Spring容器负责管理控制器Action,并利用注入依赖为控制器注入业务逻辑组件。
- 利用Spring的自动装配,Action将会自动从Spring容器中自动获取所需要的业务逻辑组件。
-
让Spring管理控制器
- 可以充分利用Spring IoC特性,但需要将配置Struts2控制器部署在Spring容器中,因此导致配置文件冗余。
- Spring插件(
struts2-spring-plugin.jar
)提供了一种伪Action,该Action不再指定Action的实际实现类,而是指定为Spring容器中的Bean ID,这样Struts2不再自己负责创建Action实例,而是直接通过Spring容器去获取Action对象。这就实现了让核心控制器调用Spring容器中的Action来处理用户请求。但Spring插件创建Action实例时,并不是利用配置Action时指定的class属性来创建该Action实例,而是从Spring容器中取出对应的Bean实例完成创建的。 - 为了处理用户请求,需要提供用户请求的Action类,该Action需要调用业务逻辑组件的业务逻辑方法来处理用户请求。(Action只是控制器,处理用户请求应该由业务控制组件完成)
public class LoginAction extends ActionSupport { // 下面是用于封装用户请求参数的两个成员变量 private String username; private String password; // 系统所用的业务逻辑组件 private MyService ms; // 设值注入业务逻辑组件所必须的setter方法 public void setMs(MyService ms) { this.ms = ms; } // 省略username 和password的setter,getter方法 ... public String execute() throws Exception { // 调用业务逻辑组件的validLogin()方法 // 验证用户输入的用户名和密码是否正确 if(ms.validLogin(getUsername(), getPassword()) > 0) { addActionMessage("哈哈,整合成功!"); return SUCCESS; } return ERROR; } }
在系统中配置该控制器,本应用中的配置文件(struts.xml)如下:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <!-- 配置常量 --> <constant name="struts.i18n.encoding" value="UTF-8" /> <constant name="struts.devMode" value="true" /> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <package name="default" extends="struts-default" namespace="/"> <!-- class属性是从Spring IoC获取的 --> <action name="login" class="loginAction"> <result name="error">/WEB-INF/content/error.jsp</result> <result name="success">/WEB-INF/content/welcome.jsp</result> </action> <!-- 让用户直接访问该应用时列出所有的视图页面 --> <action name="*"> <result>/WEB-INF/content/{1}.jsp</result> </action> </package> </struts>
MyService接口
public interface MyService { int validLogin(String username, String password); }
业务组件的实现类MyServiceImpl
public class MyServiceImpl implements MyService { @Override public int validLogin(String username, String password) { if (username.equals("admin") && password.equals("123456")) { return 99; } return -1; } }
下面将该业务逻辑组件部署在Spring容器中,配置该业务逻辑组件的配置片段如下:
<!-- 定义一个业务逻辑组件,实现类为MyServiceImpl <bean id="myService" class="com.chromer.service.MyServiceImpl"></bean>
除此之外还应该在Spring容器中配置前面的Action实例,并将该业务逻辑组件注入到Action实例中。
<!-- 定义一个业务逻辑组件,实现类为MyServiceImpl --> <bean id="myService" class="com.chromer.service.MyServiceImpl" /> <!-- 让Spring管理Action实例,并依赖注入业务逻辑组件 --> <bean id="loginAction" class="com.chromer.action.LoginAction" scope="prototype" p:ms-ref="myService" />
当使用Spring容器管理Struts2的Action时,由于每个Action对应一次用户请求,且封装了该次请求的状态信息,所以不应该将Action配置成单例模式,因此需要指定scope属性,该属性值可指定为prototype或request。
这种策略充分利用了Spring的IoC特性,是一种较为优秀的解耦策略。这种策略也有一些不足之处,鬼打起来,主要有以下不足之处。
- Spring管理Action,必须将所有的Action配置在Spring容器中, 而struts.xml文件中还需要配置一个“伪Action”,从而导致配置文件臃肿、冗余。 - Action的业务逻辑组件接收容器注入,将导致代码的可读性降低。
-
使用自动装配
-
所谓自动装配,即让Spring自动管理Bean与Bean之间的依赖关系,无须使用
ref
显式指定依赖Bean。Spring容器会自动检查XML配置文件的内容,为主调Bean注入依赖Bean。 -
通过设置
struts.objectFactory.spring.autoWire
常量可以改变Spring插件的自动装配策略:name
:使用byName自动装配type
:使用byType自动装配auto
:自动检测需要哪种自动装配策略constructor
:和type
类似,但是不是使用设值注入,而是使用构造注入。
此时struts.xml配置文件还和以前配置Action一样:
<!-- 定义处理用户请求的Action,该Action的class属性不是实际处理类, 而是Spring容器中的Bean实例的ID --> <action name="login" class="com.chromer.action.LoginAction"> <result name="error">/WEB-INF/content/error.jsp</result> <result name="success">/WEB-INF/content/welcome.jsp</result> </action>
查看刚才的Action类代码,发现了如下的方法定义:
// 系统所用的业务逻辑组件 private MyService ms; // 设置注入业务逻辑组件所必须的setter方法 public void setMs(MyService ms) { this.ms = ms; }
通过上面的
setter
方法,可以看出该Action所需的业务逻辑组件的id
必须为ms
,因此配置业务逻辑组件时,必须指定其id
属性为ms
。<bean id="ms" class="com.chromer.service.MyServiceImpl" />
-
在这种整合策略下,Spring插件负责为Action自动装配业务逻辑组件,从而可以简化配置文件的配置。这种方式也存在如下两个缺点:
- Action与业务逻辑组件的耦合降低到代码层次,必须在配置文件中配置与Action所需控制器同
名的业务逻辑组件。这不利于高层次解耦。
- Action接收Spring容器的自动装配。代码的可读性较差。