Spring-_-Bear 的 CSDN 博客导航

一、创建 Web 工程

  1. 使用 IntelliJ IDEA 开发工具新建一个工程,项目类型选择 Maven,然后点击 Next。若配置过程中发现自己的图标与图片中的不一致,那是因为笔者使用了图标插件的原因,无需在意。

    在这里插入图片描述

  2. 填入 Maven 工程必要信息,如工程名、组织名和模块名等,而后点击 Finish。此处我们的工程和模块名称均为 uims,意为用户信息管理系统(User Information Management System)。

    在这里插入图片描述

  3. 将上一步新建的 Maven 工程改造为 Web 工程。在 IDEA 中依次点击 File => Project Structure(或使用快捷键:Ctrl + Shift + Alt + S)进入工程结构设置页面。

    在这里插入图片描述

  4. 在工程结构设置页面中依次点击 Facets => + => Web,而后将弹出当前工程下的所有模块。

    在这里插入图片描述

  5. 选择自己的刚刚创建的 Maven 工程 uims,点击 OK。

    在这里插入图片描述

  6. 为 Web 工程添加核心配置文件 web.xml,若 web.xml 不存在则点击 + 号新建即可。

    在这里插入图片描述

  7. 修改 web.xml 的文件路径为 src\main\webapp\WEB-INFO\web.xml,即将原有路径中的 web 修改为 src\main\webapp。

    在这里插入图片描述

  8. 指定当前 Web 工程的上下文路径为 webapp 目录,即将原有路径中的 web 修改为 src\main\webapp,而后一直点击 OK 即可。

    在这里插入图片描述

至此,标准目录格式的 Web 工程创建完成,下一步将引入 Maven 工程依赖,而后进行 Spring、Spring MVC 和 Mybatis 框架的整合。为方便后续操作,此处在 pom.xml 中一次性引入所有相关依赖,各依赖的作用见注释。

pom.xml 的文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.edu.whut.springbear</groupId>
    <artifactId>uims</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <!-- Spring 版本号统一管理 -->
        <spring.version>5.3.1</spring.version>
    </properties>

    <dependencies>
        <!-- Spring MVC -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!-- Spring JDBC -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!-- Spring Test -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>

        <!-- JUnit 单元测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <!-- Mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>
        <!-- Spring 整合 MyBatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.2</version>
        </dependency>
        <!-- MySQL 驱动文件 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.27</version>
        </dependency>
        <!-- 阿里巴巴德鲁伊数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.15</version>
        </dependency>

        <!-- Servlet API -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <!-- Spring5 整合 Thymeleaf -->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
            <version>3.0.12.RELEASE</version>
        </dependency>
        <!-- Jackson -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.9.8</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.8</version>
        </dependency>
        <!-- Lombok 简化 POJO 类开发 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
            <scope>provided</scope>
        </dependency>
        <!-- 日志 -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
        <!-- 文件上传 -->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.1</version>
        </dependency>
    </dependencies>

</project>

二、配置 web.xml

在 src\main\webapp\WEB-INF\web.xml 中配置 Spring IOC 容器、编码过滤器、HTTP 请求方法过滤器和 Spring MVC 的前端控制器等。

注:编码过滤器应配置为第一个过滤器,否则将出现中文乱码情况,因为过滤器的按配置顺序依次拦截执行。

web.xml 文件内容如下:

<?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_4_0.xsd"
         version="4.0">

    <!-- Spring IOC 容器 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>

    <!-- 编码过滤器解决中文乱码 -->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- Http 请求方法过滤器以支持 RESTful 风格编程 -->
    <filter>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- SpringMVC 前端控制器 -->
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc-config.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

如下图所示,此时 IDE 中将会出现两处 “爆红” 现象,本文后续内容中若无特殊说明,否则无需在意 IDE 出现的 “爆红” 现象,因为该情况会在后续的配置过程中自动消失。

在这里插入图片描述

在 src\main\resources 目录下新建 Spring 的核心配置文件 applicationContext.xml 和 Spring MVC 的核心配置文件 spring-mvc-config.xml。

Spring 的核心配置文件 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>

Spring MVC 的核心配置文件 spring-mvc-config.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>

此时再次点击 web.xml 查看文件内容,将会发现之前的 “爆红” 情况已经消失。至此,项目结构如下图所示:

在这里插入图片描述

三、配置 Spring

在 src\main\resources\applicationContext.xml 中进行 Spring 的相关核心配置。如配置组件扫描规则、配置数据源和整合 MyBatis 等。

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: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="cn.edu.whut.springbear.uims.mapper,
            cn.edu.whut.springbear.uims.pojo,
            cn.edu.whut.springbear.uims.service"
    />

    <!-- 引入数据库配置文件 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!-- 数据源 -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.className}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!-- MyBatis SQL 会话工厂实例  -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!-- MyBatis 核心配置文件路径 -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <!-- MyBatis Mapper.xml 文件路径 -->
        <property name="mapperLocations" value="classpath:mapper/*.xml"/>
    </bean>
    <!-- MyBatis Mapper.xml 对应接口的包路径 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <!-- Mapper.xml 对应接口所在包路径 -->
        <property name="basePackage" value="cn.edu.whut.springbear.uims.mapper"/>
    </bean>

</beans>

如下图所示,此时 <context> 标签出现 “爆红” 现象,可通过点击该标签后使用 Alt + Enter 引入 context 名称解决,其余 “爆红” 问题在后续配置中自动消失。

在这里插入图片描述

在 src\main\java 下新建组件扫描规则和 MyBatis XML 文件对应的接口包,需注意包全路径应做到一一对应,如下图所示:

在这里插入图片描述

在 src\main\resources 目录下新建 MySQL 数据库配置文件 jdbc.properties,并填入你自己的数据库连接信息,请务必确保 jdbc.url 中配置的数据库存在。jdbc.properties 的文件内容如下:

jdbc.className=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/uims?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=root

在 src\main\resources 目录下新建 MyBatis 的核心配置文件 mybatis-config.xml,文件内容如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
</configuration>

在 src\main\resources 目录下新建 MyBatis 数据库操作 XML 文件的存储目录,名为 mapper,如下图所示:

在这里插入图片描述

四、配置 Spring MVC

在 src\main\resources\spring-mvc-config.xml 中进行 Spring MVC 的核心配置,如配置控制器扫描路径、静态资源处理器、开启注解驱动、配置视图解析器和视图控制器等。此时出现的标签爆红情况仍可通过点击该标签后,使用 Alt + Enter 引入对应的名称空间解决。

spring-mvc-config.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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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 http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 控制器包路径 -->
    <context:component-scan base-package="cn.edu.whut.springbear.uims.controller"/>

    <!-- 默认 Servlet 处理静态资源 -->
    <mvc:default-servlet-handler/>
    <!-- MVC 注解驱动以支持高级功能 -->
    <mvc:annotation-driven/>

    <!-- Thymeleaf 视图解析器 -->
    <bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
        <property name="order" value="1"/>
        <property name="characterEncoding" value="UTF-8"/>
        <property name="templateEngine">
            <bean class="org.thymeleaf.spring5.SpringTemplateEngine">
                <property name="templateResolver">
                    <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
                        <property name="prefix" value="/WEB-INF/templates/"/>
                        <property name="suffix" value=".html"/>
                        <property name="templateMode" value="HTML5"/>
                        <property name="characterEncoding" value="UTF-8"/>
                    </bean>
                </property>
            </bean>
        </property>
    </bean>

    <!-- 视图控制器,此处 IDE 爆红问题可不予理会 -->
    <mvc:view-controller path="/" view-name="index"/>

</beans>

同 Spring 的配置一样,此时需要在 src\main\java 目录下新建 cn.edu.whut.springbear.uims.controller 包,因为 Spring MVC 的配置文件中扫描了此包路径。需要注意的问题是 controller 包交给 MVC 扫描,其余组件包交给 Spring 扫描,不可在 Spring 的配置文件 applicationContext.xml 重复扫描 controller 包。

在这里插入图片描述

而后在 src\main\webapp\WEB-INF 目录下创建视图文件(HTML)的保存目录 templates,并在 templates 目录中创建首页 index.html 文件,文件内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Spring MVC</title>
</head>
<body>
<h1>Hello, Spring MVC</h1>
</body>
</html>

此时的工程结构如下图所示:

在这里插入图片描述

五、配置 MyBatis

在 Mybatis 的核心配置文件 src\main\resources\mybatis-config.xml 中配置自动驼峰命名转换和以包为单位的 POJO 类别名。

mybatis-config.xml 的文件内容如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <settings>
        <!-- POJO 类与数据库表字段驼峰命名自动转换 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

    <typeAliases>
        <!-- 以包为单位指定 POJO 类别名,默认为类名 -->
        <package name="cn.edu.whut.springbear.uims.pojo"/>
    </typeAliases>

</configuration>

在 cn.edu.whut.springbear.uims.pojo 包中创建用户信息实体类 User,文件内容如下:

package cn.edu.whut.springbear.uims.pojo;

import lombok.Data;

import java.io.Serializable;

/**
 * @author Spring-_-Bear
 * @datetime 2022-06-25 18:05 Saturday
 */
@Data
public class User implements Serializable {
}

在 cn.edu.whut.springbear.uims.mapper 包中新建 User 类对应的持久层操作接口 UserMapper,文件内容如下:

package cn.edu.whut.springbear.uims.mapper;

import org.springframework.stereotype.Repository;

/**
 * @author Spring-_-Bear
 * @datetime 2022-06-25 18:07 Saturday
 */
@Repository
public interface UserMapper {
}

在 src\main\resources\mapper\ 目录下创建 UserMapper 接口对应的 MyBatis Mapper 文件 UserMapper.xml,并在 namespace 标签中指定当前 UserMapper.xml 文件所对应的接口全路径。文件内容如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!-- namespace 指定当前 Mapper.xml 所对应的 Mapper 接口类 -->
<mapper namespace="cn.edu.whut.springbear.uims.mapper.UserMapper"></mapper>

六、部署 Tomcat

  1. 生成 war 包:在项目结构设置中(快捷键:Ctrl + Shift + Alt + S)依据当前模块生成 .war exployded 文件供 Tomcat 部署访问,而后选择当前工程模块 uims,最后一直点击 OK 即可。

    在这里插入图片描述

  2. 在 IDEA 中为当前工程添加 Tomcat Web 服务器。

    在这里插入图片描述

  3. 选择本地 Tomcat 应用(需提前自行安装)。

    在这里插入图片描述

  4. 在 Tomcat 中选择第 1 步中创建的 war 包。

    在这里插入图片描述

  5. 修改 Tomcat 工程上下文路径为 /uims。完成第 4 步之后滚动鼠标滚轮,修改 Application context 的路径为 /uims,如下图所示:

    在这里插入图片描述

  6. 启动 Tomcat,成功访问 index.html。访问路径为 http://localhost:8080/uims/,基础访问路径前缀为 /uims,请务必注意。

    在这里插入图片描述

至此,成功通过 XML 配置文件方式整合 Spring、Spring MVC 和 MyBatis 框架的基本功能,完整的项目结构如下图所示:

在这里插入图片描述

下一步将在前六步的基础上整合 MyBatis 和 Spring MVC 的常用功能,如通过 MyBatis 进行基本的 CRUD 操作,整合 Spring MVC 的拦截器、异常解析器和文件上传解析器等。

七、MyBatis 基本使用

  1. 在 MySQL 控制台创建 uims 数据库,并在 uims 数据库中创建用户基本信息表 user 并插入测试数据。SQL 脚本内容如下:

    -- ----------------------------
    -- Table structure for user
    -- ----------------------------
    DROP TABLE IF EXISTS `user`;
    CREATE TABLE `user`  (
      `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',
      `username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名',
      `password` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '密码',
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
    
    -- ----------------------------
    -- Records of user
    -- ----------------------------
    INSERT INTO `user` VALUES (1, 'spring', 'spring');
    INSERT INTO `user` VALUES (2, 'bear', 'bear');
    INSERT INTO `user` VALUES (3, 'Spring-_-Bear', 'Spring-_-Bear');
    
  2. 修改 cn.edu.whut.springbear.uims.pojo.User 内容,使用 Lombok 注解简化 JavaBean 的开发。修改后的文件内容如下:

    package cn.edu.whut.springbear.uims.pojo;
    
    import lombok.Data;
    
    import java.io.Serializable;
    
    /**
     * @author Spring-_-Bear
     * @datetime 2022-06-25 18:05 Saturday
     */
    @Data
    public class User implements Serializable {
    
        private static final long serialVersionUID = -2525965567162864217L;
    
        private Long id;
        private String username;
        private String password;
    
    }
    
  3. 在 cn.edu.whut.springbear.uims.mapper.UserMapper 接口中添加接口方法,根据用户名和密码查询用户信息。修改后的文件内容如下:

    package cn.edu.whut.springbear.uims.mapper;
    
    import cn.edu.whut.springbear.uims.pojo.User;
    import org.apache.ibatis.annotations.Param;
    import org.springframework.stereotype.Repository;
    
    /**
     * @author Spring-_-Bear
     * @datetime 2022-06-25 18:07 Saturday
     */
    @Repository
    public interface UserMapper {
    
        /**
         * 根据用户名和密码查询用户信息
         *
         * @param username 用户名
         * @param password 密码
         * @return 用户信息或 null
         */
        User getByUsernameAndPassword(@Param("username") String username, @Param("password") String password);
    
    }
    
  4. 在 src\main\resources\mapper\UserMapepr.xml 中创建接口方法对应的 SQL 语句,MyBatis 框架会根据接口方法自动生成代理实现类,从而完成数据库操作。修改后的文件内容如下:

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <!-- namespace 指定当前 Mapper.xml 所对应的 Mapper 接口类 -->
    <mapper namespace="cn.edu.whut.springbear.uims.mapper.UserMapper">
    
        <!-- User getByUsernameAndPassword(@Param("username") String username, @Param("password") String password); -->
        <select id="getByUsernameAndPassword" resultType="User">
            select *
            from user
            where username = #{username}
              and password = #{password}
        </select>
    
    </mapper>
    
  5. 选中 cn.edu.whut.springbear.uims.mapper.UserMapper#getByUsernameAndPassword 接口方法,使用 IDEA 快捷键 Ctrl + Shift + T 为该方法生成单元测试,在弹框中测试框架选择 Junit4,勾选需要进行测试的方法,最后点击 OK。

    在这里插入图片描述

  6. 生成的测试类位于 src\test\cn.edu.whut.springbear.uims.mapper.UserMapperTest 目录下,在 UserMapperTest 中使用 Spring 的方式进行测试,即指定 Spring 核心配置文件和 Spring 整合的 Junit4 测试类,文件内容如下:

    package cn.edu.whut.springbear.uims.mapper;
    
    import cn.edu.whut.springbear.uims.pojo.User;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    /**
     * @author Spring-_-Bear
     * @datetime 2022-06-25 22:12 Saturday
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class UserMapperTest {
    
        @Autowired
        private UserMapper userMapper;
    
        @Test
        public void getByUsernameAndPassword() {
            User user = userMapper.getByUsernameAndPassword("bear", "bear");
            System.out.println(user);
        }
    
    }
    
  7. 运行测试方法 cn.edu.whut.springbear.uims.mapper.UserMapperTest#getByUsernameAndPassword,测试结果如下图所示:

    在这里插入图片描述

八、Spring MVC 拦截器

  1. 在 cn.edu.whut.springbear.uims.interceptor 包下新建登录拦截器类 LoginInterceptor,让其实现 HandlerInterceptor 接口以自定义拦截器功能,此处定义一个判断用户是否登录的拦截器,登录后放行,否则拦截请求。文件内容如下:

    package cn.edu.whut.springbear.uims.interceptor;
    
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.HandlerInterceptor;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * @author Spring-_-Bear
     * @datetime 2022-06-25 22:22 Saturday
     */
    @Component
    public class LoginInterceptor implements HandlerInterceptor {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            // 检查 session 类中是否存在 user,存在则已登录并放行,否则拦截请求
            return request.getSession().getAttribute("user") != null;
        }
    
    }
    
  2. 在 Spring MVC 的核心配置文件 spring-mvc-config.xml 中进行登录拦截器的相关配置。

    	<!-- 包扫描规则 -->
        <context:component-scan
                base-package="cn.edu.whut.springbear.uims.controller,
                cn.edu.whut.springbear.uims.interceptor"
        />
    
    	<!-- 拦截器配置 -->
        <mvc:interceptors>
            <!-- 登录拦截器:拦截除主页、登录、静态资源外的所有请求 -->
            <mvc:interceptor>
                <mvc:mapping path="/**"/>
                <mvc:exclude-mapping path="/"/>
                <mvc:exclude-mapping path="/login"/>
                <mvc:exclude-mapping path="/static/**"/>
                <ref bean="loginInterceptor"/>
            </mvc:interceptor>
        </mvc:interceptors>
    
  3. 将 src\main\webapp\WEB-INF\templates\index.html 文件内容修改为登录表单。修改后的文件内容如下:

    <!DOCTYPE html>
    <html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>用户登录</title>
        <style>
            body {
                font-family: Arial, sans-serif;
                background-color: #f1f1f1;
            }
    
            .container {
                max-width: 400px;
                margin: auto auto;
                padding: 20px;
                background-color: #fff;
                border-radius: 5px;
                box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
                display: flex;
                flex-direction: column;
                align-items: center;
            }
    
    
            h1 {
                text-align: center;
                margin-bottom: 20px;
            }
    
            form {
                display: flex;
                flex-direction: column;
            }
    
            label {
                margin-bottom: 10px;
                font-weight: bold;
            }
    
            input[type="text"],
            input[type="password"] {
                padding: 10px;
                border-radius: 3px;
                border: 1px solid #ccc;
                margin-bottom: 15px;
            }
    
            button {
                padding: 10px;
                background-color: #4CAF50;
                color: #fff;
                border: none;
                border-radius: 3px;
                cursor: pointer;
            }
    
            button:hover {
                background-color: #45a049;
            }
        </style>
    </head>
    <body>
    <div class="container">
        <h1>用户登录</h1>
        <form method="post" th:action="@{/login}">
            <label for="username">用户名</label>
            <input type="text" id="username" name="username" required>
            <label for="password">密码</label>
            <input type="password" id="password" name="password" required>
            <button type="submit">立即登录</button>
        </form>
    </div>
    </body>
    </html>
    
  4. 在 cn.edu.whut.springbear.uims.service 包下新建用户登录接口 UserService。文件内容如下:

    package cn.edu.whut.springbear.uims.service;
    
    import cn.edu.whut.springbear.uims.pojo.User;
    
    /**
     * @author Spring-_-Bear
     * @datetime 2022-06-25 22:38 Saturday
     */
    public interface UserService {
    
        /**
         * 用户登录
         *
         * @param username 用户名
         * @param password 密码
         * @return 用户名和密码正确返回用户信息,否则返回 null
         */
        User login(String username, String password);
    
    }
    
  5. 在 cn.edu.whut.springbear.uims.service.impl 包下新建 UserService 接口的实现类 UserServiceImpl,实现用户登录接口相关逻辑,并使用 @Service 注解将该类标识为 Spring 组件供 IOC 容器管理。文件内容如下:

    package cn.edu.whut.springbear.uims.service.impl;
    
    import cn.edu.whut.springbear.uims.mapper.UserMapper;
    import cn.edu.whut.springbear.uims.pojo.User;
    import cn.edu.whut.springbear.uims.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    /**
     * @author Spring-_-Bear
     * @datetime 2022-06-25 22:39 Saturday
     */
    @Service
    public class UserServiceImpl implements UserService {
    
        @Autowired
        private UserMapper userMapper;
    
        @Override
        public User login(String username, String password) {
            return this.userMapper.getByUsernameAndPassword(username, password);
        }
    
    }
    
  6. 在 cn.edu.whut.springbear.uims.controller 包下创建用户控制器类 UserController,处理用户相关业务逻辑。文件内容如下:

    package cn.edu.whut.springbear.uims.controller;
    
    import cn.edu.whut.springbear.uims.pojo.User;
    import cn.edu.whut.springbear.uims.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    import javax.servlet.http.HttpSession;
    
    /**
     * @author Spring-_-Bear
     * @datetime 2022-06-25 22:36 Saturday
     */
    @Controller
    public class UserController {
    
        @Autowired
        private UserService userService;
    
        @PostMapping("/login")
        public String login(HttpSession session, @RequestParam String username, @RequestParam String password) {
            User user = this.userService.login(username, password);
            // 用户名不存在或密码错误,返回首页视图名称 index,跳转到首页 index.html
            if (user == null) {
                return "index";
            }
    
            // session 域记录用户登录信息
            session.setAttribute("user", user);
            // 登录成功,重定向到成功页面
            return "redirect:/success";
        }
        
    }
    
  7. 在 src\main\webapp\WEB-INF\templates 目录下新建成功视图页面 success.html。文件内容如下:

    <!DOCTYPE html>
    <html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>登录成功</title>
        <style>
            body {
                font-family: Arial, sans-serif;
                background-color: #f1f1f1;
            }
    
            .container {
                max-width: 400px;
                margin: 0 auto;
                padding: 20px;
                background-color: #fff;
                border-radius: 5px;
                box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
                text-align: center;
            }
    
            h1 {
                margin-bottom: 20px;
            }
    
            a {
                color: #4CAF50;
                text-decoration: none;
            }
    
            a:hover {
                text-decoration: underline;
            }
        </style>
    </head>
    <body>
    <div class="container">
        <h1 th:text="${'欢迎 '+session.user.username}+',登录成功!'">欢迎张三,登录成功!</h1>
        <p>登录时间:<span id="username">2023-06-14 10:21:34</span></p>
        <p>IP 属地:<span id="location">湖北省武汉市</span></p>
        <a href="#">退出登录</a>
    </div>
    </body>
    </html>
    
  8. 在 Spring MVC 的核心配置文件 src\main\resources\spring-mvc-config.xml 中增加登录成功视图 success.html 的视图控制器配置。

        <!-- 视图控制器,此处 IDE 爆红问题可不予理会 -->
        <mvc:view-controller path="/" view-name="index"/>
        <mvc:view-controller path="/success" view-name="success"/>
    
  9. 启动 Tomcat,进行登录拦截器功能测试。若启动出现异常,可尝试删除 target 目录后重新启动 Tomcat。

    • 用户未登录,通过浏览器地址栏直接访问 success 请求,请求将被拦截器拦截,不返回任何信息。

      在这里插入图片描述

    • 输入不正确的用户名和密码,登录后验证失败将跳转到 index.html 页面。

      在这里插入图片描述

    • 用户名密码正确,登录成功重定向到成功页面 success.html,并展示用户信息。

      在这里插入图片描述

九、Spring MVC 异常解析器

  1. 在 cn.edu.whut.springbear.uims.exception 包下新建自定义异常类 InterceptorException。文件内容如下:

    package cn.edu.whut.springbear.uims.exception;
    
    /**
     * @author Spring-_-Bear
     * @datetime 2022-06-25 23:17 Saturday
     */
    public class InterceptorException extends RuntimeException {
    
        private static final long serialVersionUID = 2339004307463021785L;
    
        public InterceptorException(String message) {
            super(message);
        }
    
    }
    
  2. 在 Spring MVC 的核心配置文件 src\main\resources\spring-mvc-config.xml 中配置异常解析器处理异常信息。

        <!-- 异常解析器 -->
        <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
            <property name="exceptionMappings">
                <props>
                    <!-- 当遇到自定义的 InterceptorException 异常时,跳转视图名称为 index 的视图页面,也即 index.html -->
                    <prop key="cn.edu.whut.springbear.uims.exception.InterceptorException">index</prop>
                </props>
            </property>
    
            <!-- 为异常信息设置一个属性名 msg,默认在 request 域中进行共享 -->
            <property name="exceptionAttribute" value="msg"/>
        </bean>
    
  3. 修改 cn.edu.whut.springbear.uims.interceptor.LoginInterceptor 登录拦截器逻辑,当用户未登录时手动抛出自定义的 InterceptorException 异常,交由异常解析器处理。修改后的文件内容如下:

    package cn.edu.whut.springbear.uims.interceptor;
    
    import cn.edu.whut.springbear.uims.exception.InterceptorException;
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.HandlerInterceptor;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * @author Spring-_-Bear
     * @datetime 2022-06-25 22:22 Saturday
     */
    @Component
    public class LoginInterceptor implements HandlerInterceptor {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            Object user = request.getSession().getAttribute("user");
            // 检查用户是否登录,未登录则抛出自定义异常,否则放行
            if (user == null) {
                throw new InterceptorException("请先登录您的账号");
            }
    
            return true;
        }
    
    }
    
  4. 修改 src\main\webapp\WEB-INF\index.html 中的登录表单,增加错误信息展示元素。

    <div class="container">
        <h1>用户登录</h1>
        <form method="post" th:action="@{/login}">
            <label for="username">用户名</label>
            <input type="text" id="username" name="username" required>
            <label for="password">密码</label>
            <input type="password" id="password" name="password" required>
            <!-- 取出 request 域中保存的 msg 异常信息并展示 -->
            <div style="margin-bottom: 15px; color: #ee0a24" th:text="${msg}"></div>
            <button type="submit">立即登录</button>
        </form>
    </div>
    
  5. 修改 cn.edu.whut.springbear.uims.controller.UserController#login 登录逻辑,当用户登录信息验证失败时手动抛出异常以友好提示用户,修改后的文件内容如下:

    package cn.edu.whut.springbear.uims.controller;
    
    import cn.edu.whut.springbear.uims.exception.InterceptorException;
    import cn.edu.whut.springbear.uims.pojo.User;
    import cn.edu.whut.springbear.uims.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    import javax.servlet.http.HttpSession;
    
    /**
     * @author Spring-_-Bear
     * @datetime 2022-06-25 22:36 Saturday
     */
    @Controller
    public class UserController {
    
        @Autowired
        private UserService userService;
    
        @PostMapping("/login")
        public String login(HttpSession session, @RequestParam String username, @RequestParam String password) {
            User user = this.userService.login(username, password);
            // 用户名不存在或密码错误,返回首页视图名称 index,跳转到首页 index.html
            if (user == null) {
                throw new InterceptorException("用户名不存在或密码错误");
            }
    
            // session 域记录用户登录信息
            session.setAttribute("user", user);
            // 登录成功,重定向到成功页面
            return "redirect:/success";
        }
    
    }
    
  6. 启动 Tomcat,进行异常解析器功能测试。

    • 未登录账号直接访问 /success 请求时通过自定义的异常解析器跳转到 index.html 页面,并展示提示信息 “请先登录您的账号”。

      在这里插入图片描述

    • 当用户登录时输入错误的用户名和密码,跳转到 index.html,并友好提示用户 “用户名不存在或密码错误”。

      在这里插入图片描述

十、Spring MVC 文件上传解析器

  1. 在 Spring MVC 的核心配置文件 src\main\resources\spring-mvc-config.xml 中配置文件上传解析器,指定允许上传的文件大小和文件编码等信息。

        <!-- 文件上传解析器 -->
        <bean id="multipartResolver"
              class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
            <!-- 允许上传的最大文件大小 10M -->
            <property name="maxUploadSize" value="10485760"/>
            <property name="defaultEncoding" value="utf-8"/>
        </bean>
    
  2. 在 cn.edu.whut.springbear.uims.controller 包下新增 TransferController 类处理文件上传业务逻辑。文件内容如下:

    package cn.edu.whut.springbear.uims.controller;
    
    import cn.edu.whut.springbear.uims.exception.InterceptorException;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.multipart.MultipartFile;
    
    import javax.servlet.http.HttpSession;
    import java.io.File;
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * @author Spring-_-Bear
     * @since 23/06/14 14:41
     */
    @Controller
    public class TransferController {
    
        @ResponseBody
        @PostMapping("/upload")
        public Map<String, Object> fileUpload(HttpSession session, @RequestParam MultipartFile file) {
            // 获取文件上传保存目录在磁盘上的真实路径,相对路径为 ../src/main/webapp/static/upload
            String realPath = session.getServletContext().getRealPath("/static/upload");
            // 如果文件夹不存在则新建
            File directory = new File(realPath);
            if (!directory.exists()) {
                if (!directory.mkdirs()) {
                    throw new InterceptorException("文件上传保存目录创建失败");
                }
            }
    
            Map<String, Object> resultMap = new HashMap<>();
    
            // 将文件写入到磁盘
            File imgFile = new File(realPath + "/" + file.getOriginalFilename());
            try {
                file.transferTo(imgFile);
            } catch (IOException e) {
                resultMap.put("data", "文件保存失败");
            }
    
            resultMap.put("data", "文件上传成功");
            return resultMap;
        }
    
    }
    
  3. 修改 src\main\webapp\WEB-INF\success.html,增加图片上传表单和图片上传业务逻辑。需要注意以下事项:

    • 表单的请求方法 method 属性必须为 POST
    • 必须设置 enctype 的属性值为 multipart/form-data
    <!DOCTYPE html>
    <html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>登录成功</title>
        <style>
            body {
                font-family: Arial, sans-serif;
                background-color: #f1f1f1;
            }
    
            .container {
                max-width: 400px;
                margin: 0 auto;
                padding: 20px;
                background-color: #fff;
                border-radius: 5px;
                box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
                text-align: center;
            }
    
            h1 {
                margin-bottom: 20px;
            }
    
            a {
                color: #4CAF50;
                text-decoration: none;
            }
    
            a:hover {
                text-decoration: underline;
            }
    
            .form-container {
                max-width: 400px;
                margin: 0 auto;
            }
    
            .form-container h2 {
                text-align: center;
            }
    
            .form-container label {
                display: block;
                margin-bottom: 10px;
            }
    
            .form-container input[type="file"] {
                margin-bottom: 20px;
            }
    
            .form-container button {
                display: block;
                margin: 0 auto;
            }
    
            #output {
                margin-top: 30px;
                padding: 10px;
                border: 1px solid #ccc;
                background-color: #f9f9f9;
                display: none;
            }
    
            #output pre {
                white-space: pre-wrap;
            }
        </style>
    </head>
    <body>
    <div class="container">
        <h1 th:text="${'欢迎 '+session.user.username}+',登录成功!'">欢迎张三,登录成功!</h1>
        <p>登录时间:<span id="username">2023-06-14 10:21:34</span></p>
        <p>IP 属地:<span id="location">湖北省武汉市</span></p>
        <a href="#">退出登录</a>
    </div>
    
    <div class="form-container">
        <h2>图片上传表单</h2>
        <form id="uploadForm" enctype="multipart/form-data">
            <label for="fileInput">选择图片:</label>
            <input type="file" id="fileInput" accept="image/*" required>
            <button type="submit">上传</button>
        </form>
    </div>
    
    <div id="output">
        <h3>服务器返回的 JSON 数据:</h3>
        <pre id="jsonData"></pre>
    </div>
    
    <script>
        // 获取表单元素和输出区域元素
        const uploadForm = document.getElementById('uploadForm');
        const fileInput = document.getElementById('fileInput');
        const outputDiv = document.getElementById('output');
        const jsonDataPre = document.getElementById('jsonData');
    
        // 监听表单的提交事件
        uploadForm.addEventListener('submit', function (event) {
            event.preventDefault(); // 阻止表单默认提交行为
    
            // 创建 FormData 对象
            const formData = new FormData();
            formData.append('file', fileInput.files[0]);
    
            // 创建 XMLHttpRequest 对象
            const xhr = new XMLHttpRequest();
    
            // 设置请求处理的回调函数
            xhr.onreadystatechange = function () {
                if (xhr.readyState === XMLHttpRequest.DONE) {
                    console.log(xhr)
                    if (xhr.status === 200) {
                        const response = JSON.parse(xhr.responseText);
                        // 在输出区域展示 JSON 数据
                        jsonDataPre.textContent = JSON.stringify(response, null, 2);
                        outputDiv.style.display = 'block'; // 显示输出区域
                    } else {
                        alert('发生错误,无法上传文件!');
                    }
                }
            };
    
            // 发送 POST 请求到服务器
            xhr.open('POST', 'http://localhost:8080/uims/upload', true);
            xhr.send(formData);
        });
    </script>
    </body>
    </html>
    
  4. 启动 Tomcat,在 index.html 中输入正确的账号和密码,系统验证通过后跳转到 success.html 中进行图片上传。

    在这里插入图片描述

  5. 查看服务器保存的图片文件,位于 target 目录下。

在这里插入图片描述

至此,通过 XML 配置文件方式成功整合 Spring、Spring MVC 和 MyBatis 三大框架,并整合了一些开发过程中的常用功能,如单元测试、拦截器、异常解析器和文件上传解析器等。完整的工程目录结构图如下所示:

在这里插入图片描述

完成 XML 配置文件方式整合 SSM 框架后,在此基础上推荐 全注解方式整合 SSM 框架

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐