【Spring5.0】Spring整合Struts2

版权声明:转载或者引用本文内容请注明原作者名字及来源地址→ https://blog.csdn.net/weixin_43254907/article/details/88619895
  • 启动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容器的自动装配。代码的可读性较差。

猜你喜欢

转载自blog.csdn.net/weixin_43254907/article/details/88619895