本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:SSH整合是Java Web开发中的经典技术栈,涵盖Spring、Struts和Hibernate三大框架。本项目“SSH-struts第一种方式整合”详细讲解如何通过Struts实现MVC架构,利用Spring进行依赖注入与事务管理,并通过Hibernate完成数据库操作。项目包含完整的配置流程、前后端交互实现以及整合步骤,适合提升Java开发者在企业级应用开发中的实战能力。
SSH-struts

1. SSH框架整合概述

SSH(Struts + Spring + Hibernate)作为Java Web开发的经典三层架构整合方案,凭借其清晰的职责划分和良好的解耦设计,广泛应用于企业级应用开发中。其中,Struts负责表现层的请求处理与页面跳转,Spring管理业务逻辑层的依赖注入与事务控制,Hibernate则实现数据持久化操作。三者协同工作,提升了系统的可维护性与可扩展性。

相较于传统的Servlet + JSP + DAO开发模式,SSH整合在代码结构、事务控制、对象关系映射等方面具有显著优势。通过Spring的IoC容器,各层组件之间实现松耦合;Hibernate简化了数据库访问逻辑;而Struts则有效管理了Web层的交互流程。整合过程中常见的问题包括配置冲突、Bean注入失败、事务未生效等,这些问题将在后续章节中结合实践逐一解析。

2. Struts框架配置与使用

Struts作为SSH整合中的表现层框架,负责处理用户的请求和响应。它基于MVC(Model-View-Controller)架构模式,将用户请求、业务逻辑和视图展示进行分离,提高系统的可维护性和可扩展性。本章将从Struts的核心组件入手,逐步介绍其配置与使用流程,并通过代码示例和流程图说明其工作原理。

2.1 Struts框架的核心组件

Struts框架的核心组件包括MVC架构的各个模块、配置文件 struts-config.xml 以及 ActionServlet 的配置与初始化。理解这些组件的结构和作用,是掌握Struts框架使用的基础。

2.1.1 Struts的MVC架构解析

Struts框架采用标准的MVC模式进行设计,其结构如下:

  • Model(模型) :由ActionForm和业务逻辑组成,处理数据的封装和验证。
  • View(视图) :主要由JSP页面构成,用于展示数据和接收用户输入。
  • Controller(控制器) :由 ActionServlet Action 类组成,负责接收请求并调用相应的业务逻辑。
MVC流程图如下:
graph TD
    A[用户请求] --> B[ActionServlet]
    B --> C{检查是否已有ActionForm}
    C -->|是| D[填充表单数据]
    C -->|否| E[创建新的ActionForm]
    D & E --> F[调用Action类]
    F --> G[执行业务逻辑]
    G --> H[返回ActionForward]
    H --> I[跳转至指定视图]

2.1.2 Struts配置文件struts-config.xml的作用与结构

struts-config.xml 是Struts框架的核心配置文件,主要用于定义:

  • Action的映射路径
  • ActionForm的配置
  • Action与视图的跳转关系(ActionForward)
  • 插件配置
示例配置:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN"
    "http://jakarta.apache.org/struts/dtds/struts-config_1_3.dtd">

<struts-config>
    <form-beans>
        <form-bean name="loginForm" type="com.example.LoginForm"/>
    </form-beans>

    <action-mappings>
        <action path="/login" type="com.example.LoginAction" name="loginForm" scope="request" validate="true" input="/login.jsp">
            <forward name="success" path="/welcome.jsp"/>
            <forward name="failure" path="/login.jsp"/>
        </action>
    </action-mappings>
</struts-config>

代码解释:

  • <form-beans> :定义了表单Bean,用于封装用户输入数据。
  • <action-mappings> :定义了请求路径与Action类的映射关系。
  • path="/login" :表示用户访问的URL路径。
  • type="com.example.LoginAction" :指定处理该请求的Action类。
  • name="loginForm" :指定绑定的ActionForm。
  • validate="true" :表示启用表单验证。
  • <forward> :定义Action执行后的跳转路径。

2.1.3 ActionServlet的配置与初始化过程

ActionServlet 是Struts框架的中央控制器,负责接收所有的请求并根据配置进行分发。它在 web.xml 中进行配置,并在应用启动时初始化。

web.xml配置示例:
<servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    <init-param>
        <param-name>config</param-name>
        <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>action</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

参数说明:

  • config :指定Struts配置文件的路径。
  • *.do :所有以 .do 结尾的请求都会被 ActionServlet 处理。
  • load-on-startup :设置为1表示在应用启动时加载该Servlet。

2.2 Struts的请求处理流程

Struts框架的请求处理流程清晰明了,主要包括用户请求的接收、Action类的执行以及视图的返回。理解这一流程有助于更好地进行开发与调试。

2.2.1 用户请求的处理流程详解

当用户访问一个Struts应用时,整个请求处理流程如下:

  1. 浏览器发送HTTP请求(如 /login.do )。
  2. 请求被 ActionServlet 拦截。
  3. ActionServlet 根据 struts-config.xml 配置,查找对应的 ActionMapping
  4. 根据配置创建或获取 ActionForm ,并进行数据填充和验证。
  5. 调用对应的 Action 类,执行业务逻辑。
  6. Action 返回 ActionForward ,决定跳转至哪个视图页面。
  7. 将结果返回给用户浏览器。

2.2.2 Action类的编写与执行机制

Action类是Struts框架中处理业务逻辑的核心部分。它需要继承 org.apache.struts.action.Action 类,并重写其 execute() 方法。

示例代码:
package com.example;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginAction extends Action {

    @Override
    public ActionForward execute(ActionMapping mapping, ActionForm form,
                                 HttpServletRequest request, HttpServletResponse response) throws Exception {
        LoginForm loginForm = (LoginForm) form;
        String username = loginForm.getUsername();
        String password = loginForm.getPassword();

        if ("admin".equals(username) && "123456".equals(password)) {
            return mapping.findForward("success");
        } else {
            return mapping.findForward("failure");
        }
    }
}

代码逻辑分析:

  • execute() 方法是Action类的执行入口。
  • mapping.findForward("success") :根据配置文件中的 <forward> 标签查找跳转路径。
  • LoginForm 是继承自 ActionForm 的类,用于封装用户输入的数据。

2.2.3 ActionForward与ActionMapping的关系与配置方式

ActionMapping 用于定义请求路径与Action类的映射关系,而 ActionForward 则用于定义Action执行后的跳转路径。

ActionForward配置示例:
<action path="/login" type="com.example.LoginAction" name="loginForm" scope="request" validate="true" input="/login.jsp">
    <forward name="success" path="/welcome.jsp"/>
    <forward name="failure" path="/login.jsp"/>
</action>

关系说明:

  • name="success" :在Action中通过 mapping.findForward("success") 获取该跳转路径。
  • path="/welcome.jsp" :实际跳转的页面路径。

2.3 Struts框架的扩展与优化

Struts框架提供了良好的扩展机制,包括插件系统、异常处理和多模块配置等,这些功能可以提升系统的灵活性和健壮性。

2.3.1 Struts的插件机制与常见插件使用

Struts允许通过插件机制扩展其功能,常见的插件包括:

  • Tiles插件 :用于页面布局管理。
  • Validator插件 :提供表单验证功能。
  • DynaActionForm插件 :动态表单支持,无需手动创建ActionForm类。
插件配置示例(Tiles):
<plug-in className="org.apache.struts.tiles.TilesPlugin">
    <set-property property="definitions-config" value="/WEB-INF/tiles-defs.xml"/>
</plug-in>

2.3.2 Struts的异常处理机制

Struts提供了统一的异常处理机制,可以在 struts-config.xml 中配置全局异常处理器。

示例配置:
<global-exceptions>
    <exception key="error.database" type="java.sql.SQLException" path="/error.jsp"/>
</global-exceptions>

说明:

  • 当程序抛出 SQLException 时,会自动跳转到 /error.jsp 页面。
  • key 属性用于指定异常提示信息的键值。

2.3.3 多模块项目中的Struts配置实践

在大型项目中,通常需要将Struts配置按模块进行划分,以提高可维护性。可以通过多个 struts-config.xml 文件来实现。

模块化配置步骤:
  1. 创建多个配置文件,如 struts-config-user.xml struts-config-order.xml
  2. web.xml 中指定多个配置文件路径:
<init-param>
    <param-name>config</param-name>
    <param-value>/WEB-INF/struts-config.xml,/WEB-INF/struts-config-user.xml</param-value>
</init-param>
  1. 每个模块的配置文件分别定义各自的Action和Form。
多模块配置优点:
  • 模块之间解耦,便于团队协作开发。
  • 配置清晰,易于维护和升级。

通过本章的学习,我们深入了解了Struts框架的核心组件及其配置方式,掌握了请求处理的基本流程,并了解了Struts的扩展与优化手段。在实际开发中,合理配置和使用Struts框架,能够有效提升系统的可维护性和开发效率。

3. Action、ActionForm与ActionMapping详解

在Struts框架中, Action ActionForm ActionMapping 是三个核心组件,它们分别承担着控制逻辑、数据封装和请求路由的重要职责。深入理解这三者的功能和协同机制,是掌握Struts框架与Spring整合的关键。本章将围绕这三个组件的定义、生命周期、执行机制和配置方式,结合SSH整合的实际应用场景,进行详细解析。

3.1 Action类的设计与实现

Action 类是Struts框架中负责处理用户请求的核心组件。它在MVC架构中扮演控制器的角色,接收来自用户的请求,调用业务逻辑,并返回相应的视图路径。

3.1.1 Action类的生命周期与执行机制

每个 Action 类在Struts框架中都是由 ActionServlet 通过反射机制实例化的。其生命周期主要经历以下几个阶段:

  • 初始化阶段 :当用户第一次访问对应的URL时, ActionServlet 会加载并实例化该 Action 类。
  • 执行阶段 :每次请求到达时, ActionServlet 会调用 execute() 方法,处理业务逻辑。
  • 销毁阶段 :当应用关闭时, Action 实例会被销毁。

示例代码如下:

public class UserAction extends Action {
    public ActionForward execute(ActionMapping mapping, ActionForm form,
                                 HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 获取ActionForm中的数据
        UserForm userForm = (UserForm) form;
        String username = userForm.getUsername();

        // 调用业务逻辑
        UserService userService = new UserService();
        User user = userService.getUserByName(username);

        // 存入request域,供JSP页面使用
        request.setAttribute("user", user);

        // 返回视图路径
        return mapping.findForward("success");
    }
}

逐行解析:

  1. public class UserAction extends Action :定义了一个继承自 Action 的类。
  2. execute() 方法是 Action 类的核心方法,所有业务逻辑都在此方法中处理。
  3. ActionMapping 用于获取配置中的转发路径, ActionForm 封装了用户提交的表单数据。
  4. 通过 UserService 调用业务层逻辑。
  5. 使用 request.setAttribute() 将数据传递给视图层。
  6. return mapping.findForward("success") 根据配置返回视图路径。

3.1.2 Action类与Spring Bean的整合方式

在SSH整合中, Action 类通常需要调用Spring管理的Bean(如Service层对象)。可以通过Spring插件实现集成。

配置步骤如下:

  1. 添加Spring插件依赖 :确保项目中引入了 struts2-spring-plugin
  2. 配置web.xml
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
  1. 配置struts.xml
<constant name="struts.objectFactory" value="spring" />

整合后调用Spring Bean示例:

public class UserAction extends ActionSupport {
    private UserService userService;

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public String execute() {
        User user = userService.getUserByName("john");
        // ...
        return SUCCESS;
    }
}

逻辑分析:

  • UserService 由Spring管理,通过Setter注入到 UserAction 中。
  • Struts使用Spring作为对象工厂( struts.objectFactory ),实现 Action 类的IoC管理。

3.1.3 Action类调用业务逻辑的实现方法

在SSH整合中, Action 类通常不直接处理业务逻辑,而是通过调用Spring管理的Service Bean完成。这样可以实现表现层与业务层的解耦。

调用方式:

  • Setter注入 :如上节所示。
  • 注解注入 :使用 @Autowired 注入Spring Bean。

示例代码:

public class UserAction extends ActionSupport {
    @Autowired
    private UserService userService;

    public String execute() {
        User user = userService.getUserByName("john");
        return SUCCESS;
    }
}

说明:

  • @Autowired 表示由Spring自动注入 UserService 实例。
  • 此方式更简洁,但需确保Spring上下文已正确加载。

3.2 ActionForm的数据封装与验证

ActionForm 用于封装用户提交的表单数据,并支持数据验证。它是Struts MVC架构中模型层的一部分。

3.2.1 ActionForm的作用与使用场景

ActionForm 的主要作用是将HTTP请求参数自动绑定到Java对象中,并进行基本的类型转换。

使用场景:

  • 接收用户登录表单数据。
  • 接收注册信息、订单信息等复杂表单结构。

示例:

public class UserForm extends ActionForm {
    private String username;
    private String password;

    // Getter and Setter
}

逻辑说明:

  • ActionForm 继承自 org.apache.struts.action.ActionForm
  • Struts会自动将表单字段与类属性匹配,完成数据绑定。

3.2.2 表单数据的自动绑定与类型转换

Struts支持基本数据类型的自动转换,如 int double boolean 等。对于日期、集合等复杂类型,需要自定义转换器。

示例:

public class RegisterForm extends ActionForm {
    private String name;
    private int age;
    private Date birthDate;

    // Getter and Setter
}

自动绑定机制:

  • Struts会根据表单字段名(如 name="birthDate" )自动转换为 Date 类型。
  • 若格式不匹配,会抛出异常或触发验证逻辑。

3.2.3 验证逻辑的实现与自定义验证器配置

Struts提供了内置的验证机制,同时也支持自定义验证器。

内置验证示例:

public class LoginValidator extends ValidatorForm {
    private String username;
    private String password;

    public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
        ActionErrors errors = new ActionErrors();
        if (username == null || username.length() < 1) {
            errors.add("username", new ActionMessage("error.username.required"));
        }
        if (password == null || password.length() < 1) {
            errors.add("password", new ActionMessage("error.password.required"));
        }
        return errors;
    }

    // Getter and Setter
}

配置验证规则:

struts-config.xml 中启用验证:

<form-beans>
    <form-bean name="loginForm" type="com.example.LoginValidator"/>
</form-beans>

流程图说明:

graph TD
    A[用户提交表单] --> B[ActionServlet接收请求]
    B --> C[绑定ActionForm]
    C --> D[调用validate方法]
    D -->|验证失败| E[返回错误信息]
    D -->|验证通过| F[调用Action类处理业务逻辑]

3.3 ActionMapping的配置与路由机制

ActionMapping 用于定义URL请求与 Action 类之间的映射关系。它是Struts配置文件的核心内容之一。

3.3.1 ActionMapping的配置方式与路径映射

struts-config.xml 中配置 ActionMapping ,示例如下:

<action-mappings>
    <action path="/login" type="com.example.LoginAction" name="loginForm" scope="request" validate="true" input="/login.jsp">
        <forward name="success" path="/home.jsp"/>
        <forward name="failure" path="/login.jsp"/>
    </action>
</action-mappings>

参数说明:

  • path :请求路径,对应URL中的路径。
  • type :对应的 Action 类。
  • name :绑定的 ActionForm 名称。
  • scope :作用域(request/session)。
  • validate :是否启用验证。
  • input :验证失败后的返回页面。
  • <forward> :定义转发路径。

3.3.2 多路径映射与模块化配置

在一个大型项目中,多个模块可能需要不同的路径映射。Struts支持模块化配置。

模块化配置示例:

<struts-config>
    <action-mappings>
        <action path="/user/login" ... />
        <action path="/admin/login" ... />
    </action-mappings>
</struts-config>

优势:

  • 避免路径冲突。
  • 便于权限管理和模块划分。

3.3.3 使用通配符进行动态路径匹配

Struts支持通配符 * 进行路径匹配,提升配置灵活性。

通配符配置示例:

<action path="/user/*" type="com.example.UserAction">
    <param name="method" value="{1}"/>
    <forward name="success" path="/user/success.jsp"/>
</action>

说明:

  • /user/* 可以匹配 /user/add /user/delete 等路径。
  • {1} 表示第一个通配符部分(如 add delete ),可用于动态调用方法。

Action类中使用通配符:

public class UserAction extends Action {
    public ActionForward execute(ActionMapping mapping, ActionForm form,
                                 HttpServletRequest request, HttpServletResponse response) throws Exception {
        String method = mapping.getParameter();
        if ("add".equals(method)) {
            // 处理添加逻辑
        } else if ("delete".equals(method)) {
            // 处理删除逻辑
        }
        return mapping.findForward("success");
    }
}

配置表格对比:

配置方式 示例 优点 缺点
静态路径 /login 简单直观 配置繁琐
通配符路径 /user/* 灵活,可复用 可读性较差
模块化配置 /user/login , /admin/login 分模块清晰 配置文件复杂

通过本章对 Action ActionForm ActionMapping 的深入解析,我们可以清晰地看到它们在Struts框架中的作用机制,以及如何与Spring整合以实现更高效、灵活的企业级应用开发。下一章将继续深入讲解Spring与Struts整合的具体流程和配置实践。

4. Spring框架整合Struts流程

Spring作为SSH整合中的业务逻辑层核心框架,其与Struts的整合是实现模块解耦和依赖管理的关键。本章将围绕整合流程展开详细讲解,深入探讨Spring与Struts协同工作的机制、具体配置步骤以及整合后的测试与调试方法。通过本章内容,读者将掌握如何将Spring的IoC容器与Struts框架无缝集成,提升项目的可维护性和扩展性。

4.1 Spring与Struts整合的基本原理

在传统的Struts项目中,Action类是由Struts框架自身实例化的,无法直接利用Spring的依赖注入机制。为了实现Struts与Spring的整合,需要借助Spring提供的插件或机制,将Action类的创建和管理交给Spring容器,从而实现IoC控制反转和AOP功能的集成。

4.1.1 Spring IoC容器与Struts的协同机制

Spring的IoC容器负责管理Bean的生命周期,包括Action、Service、DAO等组件。Struts本身不具备IoC功能,因此需要通过特定方式让Struts的Action类由Spring容器创建,而不是由Struts的ActionServlet直接new出来。

整合的核心思想是:在Web应用启动时,先启动Spring容器,加载配置的Bean,然后让Struts框架在创建Action实例时,从Spring容器中获取Bean。

4.1.2 Struts Action类如何交由Spring管理

Struts 1.x的Action类默认是由Struts框架创建的,要实现Spring管理,需要使用 Spring的DelegatingActionProxy 类。该类继承自 org.apache.struts.action.Action ,并重写了 execute 方法,在执行时从Spring上下文中获取实际的Action Bean。

整合流程如下:

  1. 配置Spring监听器,加载Spring上下文;
  2. struts-config.xml 中配置Action类为 DelegatingActionProxy
  3. 在Spring配置文件中定义具体的Action Bean,并由Spring管理。

4.1.3 Spring插件的引入与配置方法

Spring提供了一个专门用于与Struts整合的插件 spring-webmvc-struts1 (Maven坐标为 org.springframework:spring-webmvc-struts1 )。引入该插件后,可以更方便地进行整合。

引入插件后,需要在 web.xml 中添加Spring监听器,并配置Struts的Action为代理类。以下是整合的基本结构图:

graph TD
    A[Web应用启动] --> B[初始化Spring上下文]
    B --> C[加载Spring配置文件]
    C --> D[Struts ActionServlet初始化]
    D --> E[Action请求到达]
    E --> F{是否配置为DelegatingActionProxy}
    F -- 是 --> G[从Spring容器中获取实际Action]
    F -- 否 --> H[Struts自身创建Action实例]
    G --> I[执行业务逻辑]
    H --> I

4.2 整合Spring与Struts的配置实践

接下来我们将通过具体的配置步骤来展示如何将Spring与Struts整合,并通过代码示例说明整合过程中的关键配置。

4.2.1 web.xml中Spring监听器的配置

web.xml 中配置Spring监听器 ContextLoaderListener ,用于在Web应用启动时加载Spring上下文:

<!-- web.xml -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- 可选:指定Spring配置文件位置 -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/classes/applicationContext.xml</param-value>
</context-param>

4.2.2 struts-config.xml与Spring配置的协同方式

struts-config.xml 中配置Action时,将其class属性指定为 org.springframework.web.struts.DelegatingActionProxy

<action path="/login"
        type="org.springframework.web.struts.DelegatingActionProxy"
        name="loginForm"
        scope="request"
        validate="true"
        input="/login.jsp">
    <forward name="success" path="/welcome.jsp"/>
    <forward name="failure" path="/login.jsp"/>
</action>

这样,当用户访问 /login.do 时,Struts会通过 DelegatingActionProxy 从Spring容器中获取实际的Action Bean。

4.2.3 基于注解的Spring与Struts整合实践

从Spring 2.5开始,可以使用注解方式简化配置。例如,可以在Action类上使用 @Component 注解,并结合 @Autowired 注入Service层组件:

import org.springframework.stereotype.Component;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class LoginAction extends Action {

    @Autowired
    private UserService userService;

    @Override
    public ActionForward execute(ActionMapping mapping, ActionForm form,
                                 HttpServletRequest request, HttpServletResponse response) throws Exception {
        LoginForm loginForm = (LoginForm) form;
        if (userService.authenticate(loginForm.getUsername(), loginForm.getPassword())) {
            return mapping.findForward("success");
        } else {
            return mapping.findForward("failure");
        }
    }
}

代码逻辑分析
- @Component :将该类注册为Spring Bean;
- @Autowired :自动注入UserService,无需手动new;
- 继承 Action 类,重写 execute 方法,处理请求逻辑;
- 通过 mapping.findForward() 返回视图跳转路径。

在Spring配置文件中启用注解扫描:

<!-- applicationContext.xml -->
<context:component-scan base-package="com.example.actions" />

4.3 整合后的测试与问题排查

整合完成后,必须进行功能测试和问题排查,以确保整合成功且运行稳定。

4.3.1 整合后的请求流程测试方法

测试步骤如下:

  1. 启动Web应用,观察日志中是否加载了Spring上下文;
  2. 访问Struts Action路径,如 /login.do
  3. 检查是否成功跳转到预期的JSP页面;
  4. 查看Action中注入的Service是否为Spring管理的Bean。

可以通过在Action中添加日志输出来验证注入是否成功:

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class LoginAction extends Action {
    private static final Log logger = LogFactory.getLog(LoginAction.class);

    @Autowired
    private UserService userService;

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    @Override
    public ActionForward execute(ActionMapping mapping, ActionForm form,
                                 HttpServletRequest request, HttpServletResponse response) throws Exception {
        logger.info("UserService instance: " + userService);
        // ...
    }
}

4.3.2 常见整合问题及解决策略

问题现象 原因分析 解决方案
Action类未被Spring管理 未正确配置 DelegatingActionProxy 检查struts-config.xml中type配置是否为代理类
注入的Service为null 未启用组件扫描或Bean未注册 检查Spring配置文件是否扫描到Action包
Spring上下文未加载 未配置ContextLoaderListener 检查web.xml是否添加监听器和contextConfigLocation参数
多Action共享同一个Spring Bean 默认作用域为singleton 使用 @Scope("prototype") 为每个请求创建新实例

4.3.3 日志调试与异常跟踪

推荐使用日志框架如Log4j或SLF4J进行调试。在整合过程中,常见的异常包括:

  • NoSuchBeanDefinitionException :Spring未找到对应的Bean;
  • NullPointerException :注入的Service未初始化;
  • ClassNotFoundException :缺少Spring或Struts相关依赖。

日志输出示例:

logger.debug("Action instance: " + this);
logger.warn("UserService not initialized");

使用日志可以帮助快速定位问题根源,特别是在多模块项目中,日志信息对调试至关重要。

本章小结

本章深入讲解了Spring与Struts整合的核心机制和具体实现步骤。通过配置Spring监听器、使用 DelegatingActionProxy 代理类、结合注解方式,可以实现Struts与Spring的无缝整合。整合后,不仅提升了项目的模块化程度,还使得依赖注入和事务管理等Spring高级功能得以应用。同时,通过详细的测试方法和常见问题排查策略,帮助开发者快速定位并解决整合过程中的问题。

在下一章中,我们将深入探讨Spring的依赖注入机制及其在SSH整合中的具体应用,进一步提升项目的可维护性与扩展性。

5. Spring依赖注入(DI)配置

依赖注入(Dependency Injection,DI)是Spring框架的核心功能之一,它通过控制反转(IoC)机制将对象之间的依赖关系交给容器来管理,极大地提升了代码的可维护性与可测试性。在SSH(Struts + Spring + Hibernate)整合项目中,DI机制贯穿整个应用结构,使得表现层(Struts)、业务层(Spring)与数据访问层(Hibernate)之间的耦合度显著降低,从而实现模块化开发与灵活配置。

本章将系统讲解Spring依赖注入的配置方法,涵盖基本概念、高级技巧与实际项目中的应用案例。我们将从构造函数注入与Setter注入的区别入手,逐步深入到注解方式与XML配置的对比,最后通过具体的Service层与DAO层整合示例,展示DI在企业级应用中的实际应用。

5.1 依赖注入的基本概念与方式

5.1.1 构造函数注入与Setter注入的区别

在Spring中,依赖注入主要有两种方式: 构造函数注入 (Constructor Injection)和 Setter注入 (Setter Injection)。两者在使用场景、可读性和可维护性上各有优劣。

对比项 构造函数注入 Setter注入
初始化方式 通过构造函数传递依赖 通过Setter方法赋值
可变性 通常用于不可变依赖 适用于可变依赖
可读性 依赖关系明确,适合强制依赖 更灵活,适合可选依赖
测试友好性 易于测试,适合单元测试 也可用于测试,但不如构造注入直观
示例代码:构造函数注入与Setter注入对比
// 构造函数注入示例
public class UserService {
    private UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void registerUser(String username) {
        userRepository.save(new User(username));
    }
}
<!-- XML配置:构造函数注入 -->
<bean id="userRepository" class="com.example.UserRepository" />
<bean id="userService" class="com.example.UserService">
    <constructor-arg ref="userRepository" />
</bean>
// Setter注入示例
public class UserService {
    private UserRepository userRepository;

    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void registerUser(String username) {
        userRepository.save(new User(username));
    }
}
<!-- XML配置:Setter注入 -->
<bean id="userService" class="com.example.UserService">
    <property name="userRepository" ref="userRepository" />
</bean>
逻辑分析与参数说明:
  • 构造函数注入 通过 <constructor-arg> 标签注入依赖,适用于不可变依赖或强制依赖,确保对象在创建时就处于可用状态。
  • Setter注入 通过 <property> 标签进行配置,适用于可选依赖或需要动态修改的依赖。
  • 在实际开发中,建议优先使用构造函数注入,以确保对象的完整性与可测试性。

5.1.2 注解方式注入与XML配置的对比

Spring支持通过注解(如 @Autowired @Resource )实现依赖注入,这与传统的XML配置方式相比,代码更简洁,可读性更高。

特性 注解方式 XML配置方式
配置位置 Java代码中 XML文件中
优点 简洁、易维护、与代码紧密结合 配置集中、适合大型项目
缺点 与代码耦合,难以动态修改 配置繁琐,不利于快速修改
典型注解 @Autowired , @Resource , @Inject <bean> , <property> , <constructor-arg>
示例代码:注解方式注入
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;

    public void registerUser(String username) {
        userRepository.save(new User(username));
    }
}
@Repository
public class UserRepository {
    public void save(User user) {
        // 保存逻辑
    }
}
逻辑分析与参数说明:
  • @Service :标注该类为Spring管理的业务组件。
  • @Autowired :自动注入 UserRepository 实例,Spring会自动查找匹配的Bean进行注入。
  • @Repository :用于数据访问层,Spring会为其添加异常转换机制。

5.1.3 依赖注入在业务层与DAO层的典型应用

在SSH整合项目中,Spring负责管理业务层(Service)与数据访问层(DAO)之间的依赖关系。通常,Service层通过DI注入DAO层的实现类,实现数据访问逻辑的解耦。

代码示例:Service层与DAO层的DI整合
// DAO接口
public interface UserDao {
    void save(User user);
}
// DAO实现类
@Repository
public class UserDaoImpl implements UserDao {
    public void save(User user) {
        // Hibernate操作
    }
}
// Service类
@Service
public class UserService {
    @Autowired
    private UserDao userDao;

    public void createUser(User user) {
        userDao.save(user);
    }
}
逻辑分析与参数说明:
  • @Repository 标注的 UserDaoImpl 类被Spring识别为DAO组件,自动注册为Bean。
  • @Service 标注的 UserService 类作为业务组件,通过 @Autowired 自动注入 UserDao 接口的实现。
  • 这种方式实现了接口与实现的分离,便于后期替换实现类,提高代码可扩展性。

5.2 Spring DI的高级配置技巧

5.2.1 自动装配(Autowiring)的配置与使用

Spring支持自动装配(Autowiring),即容器自动解析并注入Bean之间的依赖关系。常见的自动装配模式包括按类型自动装配( byType )和按名称自动装配( byName )。

XML配置示例:自动装配byType
<bean id="userRepository" class="com.example.UserRepository" />
<bean id="userService" class="com.example.UserService" autowire="byType" />
注解方式自动装配示例:
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
}
逻辑分析与参数说明:
  • autowire="byType" :Spring根据类型自动匹配并注入Bean。
  • @Autowired :默认按类型进行自动装配,Spring会查找容器中类型匹配的Bean进行注入。
  • 使用自动装配可以减少配置文件的冗余,提高开发效率。

5.2.2 多个Bean的依赖关系配置

当存在多个相同类型的Bean时,Spring无法自动确定注入哪一个,需要通过 @Qualifier 注解或XML配置的 ref 属性进行明确指定。

示例代码:多个Bean的依赖注入
@Repository("mysqlUserRepository")
public class MysqlUserRepository implements UserRepository {
    // MySQL实现
}
@Repository("oracleUserRepository")
public class OracleUserRepository implements UserRepository {
    // Oracle实现
}
@Service
public class UserService {
    @Autowired
    @Qualifier("mysqlUserRepository")
    private UserRepository userRepository;
}
逻辑分析与参数说明:
  • @Qualifier("mysqlUserRepository") :明确指定注入的Bean名称。
  • 如果不使用 @Qualifier ,Spring将抛出 NoUniqueBeanDefinitionException 异常,提示存在多个匹配的Bean。

5.2.3 Bean的作用域与生命周期管理

Spring支持多种Bean的作用域,常用的有 singleton prototype request session 等。Bean的生命周期可以通过 @PostConstruct @PreDestroy 或XML配置的 init-method destroy-method 进行管理。

示例代码:Bean作用域与生命周期管理
@Component
@Scope("prototype")
public class UserSession {
    @PostConstruct
    public void init() {
        System.out.println("UserSession initialized.");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("UserSession destroyed.");
    }
}
<bean id="userSession" class="com.example.UserSession" scope="prototype"
      init-method="init" destroy-method="destroy" />
逻辑分析与参数说明:
  • @Scope("prototype") :表示每次请求都会创建一个新的Bean实例。
  • @PostConstruct :在Bean初始化后执行,通常用于资源加载。
  • @PreDestroy :在Bean销毁前执行,用于资源释放。
  • init-method destroy-method 为XML方式配置生命周期方法。

5.3 实际项目中的DI配置案例

5.3.1 Service层与DAO层的DI整合示例

在典型的SSH项目中,Service层通过Spring管理DAO层的依赖关系。下面是一个完整的服务调用流程示例。

流程图:Service调用DAO的依赖注入流程
graph TD
    A[UserService] -->|@Autowired| B(UserDao)
    B --> C[MysqlUserDao]
    B --> D[OracleUserDao]
    A -->|调用方法| C
    A -->|调用方法| D
逻辑分析:
  • UserService 通过 @Autowired 注入 UserDao 接口。
  • Spring根据配置或 @Qualifier 决定注入 MysqlUserDao 还是 OracleUserDao
  • 接口与实现分离的设计模式,使得系统具有良好的扩展性。

5.3.2 事务管理器的注入与配置

Spring事务管理通常通过依赖注入将事务管理器( PlatformTransactionManager )注入到配置类或Service中。

示例代码:事务管理器的注入
@Configuration
@EnableTransactionManagement
public class SpringConfig {
    @Autowired
    private DataSource dataSource;

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource);
    }
}
@Service
@Transactional
public class UserService {
    @Autowired
    private UserRepository userRepository;

    public void createUser(User user) {
        userRepository.save(user);
    }
}
逻辑分析与参数说明:
  • @EnableTransactionManagement :启用Spring的事务管理功能。
  • @Transactional :标注类或方法开启事务管理。
  • DataSourceTransactionManager :基于JDBC的数据源事务管理器,适用于单数据源场景。

5.3.3 配置多数据源时的依赖注入处理

在多数据源场景下,每个数据源都需要独立的事务管理器,并通过DI机制进行管理。

示例代码:多数据源配置
@Configuration
public class MultiDataSourceConfig {
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.mysql")
    public DataSource mysqlDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.oracle")
    public DataSource oracleDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public PlatformTransactionManager mysqlTransactionManager(
            @Qualifier("mysqlDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean
    public PlatformTransactionManager oracleTransactionManager(
            @Qualifier("oracleDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}
逻辑分析与参数说明:
  • @ConfigurationProperties :根据配置文件自动绑定数据源属性。
  • @Qualifier :明确指定注入哪个数据源。
  • 每个数据源对应一个事务管理器,避免事务冲突。

本章系统讲解了Spring依赖注入的配置方式,从基本概念到高级技巧再到实际项目应用,全面展示了DI机制在SSH整合项目中的核心作用。下一章将围绕Spring事务管理展开,深入探讨事务的配置与优化策略。

6. Spring事务管理集成

6.1 Spring事务管理的基本概念

Spring的事务管理机制是构建企业级应用的重要基石,它提供了声明式事务和编程式事务两种方式,极大地简化了事务控制逻辑,同时保证了数据的一致性与完整性。

6.1.1 事务的ACID特性与传播行为

事务必须满足ACID特性:

特性 描述
原子性(Atomicity) 事务中的操作要么全部成功,要么全部失败
一致性(Consistency) 事务执行前后数据保持一致性状态
隔离性(Isolation) 多个事务并发执行时互不干扰
持久性(Durability) 事务一旦提交,数据将永久保存

Spring支持的事务传播行为有以下几种常见类型:

传播行为 描述
PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务
PROPAGATION_REQUIRES_NEW 总是新建一个事务,并挂起当前事务(如果存在)
PROPAGATION_SUPPORTS 支持当前事务,不存在则以非事务方式执行
PROPAGATION_NEVER 不能在事务中执行,否则抛出异常

6.1.2 声明式事务与编程式事务的区别

  • 声明式事务 :通过XML配置或注解方式定义事务边界,代码侵入性小,易于维护。
  • 编程式事务 :通过 TransactionTemplate PlatformTransactionManager 手动控制事务,适用于对事务控制要求更精细的场景。

示例:声明式事务注解方式

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void transferMoney(String fromAccount, String toAccount, double amount) {
    // 执行转账业务逻辑
}

6.1.3 事务管理器的选择与配置

Spring支持多种事务管理器,常见的有:

事务管理器类 用途
DataSourceTransactionManager 用于单数据源事务管理
HibernateTransactionManager 与Hibernate整合时使用
JtaTransactionManager 用于分布式事务管理

配置示例(基于Hibernate):

<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>

6.2 Spring事务管理的配置方式

6.2.1 XML配置方式的事务管理

使用XML方式配置事务管理,需要引入 tx 命名空间:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="...">

    <!-- 启用事务注解 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>
</beans>

6.2.2 注解方式的事务管理配置

在Service层的方法上使用 @Transactional 注解,即可启用事务控制:

@Service
public class AccountService {

    @Transactional
    public void transfer(String from, String to, double amount) {
        // 业务逻辑
    }
}

注意 :只有在Spring管理的Bean中使用 @Transactional 才有效,且事务方法必须为 public

6.2.3 多事务资源下的事务管理策略

当系统涉及多个数据源或外部服务时,需考虑使用分布式事务管理。此时可以引入JTA(Java Transaction API)规范,配置 JtaTransactionManager

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>

或者结合Atomikos、Bitronix等第三方事务管理器进行集成。

6.3 SSH整合中的事务管理实践

6.3.1 事务在Service层的控制逻辑

在SSH整合架构中,事务应控制在Service层,避免在Action中直接操作事务。Service类由Spring管理,事务通过AOP方式自动开启与提交。

示例:

@Service
public class OrderService {

    @Autowired
    private OrderDAO orderDAO;

    @Transactional
    public void placeOrder(Order order) {
        orderDAO.save(order);
        // 其他业务操作
    }
}

6.3.2 事务回滚与异常处理机制

Spring默认只在遇到 RuntimeException 时回滚事务。如需在检查型异常(checked exception)下也回滚,需在 @Transactional 中指定 rollbackFor 属性:

@Transactional(rollbackFor = Exception.class)
public void processPayment() throws Exception {
    // 可能抛出异常的操作
}

6.3.3 事务管理在Hibernate操作中的应用

在Hibernate中,事务的开启与提交通常由Spring统一管理。Spring通过 HibernateTransactionManager 与Hibernate的 Session 进行绑定,确保事务边界清晰。

事务流程示意图(mermaid):

sequenceDiagram
    participant Client
    participant Action
    participant Service
    participant DAO
    participant Hibernate
    participant TransactionManager

    Client->>Action: 提交请求
    Action->>Service: 调用Service方法
    Service->>TransactionManager: 开启事务
    Service->>DAO: 调用持久层
    DAO->>Hibernate: 操作Session
    Hibernate->>DAO: 返回结果
    DAO->>Service: 返回处理结果
    Service->>TransactionManager: 提交事务
    Service->>Action: 返回响应
    Action->>Client: 响应用户

6.4 事务管理的性能优化与监控

6.4.1 事务粒度控制与性能调优

  • 事务粒度 :尽量减小事务范围,避免长时间占用数据库连接。
  • 只读事务优化 :对查询操作使用 @Transactional(readOnly = true) ,提升性能。
  • 批量操作优化 :在批量插入或更新时,合理使用Hibernate的批处理机制。

示例:

@Transactional(readOnly = true)
public List<User> getAllUsers() {
    return userDao.findAll();
}

6.4.2 事务日志与监控工具的使用

可以通过日志框架(如Log4j、SLF4J)记录事务的开启、提交和回滚过程,也可以集成监控工具(如Spring Boot Actuator、Prometheus + Grafana)进行事务行为的可视化分析。

6.4.3 分布式事务的初步探讨

在微服务架构或多个数据源协同工作的场景中,单机事务已无法满足需求。可采用以下方案:

  • 两阶段提交(2PC) :由事务协调者统一控制,保证一致性。
  • Seata、TCC、Saga模式 :适合高并发、分布式环境的事务管理方案。

例如使用Seata配置分布式事务(伪代码):

seata:
  enabled: true
  application-id: order-service
  tx-service-group: my_tx_group

下一章节将围绕Hibernate的实体映射与缓存机制展开深入解析。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:SSH整合是Java Web开发中的经典技术栈,涵盖Spring、Struts和Hibernate三大框架。本项目“SSH-struts第一种方式整合”详细讲解如何通过Struts实现MVC架构,利用Spring进行依赖注入与事务管理,并通过Hibernate完成数据库操作。项目包含完整的配置流程、前后端交互实现以及整合步骤,适合提升Java开发者在企业级应用开发中的实战能力。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

更多推荐