Struts:Spring整合Struts2&零配置

1,Spring整合Struts2

虽然Spring也提供了自己的MVC组件,但一来Spring的MVC组件略显烦琐;二来Struts2的拥护者实在太多。因此,很多项目都会选择使用Spring整合Struts2框架。而且Spring完全可以无缝衔接整合Struts2框架,二者结合成一个更实际的Java EE开发平台。

jar:

1.1,启动Spring容器

对于使用Spring的Web应用,无须手动创建Spring容器,而是通过配置文件声明式地创建Spring容器。Spring提供了一个ContextLoaderListener,该监听类实现了ServletContextListener接口。该类可以作为Listener使用,它会在创建时自动查找WEB-INF/下的applicationContext.xml文件,因此如果只有一个配置文件且配置文件命名为applicationContext.xml,则只需在web.xml文件中增加如下配置片段:

<!-- 使用ContextLoaderListener初始化Spring容器 -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

如果有多个配置文件需要载入,则考虑使用<context-param.../>元素确定配置文件的文件名。,COntextLoaderListener加载时,会查找名为contextConfigLocation的初始化参数,因此配置<context-param.../>时应指定参数名为contextConfigLocation。

<context-param>
    <param-name> contectCOnfigLocation</param-name>
    <param-value>/WEB-INF/daocontext.xml,/WEB-INF/applicationCotext.xml</param-value>
</context-param>

Spring根据配置文件创建WebApplicationContext对象,并将其保存在Web应用的ServletContext中。如果要获取应用中的ApplicationContext实例,则可以根据如下获取:

WebApplicationContext ctx=WebApplicationContextUtils.getWebApplicationContext(servletContext)

1.2,MVC框架与Spring整合的思考

对于一个基于B/S架构的Java EE而言,用户请求总是向MVC框架的控制器请求,而当控制器拦截到用户请求后,必须调用业务逻辑组件来处理用户请求。此时有个问题:控制器应该如何获得业务逻辑组件?

最容易的策略是,直接通过new关键字创建业务逻辑组件,然后调用业务逻辑组件的方法,根据业务逻辑方法的返回值确定结果。

在实际的应用中,很少采用上面的访问策略,因为这是一种非常差的策略。不这样做的原因至少有三点:

  • 控制器直接创建业务逻辑组件,导致控制器和业务逻辑组件的耦合降低到代码层次,不利于高层次耦合。
  • 控制器不应该负责业务逻辑组件的创建,控制器只是业务逻辑组件的使用者,无须关心业务逻辑组件的实现。
  • 每次创建新的业务逻辑组件将导致性能下降。

答案是采用工厂模式,或者服务定位模式。对于采用服务定位器模式,是远程访问的场景。在这种场景下,业务逻辑组件已经在某个容器中运行,并对外提供某种服务。控制器无须理会该业务逻辑组件的创建,直接调用该服务即可,但在调用之前,必须先找到该服务——这就是服务定位器的概念。经典的Java EE应用就是这种结构的应用。

对于轻量级的Java EE应用,工厂模式则是更实际的策略。因为在轻量级的Java EE应用中,业务逻辑组件不是EJB,通常就是一个POJO,业务逻辑组件的生成通常应由工厂负责,而工厂可以保证该组件的实例只需一个就够了,可以避免重复实例化造成的系统开销。

采用工厂模式,将控制器与业务逻辑组件的实现分离,从而提供更好的解耦。在采用工厂模式访问的访问策略中,所有的业务逻辑组件的创建由工厂负责,业务逻辑组件的运行也由工厂负责,而控制器只需定位工厂实例即可。

如果系统采用Spring框架,则Spring成为最大的工厂。Spring负责业务逻辑组件的创建和生成,并可管理业务逻辑组件的生命周期。Spring是个性能非常优秀的工厂,可以生产出所有的实例,从业务逻辑组件,到持久层组件,甚至控制器组件。

现在的问题是:控制器如何访问到Spring容器中的业务逻辑呢?为了让Action访问到Spring的业务逻辑组件,有两种策略:

  • Spring容器负责管理控制器Action,并利用依赖注入为控制器注入业务逻辑组件。
  • 利用Spring的自动装配,Action将会自动从Spring容器中获取所需的业务逻辑组件。

1.3,让Spring管理控制器

让Spring容器来管理应用中的控制器,可以充分利用Spring的IoC特性,但需要将配置Struts2的控制器部署在Spring容器中,因此导致配置文件冗余。

Struts2的核心控制器首先拦截到用户请求,将请求转发给对应的Action处理,在此过程中,Struts2将负责创建Action实例,并调用其execute()方法。这个过程是固定的。现在的情形是:如果把Action实例交由Spring容器来管理,而不是Struts2产生,那么核心控制器如何知道调用Spring容器中的Action,而不是自行创建Action实例呢?这个工作由Struts2提供的Spring插件来完成:struts2-spring-2.5.26

Spring插件提供了一种伪Action,在struts.xml文件中配置Action时,通常需要指定其class属性,该属性就是用于创建Action实例的实现类。但Struts2提供的Spring插件允许指定class属性时,不再指定Action的实际实现类,而是指定为Spring容器中的Bean ID,这样Struts2不再自己负责创建Action实例,而是直接通过Spring容器去获取Action对象。

通过上面的方式,不难发现这种整合策略的关键:当Struts2将请求转发给指定的Action时,Struts2中的该Action只是一个傀儡,它只是一个代号,并没有实际的实现类,当然也不可能创建Action实例,而是隐藏在该Action下的是Spring容器中的Action实例。

1,使用ContextLoaderListener初始化Spring容器

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping> 	 	<!-- spring配置文件对应的参数,如果applicationContext.xml放在/WEB-INF/目录下,可以忽略;若不是,则需要配置 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>WEB-INF/applicationContext.xml</param-value>
    </context-param>
</web-app>

2,在Spring的配置文件中配置 Struts2 的 Action 实例

当使用Spring容器管理Struts2的Action时,由于每个Action对应一次用户请求,且封装了该次请求的状态信息,所以不应将Action配置成单例模式,因此必须指定scope属性,该属性值可指定为prototype或request。

<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="registerAction" class="action.LoginAction" scope="prototype" p:myService-ref="myService"/>
    <bean id="myService" class="service.MyService" />
</beans>

 3,在 Struts 配置文件中配置 action, 但其 class 属性不再指向该 Action 的实现类, 而是指向 Spring 容器中 Action 实例的 ID

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
        "http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
    <package name="myFirstPackage" extends="struts-default">
        <action name="register" class="registerAction">
            <result name="error">/WEB-INF/error.jsp</result>
            <result name="success">/WEB-INF/welcome.jsp</result>
        </action>
    </package>
</struts>

1.4,使用自动装配

自动装配:https://shao12138.blog.csdn.net/article/details/113058359#t6

在自动装配策略下,Action还是由Spring插件创建,Spring插件在创建Action实例时,利用Spring的自动装配策略,将对应的业务逻辑组件注入Action实例中。这种整合策略配置文件简单,但控制器和业务逻辑组件耦合又提升了代码层次,耦合较高。

如果不指定自动装配,则系统默认使用按byName自动装配。前面的整合策略并没有指定任何自动装配策略,当Web启动时,可以看到自动装配策略为byName。

自动装配:即让Spring自动管理Bean与Bean之间的依赖关系,无须使用ref显式指定依赖Bean。Spring容器会自动检查XML配置文件的内容,为主调Bean注入依赖Bean。自动装配可以减少配置文件的工作量,但会降低依赖关系的透明性和清晰性。

通过自动装配,可以让Spring插件自动将业务逻辑组件注入Struts2的Action实例中。通过设置struts.dobjectFactory.spring.autoWire常量可以改变Spring插件的自动装配策略,该常量有如下几个值:

  • name:根据属性名自动装配。Spring插件会查找容器中全部Bean,找到其中id属性与Action所需的业务逻辑组件同名的Bean,将该bean实例注入到Action实例。
  • type:根据属性类型自动装配。Spring插件会查找容器中全部Bean,找出其类型恰好与Action所需的业务逻辑组件相同的Bean,将该Bean实例注入到Action实例。
  • auto:Spring插件会自动检测需要使用哪种自动装配方式。
  • constructor:与type类似,区别是constructor使用构造器来构造注入的所需参数而不是使用设值注入方式。

1,使用ContextLoaderListener初始化Spring容器

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping> 	 	<!-- spring配置文件对应的参数,如果applicationContext.xml放在/WEB-INF/目录下,可以忽略;若不是,则需要配置 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>WEB-INF/applicationContext.xml</param-value>
    </context-param>
</web-app>

2,正常编写struts配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
        "http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
    <package name="myFirstPackage" extends="struts-default">
        <action name="register" class="action.LoginAction">
            <result name="error">/WEB-INF/error.jsp</result>
            <result name="success">/WEB-INF/welcome.jsp</result>
        </action>
    </package>
</struts>

3,编写 spring 配置文件, 在该配置文件中不需要配置 Action 实例

此时Bean的ID必须为Struts中MyService的实例名。

<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="myService" class="service.MyService" />
</beans>

2,零配置

2.1,Struts2零配置

零配置省去了在struts.xml的配置,但也无法使用通配符,也无法配置Struts2的常量。

拦截器必须在struts,xml中配置,但是可以以注解的形式添加到action中。

1、添加jar

2、添加注解

package action;

import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.convention.annotation.*;
@ParentPackage("struts-default")    //package名称
@Namespace("/")    //命名空间
@InterceptorRef("simple")   //拦截器
public class LoginAction extends ActionSupport {
    private String username;
    private String password;

    // username的setter和getter方法
    public void setUsername(String username) {
        this.username = username;
    }

    public String getUsername() {
        return username;
    }

    // password的setter和getter方法
    public void setPassword(String password) {
        this.password = password;
    }

    public String getPassword() {
        return password;
    }
    @Action(value = "login", results = {  //相当于配置中的name
            @Result(name = "success", location = "/success.jsp"),
            @Result(name = "error", location = "/error.jsp") })	//执行函数名称,结果,跳转路径
    public String execute() throws Exception {
        System.out.println("进入execute方法执行体..........");
        Thread.sleep(1000);
        if (getUsername().equals("123456") && getPassword().equals("123456")) {
            return SUCCESS;
        }
        return ERROR;
    }
}

2.2,配置包

【问题】Struts2默认扫描action、actions、struts、struts2这4个包的注解。意思就是如果我的包名不是这4个,就无法使用注解达到零配置了。会出现:Action访问找不到namespace问题。

【解决】需要手动配置扩充包名称,来覆盖默认配置。struts.xml配置文件加一个配置:

<constant name="struts.convention.package.locators" value="action,actions,struts,struts2,test" />

2.3,Spring整合Struts零配置

Spring零配置:https://shao12138.blog.csdn.net/article/details/113327063#t6

1,Action

package action;

import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.convention.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import service.MyService;

@Controller
@ParentPackage("struts-default")
@Namespace("/")
public class LoginAction extends ActionSupport {
    private String username;
    private String password;
    @Autowired
    private MyService myService;
    public void setMyService(MyService myService) {
        this.myService = myService;
    }

    // username的setter和getter方法
    public void setUsername(String username) {
        this.username = username;
    }

    public String getUsername() {
        return username;
    }

    // password的setter和getter方法
    public void setPassword(String password) {
        this.password = password;
    }

    public String getPassword() {
        return password;
    }
    @Action(value = "register", results = { @Result(name = "success", location = "/success.jsp"),
            @Result(name = "error", location = "/error.jsp") })
    public String execute() throws Exception {
        System.out.println("进入execute方法执行体..........");
        if (myService.validLogin(username,password)) {
            return SUCCESS;
        }
        return ERROR;
    }
}

2,Service

package service;

import org.springframework.stereotype.Service;

@Service
public class MyService {
    public boolean validLogin(String username, String password) {
        if (username.equals("123456")&&password.equals("123456")){
            return true;
        }else {
            return false;
        }
    }
}

3,applicationContext.xml

<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="action"/>
    <context:component-scan base-package="service"/>
</beans>

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值