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

简介:SSH框架由Spring、Struts2和Hibernate三大开源框架组成,是Java Web开发中的经典组合。本教学代码提供完整的SSH整合实战资源,涵盖Spring的依赖注入与事务管理、Struts2的MVC请求处理、Hibernate的ORM持久化操作,并通过实际业务场景如用户注册、商品管理等演示如何将三者集成开发。适合初学者深入理解MVC架构、DI/AOP编程思想和ORM映射机制,也适合有经验开发者作为项目参考手册。
ssh完整版教学代码

1. SSH框架简介与整合意义

SSH框架是Java企业级Web开发中最具代表性的技术组合,由Spring、Struts2和Hibernate三大开源框架整合而成。Spring负责业务逻辑管理与依赖控制,Struts2实现MVC架构以分离表现层与控制层,Hibernate则提供持久层的ORM映射,简化数据库操作。

三者整合后,不仅提升了系统的模块化程度和可维护性,还增强了事务处理能力与开发效率,广泛应用于中大型企业级项目开发中。掌握SSH框架的整合与应用,是Java开发者迈向高级工程师的重要一步。

2. Spring框架IoC配置与依赖注入实现

Spring框架作为SSH(Spring + Struts2 + Hibernate)的核心组件,其核心功能之一是 控制反转(IoC) 依赖注入(DI) 机制。本章将从Spring框架的基础概念出发,深入解析IoC容器的配置方式、DI的实现机制,并通过实战演示如何使用Spring来管理业务组件。

2.1 Spring框架概述

Spring是一个轻量级的Java企业级开发框架,广泛用于构建模块化、松耦合的应用程序。它通过控制反转(Inversion of Control, IoC)和面向切面编程(AOP)来提升代码的可维护性和可测试性。

2.1.1 Spring框架的模块结构

Spring框架采用模块化设计,其主要模块包括:

模块名称 功能描述
Core Container (Core, Beans, Context) 提供IoC和DI的基础支持
AOP模块 支持面向切面编程
Data Access/Integration 提供JDBC、ORM、事务等数据访问支持
Web模块 支持构建Web应用,如集成Servlet和Struts2
Messaging 支持STOMP协议和WebSocket消息处理

Spring框架的核心是IoC容器,它负责创建对象(Bean)、管理对象的生命周期,并实现依赖注入。

2.1.2 控制反转(IoC)的核心思想

控制反转(IoC)是指将对象的创建和依赖关系的管理交给框架来处理,而不是由程序员手动控制。这种设计可以降低组件之间的耦合度,提升代码的可维护性。

例如,传统方式中,一个服务类会直接创建其依赖的DAO对象:

public class UserService {
    private UserDao userDao = new UserDao();
}

而在Spring中,该依赖关系由容器注入:

public class UserService {
    private UserDao userDao;

    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
}

这样,UserService不再关心UserDao的实现类,而是由Spring容器根据配置动态注入。

2.2 IoC容器的配置方式

Spring提供了两种主要的IoC配置方式: 基于XML的配置 基于注解的配置

2.2.1 基于XML的Bean配置

XML配置是Spring早期的主要配置方式。通过 beans.xml 文件定义Bean及其依赖关系。

示例:beans.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userDao" class="com.example.dao.UserDaoImpl" />

    <bean id="userService" class="com.example.service.UserService">
        <constructor-arg ref="userDao" />
    </bean>
</beans>

代码逻辑分析:

  • <bean id="userDao" class="..." /> 定义了一个UserDao的Bean。
  • <bean id="userService" class="..." /> 定义了UserService,并通过 <constructor-arg> 注入UserDao依赖。
  • constructor-arg 表示使用构造器注入方式。

Java代码调用:

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) context.getBean("userService");
userService.getUserById(1);

执行流程:

  1. Spring容器加载 beans.xml 配置文件。
  2. 创建 userDao userService 的实例。
  3. userService 通过构造器注入 userDao
  4. 通过 getBean 获取实例并调用方法。

2.2.2 基于注解的Bean管理

随着Spring版本的演进,注解配置逐渐成为主流。通过 @Component @Service @Repository 等注解来声明Bean,并结合 @Autowired 实现依赖注入。

示例:UserDao接口与实现类

@Repository
public class UserDaoImpl implements UserDao {
    public User getUserById(int id) {
        // 模拟数据库查询
        return new User(id, "John");
    }
}

UserService类:

@Service
public class UserService {
    @Autowired
    private UserDao userDao;

    public User getUserById(int id) {
        return userDao.getUserById(id);
    }
}

启用组件扫描:

在配置类中添加 @ComponentScan 启用组件扫描:

@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
}

Java测试类:

public class MainApp {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = context.getBean(UserService.class);
        User user = userService.getUserById(1);
        System.out.println(user.getName());
    }
}

执行流程:

  1. Spring容器扫描 com.example 包下的注解Bean。
  2. 自动注册 UserDaoImpl UserService 为Bean。
  3. 使用 @Autowired 自动注入依赖。
  4. 通过 getBean 获取实例并调用方法。

2.3 依赖注入(DI)的实现机制

依赖注入(DI)是Spring实现IoC的核心机制,主要包括 构造器注入 Setter注入 以及 自动装配

2.3.1 构造器注入与Setter注入

构造器注入(Constructor Injection)

构造器注入是在Bean创建时通过构造函数传入依赖对象。

XML配置示例:

<bean id="userService" class="com.example.service.UserService">
    <constructor-arg ref="userDao" />
</bean>

Java类定义:

public class UserService {
    private UserDao userDao;

    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    public void doSomething() {
        userDao.save();
    }
}
Setter注入(Setter Injection)

Setter注入通过Setter方法设置依赖对象。

XML配置示例:

<bean id="userService" class="com.example.service.UserService">
    <property name="userDao" ref="userDao" />
</bean>

Java类定义:

public class UserService {
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void doSomething() {
        userDao.save();
    }
}

2.3.2 自动装配与作用域管理

自动装配(Autowiring)

Spring支持自动装配,通过 byType byName constructor 等方式自动匹配依赖。

XML配置自动装配:

<bean id="userService" class="com.example.service.UserService" autowire="byType" />

注解方式自动装配:

@Autowired
private UserDao userDao;
Bean的作用域(Scope)

Spring支持多种Bean作用域:

作用域类型 描述
singleton 默认作用域,整个Spring上下文中只创建一个实例
prototype 每次请求都会创建一个新的实例
request 在一次HTTP请求中有效(仅Web环境)
session 在一个HTTP会话中有效(仅Web环境)

示例:配置prototype作用域

<bean id="userService" class="com.example.service.UserService" scope="prototype" />

2.4 实战:使用Spring管理业务组件

2.4.1 创建Spring配置文件

创建 applicationContext.xml 作为Spring主配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 配置UserDao -->
    <bean id="userDao" class="com.example.dao.UserDaoImpl" />

    <!-- 配置UserService -->
    <bean id="userService" class="com.example.service.UserService">
        <property name="userDao" ref="userDao" />
    </bean>
</beans>

2.4.2 编写测试类验证IoC功能

TestMain.java

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestMain {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) context.getBean("userService");
        User user = userService.getUserById(1001);
        System.out.println("用户ID:" + user.getId() + ",用户名:" + user.getName());
    }
}

运行结果:

用户ID:1001,用户名:John

流程图说明:

graph TD
    A[Spring容器启动] --> B[加载applicationContext.xml]
    B --> C[创建userDao实例]
    B --> D[创建userService实例]
    D --> E[通过Setter注入userDao]
    A --> F[调用getBean获取userService]
    F --> G[调用userService.getUserById()]
    G --> H[调用userDao.getUserById()]
    H --> I[返回用户对象]

小结(非总结段落,仅作章节回顾)

本章详细介绍了Spring框架的核心功能之一:IoC容器与依赖注入机制。我们从Spring框架的模块结构出发,深入探讨了控制反转的基本思想,并通过XML和注解两种方式配置Bean。同时,我们解析了构造器注入与Setter注入的区别,以及自动装配与作用域管理的实现方式。最后通过一个完整的实战案例,演示了如何使用Spring管理业务组件,并验证了IoC和DI的实际效果。

下一章将进入Spring AOP的深入讲解,进一步探讨如何利用面向切面编程实现日志记录、权限控制等功能。

3. Spring AOP面向切面编程实现

面向切面编程(Aspect-Oriented Programming,AOP)是Spring框架中极为重要的特性之一,它通过将横切关注点(如日志、安全、事务管理等)从核心业务逻辑中分离出来,实现系统的高内聚、低耦合。本章将深入讲解Spring AOP的基本概念、实现方式、常见应用场景,并通过一个日志切面模块的实战案例,帮助读者掌握如何在实际项目中使用AOP进行模块化开发。

3.1 AOP的基本概念

3.1.1 切面、连接点与通知类型

在AOP中,以下几个核心概念是理解其工作原理的关键:

  • 切面(Aspect) :模块化的横切关注点,通常是一个类,包含多个通知(Advice)和切入点(Pointcut)。
  • 连接点(Joinpoint) :程序执行过程中的某个具体步骤,例如方法调用或异常抛出。
  • 通知(Advice) :切面在特定连接点上执行的动作,分为以下几种类型:
  • @Before :在方法执行前执行。
  • @After :在方法执行后执行(无论是否抛出异常)。
  • @AfterReturning :在方法成功返回后执行。
  • @AfterThrowing :在方法抛出异常后执行。
  • @Around :在方法执行前后都可执行,是最强大的通知类型。

  • 切入点(Pointcut) :匹配连接点的表达式,用于指定切面在哪些连接点上生效。

  • 引入(Introduction) :为现有类添加新的方法或属性。
  • 织入(Weaving) :将切面与目标对象结合,生成代理对象的过程。可以在编译期、类加载期或运行期完成。

以下是一个简单的Mermaid流程图,展示AOP的工作流程:

graph TD
    A[目标类] -->|调用方法| B(代理对象)
    B --> C{切面是否匹配}
    C -->|是| D[执行前置通知]
    D --> E[调用目标方法]
    E --> F{是否抛出异常}
    F -->|否| G[执行后置通知]
    F -->|是| H[执行异常通知]
    G --> I[返回结果]
    H --> J[抛出异常]

3.1.2 切入点表达式语法

Spring AOP使用AspectJ的切入点表达式语言来定义匹配规则。常见的表达式包括:

  • execution(* com.example.service.*.*(..)) :匹配 com.example.service 包下所有类的所有方法。
  • within(com.example.service.*) :限定在某个包下的所有方法。
  • args(String, ..) :匹配参数为String开头的方法。
  • @annotation(com.example.annotation.Log) :匹配被特定注解标记的方法。

例如,定义一个匹配所有 com.example.service 包下方法的切入点:

@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}

3.2 Spring AOP的实现方式

3.2.1 基于XML配置的AOP

虽然注解方式更为简洁,但在某些项目中,仍然会使用XML配置来定义AOP切面。以下是基于XML配置的AOP示例:

<!-- applicationContext.xml -->
<aop:config>
    <aop:aspect id="loggingAspect" ref="logAspect">
        <!-- 定义切入点 -->
        <aop:pointcut id="serviceMethods" expression="execution(* com.example.service.*.*(..))" />
        <!-- 前置通知 -->
        <aop:before method="beforeAdvice" pointcut-ref="serviceMethods" />
        <!-- 后置通知 -->
        <aop:after method="afterAdvice" pointcut-ref="serviceMethods" />
    </aop:aspect>
</aop:config>

<bean id="logAspect" class="com.example.aspect.LoggingAspect" />

对应的Java类:

public class LoggingAspect {
    public void beforeAdvice() {
        System.out.println("Before method call");
    }

    public void afterAdvice() {
        System.out.println("After method call");
    }
}

参数说明:

  • <aop:config> :AOP配置的根元素。
  • <aop:aspect> :定义切面, ref 指向具体的切面Bean。
  • <aop:pointcut> :定义切入点, expression 为切入点表达式。
  • <aop:before> :定义前置通知, method 指定切面中的方法名。

3.2.2 基于@Aspect注解的切面开发

使用注解方式是当前主流做法,代码更简洁、可读性更强。以下是一个使用 @Aspect 注解的切面类示例:

@Aspect
@Component
public class LoggingAspect {

    // 定义切入点:匹配service包下所有方法
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceLayer() {}

    // 前置通知
    @Before("serviceLayer()")
    public void beforeMethod(JoinPoint joinPoint) {
        System.out.println("Before method: " + joinPoint.getSignature().getName());
    }

    // 后置通知(无论是否抛出异常)
    @After("serviceLayer()")
    public void afterMethod(JoinPoint joinPoint) {
        System.out.println("After method: " + joinPoint.getSignature().getName());
    }

    // 方法返回后通知
    @AfterReturning(pointcut = "serviceLayer()", returning = "result")
    public void afterReturn(JoinPoint joinPoint, Object result) {
        System.out.println("Method returned: " + result);
    }

    // 方法抛出异常后通知
    @AfterThrowing(pointcut = "serviceLayer()", throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
        System.out.println("Exception in method: " + ex.getMessage());
    }

    // 环绕通知
    @Around("serviceLayer()")
    public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        System.out.println("Start method: " + joinPoint.getSignature().getName());
        Object result = joinPoint.proceed(); // 执行目标方法
        long endTime = System.currentTimeMillis();
        System.out.println("End method: " + joinPoint.getSignature().getName() + ", Time taken: " + (endTime - startTime) + "ms");
        return result;
    }
}

代码逻辑分析:

  • @Aspect :声明该类为一个切面。
  • @Component :将该类交给Spring容器管理。
  • @Pointcut :定义切入点,匹配 service 包下所有方法。
  • @Before @After @AfterReturning @AfterThrowing @Around :分别定义不同类型的切面通知。
  • aroundMethod 中调用 proceed() 方法执行目标方法,并记录执行时间。

3.3 常见应用场景

3.3.1 日志记录、性能监控

AOP最常见的用途之一是记录方法调用日志和性能监控。例如,通过环绕通知可以记录每个方法的执行时间,便于后续性能分析。

日志记录示例:

@Around("serviceLayer()")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
    long start = System.currentTimeMillis();
    Object result = joinPoint.proceed();
    long executionTime = System.currentTimeMillis() - start;
    System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
    return result;
}

性能监控表格:

方法名 调用次数 平均执行时间 最大执行时间
userService.saveUser() 100次 12ms 45ms
orderService.processOrder() 80次 23ms 67ms

3.3.2 权限控制与事务处理

AOP也常用于权限校验和事务控制。例如,在执行某个方法前检查用户是否有权限,或者在方法调用前后开启/提交事务。

权限控制示例:

@Before("execution(* com.example.service.AdminService.*(..))")
public void checkPermission(JoinPoint joinPoint) {
    String methodName = joinPoint.getSignature().getName();
    if (!hasAdminAccess()) {
        throw new AccessDeniedException("User does not have permission to call " + methodName);
    }
}

事务处理流程图:

graph TD
    A[调用service方法] --> B{是否需要事务}
    B -->|是| C[开启事务]
    C --> D[执行业务逻辑]
    D --> E{是否抛出异常}
    E -->|否| F[提交事务]
    E -->|是| G[回滚事务]
    F --> H[返回结果]
    G --> I[抛出异常]

3.4 实战:编写日志切面模块

3.4.1 定义切面类并实现通知逻辑

我们将创建一个完整的日志切面模块,用于记录系统中所有Service层方法的调用日志、执行时间以及异常信息。

步骤1:创建切面类

@Aspect
@Component
public class LoggingAspect {

    private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);

    // 定义切入点:匹配所有service包下的方法
    @Pointcut("execution(* com.example.service..*.*(..))")
    public void serviceLayer() {}

    // 前置通知:记录方法开始
    @Before("serviceLayer()")
    public void beforeServiceMethod(JoinPoint joinPoint) {
        logger.info("Entering method: {}", joinPoint.getSignature().getName());
    }

    // 环绕通知:记录方法执行时间
    @Around("serviceLayer()")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long executionTime = System.currentTimeMillis() - start;
        logger.info("Method {} executed in {} ms", joinPoint.getSignature().getName(), executionTime);
        return result;
    }

    // 后置通知:记录方法结束
    @After("serviceLayer()")
    public void afterServiceMethod(JoinPoint joinPoint) {
        logger.info("Exiting method: {}", joinPoint.getSignature().getName());
    }

    // 异常通知:记录异常信息
    @AfterThrowing(pointcut = "serviceLayer()", throwing = "ex")
    public void handleException(JoinPoint joinPoint, Throwable ex) {
        logger.error("Exception in method {}: {}", joinPoint.getSignature().getName(), ex.getMessage());
    }
}

代码逻辑分析:

  • 使用 @Pointcut 统一定义切入点,避免重复表达式。
  • 使用 @Before 记录方法进入。
  • 使用 @Around 记录执行时间。
  • 使用 @After 记录方法退出。
  • 使用 @AfterThrowing 捕获异常并记录错误日志。

3.4.2 测试AOP功能的运行效果

步骤1:创建一个Service类

@Service
public class UserService {

    public void createUser(String username) {
        System.out.println("Creating user: " + username);
        // 模拟耗时操作
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

步骤2:编写测试类

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {

    @Autowired
    private UserService userService;

    @Test
    public void testCreateUser() {
        userService.createUser("john_doe");
    }
}

测试输出日志示例:

INFO  LoggingAspect - Entering method: createUser
Creating user: john_doe
INFO  LoggingAspect - Method createUser executed in 102 ms
INFO  LoggingAspect - Exiting method: createUser

通过本章的学习,读者应能够深入理解Spring AOP的核心概念与实现机制,掌握基于XML和注解的AOP开发方式,并能够在实际项目中灵活运用AOP进行日志记录、性能监控、权限控制和事务管理等横切逻辑的封装与复用。

4. Hibernate ORM映射原理与实现

4.1 Hibernate框架概述

4.1.1 ORM技术的基本原理

Hibernate 是一个基于 Java 的持久层框架,其核心在于实现了 ORM(Object Relational Mapping,对象关系映射)技术。ORM 是一种程序设计技术,用于将面向对象语言中的对象模型与关系型数据库中的数据模型进行映射。

在传统的数据库访问中,开发者需要手动编写 SQL 语句来操作数据库,而在 ORM 框架中,开发人员只需操作 Java 对象,框架会自动将这些对象映射为数据库中的表,并将对象属性映射为表中的字段。

ORM 的核心优势包括:

优势 说明
提高开发效率 无需手动编写大量 SQL 语句,减少数据库访问代码量
提升代码可维护性 数据访问逻辑与业务逻辑分离,易于维护
提升数据库可移植性 通过 ORM 抽象,屏蔽底层数据库差异
减少错误率 自动管理 SQL 语句生成,降低 SQL 编写错误

4.1.2 Hibernate的核心接口与工作流程

Hibernate 框架的核心接口包括:

  • SessionFactory :用于创建 Session 对象,是线程安全的,通常一个应用中只初始化一次。
  • Session :代表与数据库的一次会话,用于执行持久化操作。
  • Transaction :事务管理接口,用于控制事务的提交与回滚。
  • Query Criteria :用于执行查询操作。
  • Configuration :用于加载 Hibernate 配置文件和映射文件。

Hibernate 的典型工作流程如下:

graph TD
    A[Configuration 加载配置文件] --> B[创建 SessionFactory]
    B --> C[打开 Session]
    C --> D[开启 Transaction]
    D --> E{执行持久化操作}
    E --> F[保存、更新、删除、查询]
    F --> G[提交 Transaction]
    G --> H[关闭 Session]

通过这一流程,Hibernate 实现了对数据库操作的封装,使得开发者只需操作 Java 对象即可完成对数据库的增删改查。

4.2 映射文件的编写

4.2.1 POJO与数据库表的映射关系

Hibernate 通过映射文件(如 hbm.xml )将 Java 类(POJO)与数据库表进行关联。一个基本的映射文件结构如下:

<!-- User.hbm.xml -->
<hibernate-mapping>
    <class name="com.example.model.User" table="users">
        <id name="id" column="user_id">
            <generator class="native"/>
        </id>
        <property name="username" column="username"/>
        <property name="email" column="email"/>
    </class>
</hibernate-mapping>

参数说明:

  • name="com.example.model.User" :指定对应的 Java 类。
  • table="users" :指定对应的数据库表名。
  • <id> :定义主键映射。
  • name="id" :Java 类中的属性名。
  • column="user_id" :数据库表中的主键字段名。
  • <generator class="native"/> :主键生成策略, native 表示根据数据库选择合适的生成方式(如 MySQL 使用 auto_increment ,Oracle 使用序列)。
  • <property> :定义普通属性与字段的映射。

逻辑分析:

该映射文件的作用是将 User 类中的 id username email 属性分别映射到数据库表 users 中的 user_id username email 字段。Hibernate 在执行持久化操作时,会根据该映射文件自动生成 SQL 语句。

4.2.2 主键生成策略与关系映射

Hibernate 支持多种主键生成策略,常用的包括:

策略 说明
native 自动选择最适合当前数据库的主键生成方式
identity 使用数据库自增主键(如 MySQL)
sequence 使用序列(如 Oracle)
uuid 使用 UUID 字符串作为主键
assigned 手动分配主键值

例如,使用 uuid 策略生成主键:

<id name="id" column="user_id">
    <generator class="uuid"/>
</id>

此外,Hibernate 还支持复杂的关系映射,如一对多、多对一、一对一、多对多等。以一对多为例:

<set name="orders" inverse="true" lazy="true" fetch="select">
    <key column="user_id"/>
    <one-to-many class="com.example.model.Order"/>
</set>

参数说明:

  • name="orders" :对应 Java 类中的集合属性。
  • column="user_id" :外键字段名。
  • class="com.example.model.Order" :关联的实体类。

通过上述映射配置,Hibernate 可以自动处理对象之间的关联关系,实现级联操作。

4.3 注解方式实现ORM

4.3.1 使用@Entity与@Mapped annotations

除了 XML 映射文件,Hibernate 也支持使用注解方式进行 ORM 映射。这种方式更加简洁,且将映射信息直接嵌入到 Java 类中。

一个使用注解的实体类示例如下:

import javax.persistence.*;

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "user_id")
    private Long id;

    @Column(name = "username")
    private String username;

    @Column(name = "email")
    private String email;

    // Getters and Setters
}

参数说明:

  • @Entity :声明该类为实体类。
  • @Table(name = "users") :指定对应的数据库表名。
  • @Id :标识该属性为主键。
  • @GeneratedValue(strategy = GenerationType.IDENTITY) :设置主键生成策略为数据库自增。
  • @Column(name = "username") :指定对应的数据库字段名。

逻辑分析:

该类使用 JPA 注解将 User 类映射到 users 表,并定义了字段与属性的对应关系。Hibernate 在启动时会扫描这些注解,自动生成相应的数据库操作逻辑。

4.3.2 复杂映射(一对一、一对多、多对多)

以一对多为例,使用注解实现用户与订单的一对多关系:

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToMany(mappedBy = "user", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    private Set<Order> orders = new HashSet<>();

    // Getters and Setters
}
@Entity
@Table(name = "orders")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;

    // Getters and Setters
}

参数说明:

  • @OneToMany(mappedBy = "user") :表示一对多关系, mappedBy 指定对方类中的关联属性。
  • fetch = FetchType.LAZY :延迟加载,避免一次性加载大量数据。
  • cascade = CascadeType.ALL :级联操作,当操作用户时,会同步操作其关联的订单。
  • @JoinColumn(name = "user_id") :指定外键字段。

逻辑分析:

通过上述注解,Hibernate 会自动在数据库中建立外键约束,并在查询时自动加载关联数据。例如,当查询一个 User 对象时,其 orders 属性会被延迟加载,只有在访问时才会从数据库中获取。

4.4 实战:完成用户实体类与数据库映射

4.4.1 配置Hibernate核心文件

在 Hibernate 的核心配置文件 hibernate.cfg.xml 中,需要配置数据库连接信息、方言、映射类等:

<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <!-- Database connection settings -->
        <property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/mydb</property>
        <property name="connection.username">root</property>
        <property name="connection.password">password</property>

        <!-- JDBC connection pool (using the built-in) -->
        <property name="connection.pool_size">1</property>

        <!-- SQL dialect -->
        <property name="dialect">org.hibernate.dialect.MySQL8Dialect</property>

        <!-- Enable Hibernate's automatic session context management -->
        <property name="current_session_context_class">thread</property>

        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>

        <!-- Drop and re-create the database schema on startup -->
        <property name="hbm2ddl.auto">update</property>

        <!-- Mapping class -->
        <mapping class="com.example.model.User"/>
    </session-factory>
</hibernate-configuration>

参数说明:

  • driver_class :数据库驱动类。
  • url :数据库连接地址。
  • username password :数据库登录凭证。
  • dialect :指定数据库方言,用于生成特定数据库的 SQL。
  • hbm2ddl.auto :数据库自动建表策略, update 表示自动更新表结构。
  • mapping class :指定要映射的实体类。

4.4.2 编写测试类验证持久化操作

下面是一个简单的测试类,用于验证用户实体的持久化操作:

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

public class UserTest {
    public static void main(String[] args) {
        // 加载配置并创建 SessionFactory
        Configuration configuration = new Configuration().configure();
        SessionFactory sessionFactory = configuration.buildSessionFactory();

        // 打开 Session 并开启事务
        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();

        // 创建用户对象并保存
        User user = new User();
        user.setUsername("john_doe");
        user.setEmail("john@example.com");

        session.save(user);

        // 提交事务并关闭 Session
        transaction.commit();
        session.close();
        sessionFactory.close();
    }
}

逻辑分析:

  1. 使用 Configuration 加载 hibernate.cfg.xml 配置文件。
  2. 创建 SessionFactory ,它是线程安全的,整个应用中通常只创建一次。
  3. 打开 Session ,它代表与数据库的一次会话。
  4. 开启事务,Hibernate 所有操作都应在事务中进行。
  5. 创建 User 对象并调用 session.save() 方法进行保存。
  6. 提交事务,确保数据写入数据库。
  7. 关闭 Session SessionFactory ,释放资源。

通过上述测试类,我们可以验证 Hibernate 是否正确地将 Java 对象映射到数据库,并完成持久化操作。

5. Struts2 MVC架构配置与Action处理

Struts2 是基于 MVC(Model-View-Controller)架构模式的 Java Web 框架,它通过清晰的分层结构实现请求处理、业务逻辑与页面渲染的分离。本章将深入讲解 Struts2 的 MVC 架构原理、Action 的定义与配置方式、数据封装与类型转换机制,并通过一个完整的用户登录模块实战,帮助读者掌握其在实际项目中的应用。

5.1 Struts2框架概述

Struts2 作为 Struts 框架的演进版本,融合了 WebWork 的特性,提供了更加灵活和强大的 MVC 架构支持。其核心设计围绕 Action、Interceptor、Result 等组件展开,能够有效处理 HTTP 请求并驱动业务逻辑的执行。

5.1.1 MVC设计模式与Struts2的实现

MVC(Model-View-Controller)是一种广泛应用于 Web 开发的设计模式,其核心思想是将应用程序分为三个部分:

  • Model(模型) :负责处理数据和业务逻辑,通常由 JavaBean 或 POJO 实现。
  • View(视图) :负责数据的展示,通常由 JSP、HTML 或 JSON 实现。
  • Controller(控制器) :负责接收请求、调用模型并选择视图返回结果。

在 Struts2 中, Action 类扮演 Controller 的角色,接收用户的请求并处理业务逻辑; Result 配置定义了视图跳转的逻辑;而 Model 则通常由 Hibernate 或其他 ORM 框架处理数据持久化任务。

Struts2 MVC 请求处理流程图如下:

graph TD
    A[客户端请求] --> B(FilterDispatcher)
    B --> C[配置加载]
    C --> D[确定Action类]
    D --> E[调用拦截器]
    E --> F[执行Action]
    F --> G[返回Result]
    G --> H[渲染视图]
    H --> I[响应返回客户端]

该流程展示了 Struts2 如何通过一系列组件完成请求的解析、处理与响应。

5.1.2 核心组件与请求处理流程

Struts2 的核心组件包括:

  • ActionMapper :将 HTTP 请求映射到对应的 Action。
  • ActionProxy :封装 Action 的执行过程,负责调用拦截器链和 Action。
  • Interceptor :拦截请求,在 Action 执行前后进行预处理或后处理。
  • ValueStack :用于存储 Action 的属性,支持在 JSP 页面中通过 OGNL 表达式访问。
  • Result :决定 Action 执行后的视图跳转方式,如 JSP、FreeMarker、JSON 等。

请求处理流程详解:

  1. 客户端发送 HTTP 请求到服务器。
  2. Struts2 的核心过滤器 StrutsPrepareAndExecuteFilter 拦截请求。
  3. 通过 ActionMapper 确定要执行的 Action 类。
  4. 创建 ActionProxy 并加载配置。
  5. 执行拦截器链(如日志记录、权限验证等)。
  6. 调用 Action 的 execute() 方法处理业务逻辑。
  7. 根据返回值选择对应的 Result ,如 "success" "input" 等。
  8. 渲染视图并返回响应给客户端。

Struts2 的这一流程设计使得开发者可以灵活地控制请求生命周期,便于进行日志记录、权限控制、事务管理等操作。

5.2 Action的定义与配置

在 Struts2 中,Action 是处理请求的核心单元,它通常是一个 Java 类,包含一个 execute() 方法,返回字符串表示视图跳转路径。

5.2.1 编写Action类并配置struts.xml

步骤 1:创建 Action 类

package com.example.action;

import com.opensymphony.xwork2.ActionSupport;

public class LoginAction extends ActionSupport {
    private String username;
    private String password;

    public String execute() {
        if ("admin".equals(username) && "123456".equals(password)) {
            return SUCCESS;
        } else {
            return ERROR;
        }
    }

    // Getter 和 Setter 方法
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

逻辑分析:

  • 该类继承自 ActionSupport ,提供了默认的 execute() 方法。
  • execute() 方法用于处理业务逻辑,返回 "SUCCESS" "ERROR"
  • username password 是表单提交的数据字段,需要提供 Getter 和 Setter 方法供 Struts2 自动注入。

步骤 2:配置 struts.xml 文件

<?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="default" extends="struts-default">
        <action name="login" class="com.example.action.LoginAction">
            <result name="success">/welcome.jsp</result>
            <result name="error">/login.jsp</result>
        </action>
    </package>
</struts>

参数说明:

  • <action> :定义一个 Action, name 属性对应请求路径, class 指定 Action 类。
  • <result> :定义返回值对应的视图路径, name 应与 Action 返回值一致。

5.2.2 使用注解简化Action配置

Struts2 支持使用注解代替 struts.xml 的配置方式,提高开发效率。

修改 LoginAction 类:

package com.example.action;

import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Result;

@Namespace("/")
public class LoginAction extends ActionSupport {

    private String username;
    private String password;

    @Action(value = "login", results = {
        @Result(name = "success", location = "/welcome.jsp"),
        @Result(name = "error", location = "/login.jsp")
    })
    public String execute() {
        if ("admin".equals(username) && "123456".equals(password)) {
            return SUCCESS;
        } else {
            return ERROR;
        }
    }

    // Getter 和 Setter 方法
}

逻辑分析:

  • @Namespace :定义 Action 的命名空间。
  • @Action :定义请求路径和返回结果。
  • @Result :指定视图跳转路径。

这种方式无需额外的 XML 配置,适合模块化开发,提高代码可读性和维护性。

5.3 数据封装与类型转换

Struts2 提供了强大的数据自动封装机制,能够将表单数据自动映射到 Action 的属性中,同时支持自定义类型转换器。

5.3.1 使用ModelDriven接口绑定数据

当表单数据较多时,建议使用 ModelDriven 接口将数据封装到一个 JavaBean 中。

示例:

package com.example.model;

public class User {
    private String username;
    private String password;

    // Getter 和 Setter 方法
}

修改 Action 类:

package com.example.action;

import com.example.model.User;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;

public class UserAction extends ActionSupport implements ModelDriven<User> {
    private User user = new User();

    @Override
    public User getModel() {
        return user;
    }

    public String execute() {
        System.out.println("用户名:" + user.getUsername());
        System.out.println("密码:" + user.getPassword());
        return SUCCESS;
    }
}

逻辑分析:

  • ModelDriven 接口允许 Action 返回一个模型对象。
  • Struts2 会自动将表单数据填充到该模型对象中,避免 Action 类属性过多。

5.3.2 自定义类型转换器实现

当表单提交的数据类型与 Action 中定义的类型不一致时,可以通过自定义类型转换器进行处理。

步骤 1:创建类型转换器类

package com.example.converter;

import com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter;
import java.util.Map;

public class StringToUserConverter extends DefaultTypeConverter {
    @Override
    public Object convertValue(Map<String, Object> context, Object value, Class toType) {
        if (toType == User.class) {
            String[] params = (String[]) value;
            User user = new User();
            String[] data = params[0].split(",");
            user.setUsername(data[0]);
            user.setPassword(data[1]);
            return user;
        }
        return null;
    }
}

步骤 2:注册类型转换器

xwork-conversion.properties 文件中注册:

com.example.model.User=com.example.converter.StringToUserConverter

逻辑分析:

  • 该转换器将字符串按逗号分隔,构造 User 对象。
  • 通过 xwork-conversion.properties 文件注册,Struts2 会在需要时自动调用。

5.4 实战:实现用户登录模块

在本节中,我们将整合前面的知识,实现一个完整的用户登录模块。

5.4.1 接收用户输入并处理业务逻辑

login.jsp 页面:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
    <title>用户登录</title>
</head>
<body>
    <form action="login" method="post">
        用户名:<input type="text" name="username"><br>
        密码:<input type="password" name="password"><br>
        <input type="submit" value="登录">
    </form>
</body>
</html>

LoginAction.java 处理逻辑:

public class LoginAction extends ActionSupport {
    private String username;
    private String password;

    public String execute() {
        if ("admin".equals(username) && "123456".equals(password)) {
            return SUCCESS;
        } else {
            addActionError("用户名或密码错误");
            return ERROR;
        }
    }

    // Getter 和 Setter 方法
}

5.4.2 页面跳转与错误处理机制

struts.xml 配置:

<action name="login" class="com.example.action.LoginAction">
    <result name="success">/welcome.jsp</result>
    <result name="error">/login.jsp</result>
</action>

welcome.jsp 页面:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
    <title>欢迎页面</title>
</head>
<body>
    <h2>登录成功,欢迎 ${username}!</h2>
</body>
</html>

错误处理增强:

可以在 JSP 页面中显示错误信息:

<s:actionerror />

逻辑分析:

  • 登录成功跳转至 welcome.jsp ,并显示用户名。
  • 登录失败则返回 login.jsp ,并通过 addActionError() 方法显示错误信息。

本章详细讲解了 Struts2 的 MVC 架构、Action 的定义与配置方式、数据封装与类型转换机制,并通过一个完整的用户登录模块实战演示了其在实际开发中的应用。下一章将介绍 Spring 与 Hibernate 的整合,进一步构建完整的 SSH 企业级开发体系。

6. Spring与Hibernate整合实现

6.1 整合Spring与Hibernate的意义

Spring与Hibernate的整合是Java企业级应用开发中极为常见且关键的环节。Spring作为核心容器,负责管理对象的生命周期和依赖注入,而Hibernate作为ORM框架,专注于数据库的持久化操作。通过整合,可以实现以下优势:

6.1.1 Spring对Hibernate的集成支持

Spring为Hibernate提供了良好的支持,包括:

  • SessionFactory的管理 :Spring可以管理Hibernate的 SessionFactory ,避免手动创建和关闭。
  • 事务管理 :Spring提供声明式事务管理,通过AOP实现事务的自动提交与回滚。
  • DAO层解耦 :通过依赖注入,DAO层与Service层之间的耦合度降低,提升代码可维护性。

6.1.2 使用Spring管理Hibernate会话

通过Spring管理Hibernate的会话(Session),可以有效避免手动打开/关闭Session的繁琐操作,并确保Session在事务中被正确使用。

// 示例:Spring管理Hibernate的DAO类
@Repository
public class UserDao {

    @Autowired
    private SessionFactory sessionFactory;

    public void save(User user) {
        sessionFactory.getCurrentSession().save(user);
    }
}

说明 @Repository 注解将该类标识为Spring的DAO组件, @Autowired 自动注入 SessionFactory getCurrentSession() 方法由Spring管理生命周期。

6.2 数据源与SessionFactory配置

整合Spring与Hibernate的核心步骤是配置数据源和SessionFactory。通常使用C3P0连接池来管理数据库连接,提升系统性能。

6.2.1 配置C3P0连接池

在Spring配置文件中(如 applicationContext.xml ),配置C3P0连接池如下:

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mydb"/>
    <property name="user" value="root"/>
    <property name="password" value="password"/>
    <property name="initialPoolSize" value="5"/>
    <property name="maxPoolSize" value="20"/>
</bean>

参数说明
- driverClass :数据库驱动类。
- jdbcUrl :数据库连接地址。
- user/password :数据库账号密码。
- initialPoolSize/maxPoolSize :连接池初始和最大连接数。

6.2.2 使用LocalSessionFactoryBean创建SessionFactory

配置Hibernate的 SessionFactory ,使用Spring的 LocalSessionFactoryBean 类:

<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="packagesToScan" value="com.example.model"/>
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQL8Dialect</prop>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.hbm2ddl.auto">update</prop>
        </props>
    </property>
</bean>

参数说明
- packagesToScan :指定Hibernate实体类所在的包。
- hibernate.dialect :数据库方言,适配MySQL 8。
- hibernate.show_sql :是否显示生成的SQL语句。
- hibernate.hbm2ddl.auto :自动更新数据库结构。

6.3 事务管理机制整合

事务管理是整合Spring与Hibernate的核心功能之一。Spring提供了声明式事务管理,极大简化了事务控制逻辑。

6.3.1 声明式事务管理配置

在Spring配置文件中启用事务管理:

<tx:annotation-driven transaction-manager="transactionManager"/>

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

说明
- <tx:annotation-driven> 启用基于注解的事务管理。
- HibernateTransactionManager 是Spring对Hibernate事务的支持类。

6.3.2 使用@Transactional注解控制事务

在Service层使用 @Transactional 注解进行事务控制:

@Service
@Transactional
public class UserService {

    @Autowired
    private UserDao userDao;

    public void addUser(User user) {
        userDao.save(user);
    }
}

执行逻辑说明
- 当调用 addUser() 方法时,Spring会自动开启事务。
- 若方法执行成功,则事务提交。
- 若发生异常,事务将自动回滚。

6.4 实战:用户信息的增删改查整合模块

本节将演示如何整合Spring与Hibernate,实现用户信息的CRUD操作。

6.4.1 编写DAO层与Service层代码

User实体类(使用JPA注解)

@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private String email;

    // Getter and Setter
}

UserDao类(DAO层)

@Repository
public class UserDao {

    @Autowired
    private SessionFactory sessionFactory;

    public void save(User user) {
        sessionFactory.getCurrentSession().save(user);
    }

    public User getById(Long id) {
        return sessionFactory.getCurrentSession().get(User.class, id);
    }

    public void update(User user) {
        sessionFactory.getCurrentSession().update(user);
    }

    public void delete(User user) {
        sessionFactory.getCurrentSession().delete(user);
    }
}

UserService类(Service层)

@Service
@Transactional
public class UserService {

    @Autowired
    private UserDao userDao;

    public void createUser(User user) {
        userDao.save(user);
    }

    public User getUserById(Long id) {
        return userDao.getById(id);
    }

    public void updateUser(User user) {
        userDao.update(user);
    }

    public void deleteUser(User user) {
        userDao.delete(user);
    }
}

6.4.2 使用Spring管理事务并测试功能

编写测试类

public class UserTest {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = context.getBean(UserService.class);

        User user = new User();
        user.setName("张三");
        user.setEmail("zhangsan@example.com");

        // 新增用户
        userService.createUser(user);
        System.out.println("用户添加成功");

        // 查询用户
        User retrievedUser = userService.getUserById(user.getId());
        System.out.println("查询到用户:" + retrievedUser.getName());

        // 更新用户
        retrievedUser.setName("李四");
        userService.updateUser(retrievedUser);
        System.out.println("用户信息更新成功");

        // 删除用户
        userService.deleteUser(retrievedUser);
        System.out.println("用户删除成功");
    }
}

执行流程说明
- 通过Spring容器获取 UserService 实例。
- 调用方法执行增删改查操作。
- 所有操作在Spring事务管理下执行,保证数据一致性。

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

简介:SSH框架由Spring、Struts2和Hibernate三大开源框架组成,是Java Web开发中的经典组合。本教学代码提供完整的SSH整合实战资源,涵盖Spring的依赖注入与事务管理、Struts2的MVC请求处理、Hibernate的ORM持久化操作,并通过实际业务场景如用户注册、商品管理等演示如何将三者集成开发。适合初学者深入理解MVC架构、DI/AOP编程思想和ORM映射机制,也适合有经验开发者作为项目参考手册。


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

更多推荐