【水平:编写简单的Spring项目】用一篇文章精通Spring+SpringMVC+Myabtis实践

Spring项目结构与配置思维导图

在这里插入图片描述

项目结构

src/
├── main/
│   ├── java/         # Java源代码目录 
│   │   └── com/      # 公司/组织域名倒序 
│   │       └── example/
│   │           ├── config/    # Spring配置类 
│   │           ├── controller/ # MVC控制器
│   │           ├── service/    # 业务服务层
│   │           ├── dao/        # 数据访问层 
│   │           └── model/      # 数据模型/实体类
│   └── resources/    # 资源文件目录 
│       ├── static/    # 静态资源(js,css,img)
│       ├── templates/ # 模板文件(如Thymeleaf)
│       └── application.properties # 主配置文件 
└── test/             # 测试代码目录 

配置文件详解

1. 传统XML配置方式

<!-- applicationContext.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:c
       xsi:schemaLocation="...">
       
    <!-- 组件扫描 -->
    <context:component-scan base-package="com.example"/>
    
    <!-- 数据源配置 -->
    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    
    <!-- 事务管理 -->
    <bean id="transactionManager" 
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
</beans>

2. Spring MVC配置

<!-- spring-mvc.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:c
       xsi:schemaLocation="...">
       
    <!-- 启用注解驱动 -->
    <mvc:annotation-driven/>
    
    <!-- 静态资源处理 -->
    <mvc:resources mapping="/static/**" location="/static/"/>
    
    <!-- 视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

3. 现代Java配置方式

@Configuration
@EnableWebMvc 
@ComponentScan("com.example")
public class AppConfig implements WebMvcConfigurer {
    
    @Bean
    public DataSource dataSource() {
        return DataSourceBuilder.create()
            .driverClassName("com.mysql.jdbc.Driver")
            .url("jdbc:mysql://localhost:3306/mydb")
            .username("root")
            .password("password")
            .build();
    }
    
    @Bean 
    public ViewResolver viewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        return resolver;
    }
    
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**")
                .addResourceLocations("/static/");
    }
}

最佳实践建议

  1. 分层清晰:严格遵循MVC分层结构,控制器只负责请求转发,业务逻辑放在service层
  2. 配置分离
    • 开发/测试/生产环境使用不同的配置文件
    • 敏感信息(如数据库密码)放在单独的properties文件中
  3. 注解与XML结合
    • 现代Spring项目推荐使用Java配置+注解
    • 遗留系统可以逐步从XML迁移到注解
  4. 模块化配置
    • 将不同功能的配置分开(如数据源配置、安全配置等)
    • 使用@Import导入其他配置类
  5. 版本控制
    • 不要将本地开发配置提交到版本控制系统
    • 使用.gitignore过滤IDE配置文件和本地配置文件

就像我曾经在一个电商项目中,开始时所有配置都堆在一个巨大的XML文件中,后来随着业务增长,维护变得极其困难。我们花了两个月时间重构,将配置按功能模块拆分,并逐步迁移到Java配置方式,最终使得项目的可维护性大大提高。

SpringMVC控制器与视图详解

让我们用一个餐厅点餐系统的例子来理解SpringMVC控制器与视图的配合:

1. 控制器类编写 - 餐厅的点餐台

@Controller 
@RequestMapping("/order")
public class OrderController {
    
    // 相当于餐厅的点餐入口 
    @GetMapping("/menu")
    public String showMenu(Model model) {
        // 获取菜单数据(相当于后厨准备菜单)
        List<Dish> menu = menuService.getAllDishes();
        model.addAttribute("menuList", menu);
        return "menuPage"; // 返回菜单页面视图
    }
    
    // 相当于接收顾客订单 
    @PostMapping("/submit")
    public String submitOrder(@RequestParam("dishId") Integer dishId, 
                             @ModelAttribute Customer customer) {
        // 处理订单逻辑(相当于服务员记录订单)
        orderService.createOrder(dishId, customer);
        return "redirect:/order/confirmation"; // 重定向到确认页面 
    }
    
    // 相当于订单确认页面
    @GetMapping("/confirmation")
    public String showConfirmation(Model model) {
        // 获取最新订单信息
        Order latestOrder = orderService.getLatestOrder();
        model.addAttribute("order", latestOrder);
        return "orderConfirmation"; // 返回确认页面视图 
    }
}

2. 方法参数获取与返回值处理 - 服务员的工作流程

参数获取方式(服务员接收信息的方式):

  • @RequestParam:接收单个参数,如菜品ID
  • @ModelAttribute:接收对象参数,如顾客信息
  • @RequestBody:接收JSON数据(高级餐厅的电子订单)
  • HttpServletRequest/HttpServletResponse:直接访问请求/响应对象

返回值处理(服务员如何回应顾客):

  • 返回字符串:视图名称(如"menuPage")
  • 返回ModelAndView:包含模型数据和视图名称的对象
  • 使用redirect::重定向到另一个URL(如订单确认页面)
  • 使用@ResponseBody:直接返回数据(如JSON)

3. 视图解析器配置 - 餐厅的展示区布置

JSP视图解析器(传统菜单展示):

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/"/>  <!-- 菜单存放在厨房的哪个柜子 -->
    <property name="suffix" value=".jsp"/>             <!-- 菜单的文件格式 -->
</bean>

Freemarker视图解析器(电子菜单展示):

<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
    <property name="prefix" value=""/>                  <!-- 模板文件的前缀 -->
    <property name="suffix" value=".ftl"/>             <!-- 模板文件的后缀 -->
    <property name="order" value="0"/>                  <!-- 优先级高于JSP -->
</bean>
 
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
    <property name="templateLoaderPath" value="/WEB-INF/views/"/>  <!-- 模板存放位置 -->
</bean>

4. 完整请求响应流程 - 餐厅服务全过程

  1. 顾客点餐(用户发起请求) -> /order/menu
  2. 服务员接待(DispatcherServlet接收请求)
  3. 查看菜单(调用OrderController的showMenu方法)
  4. 后厨准备(业务逻辑处理,获取菜单数据)
  5. 展示菜单(返回"menuPage"视图名称)
  6. 视图解析器找到/WEB-INF/views/menuPage.jsp
  7. 渲染菜单(JSP页面填充数据并生成HTML)
  8. 上菜(返回响应给用户)

5. 实际项目中的最佳实践

  1. 控制器设计原则:
    • 保持控制器精简(像优秀服务员,只负责传递信息)
    • 一个控制器对应一个业务模块(如OrderController、UserController)
  2. 视图选择建议:
    • 简单页面用JSP
    • 复杂页面或需要模板继承用Freemarker/Thymeleaf
    • RESTful API用@ResponseBody返回JSON
  3. 参数处理技巧:
    • 使用@Valid进行参数验证(确保订单信息完整)
    • 复杂表单使用@ModelAttribute绑定对象

通过这个餐厅的比喻,你应该能更直观地理解SpringMVC中控制器与视图的配合方式了。就像餐厅需要服务员(控制器)和展示区(视图)的完美配合才能提供优质服务一样,SpringMVC应用也需要合理设计控制器和视图解析器。

MyBatis Mapper接口与XML映射文件

一、Mapper接口编写

1. 接口命名规则

  • 命名规范:一般采用表名+Mapper的命名方式,如UserMapper
  • 位置要求:通常放在mapperdao包下
  • 最佳实践:保持接口名称与XML映射文件名称一致(除后缀外)

2. 接口定义

public interface UserMapper {
    // 根据ID查询用户 
    User selectById(@Param("id") Long id);
    
    // 查询所有用户 
    List<User> selectAll();
    
    // 插入用户 
    int insert(User user);
    
    // 更新用户
    int update(User user);
    
    // 删除用户 
    int deleteById(@Param("id") Long id);
    
    // 条件查询
    List<User> selectByCondition(@Param("condition") Map<String, Object> condition);
}

二、XML映射文件编写

1. SQL语句编写与映射

<?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">
    
<!-- 命名空间必须对应Mapper接口的全限定名 -->
<mapper namespace="com.example.mapper.UserMapper">
    
    <!-- 结果映射 -->
    <resultMap id="userResultMap" type="com.example.entity.User">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="password" column="password"/>
        <result property="email" column="email"/>
        <result property="createTime" column="create_time"/>
    </resultMap>
    
    <!-- 根据ID查询 -->
    <select id="selectById" resultMap="userResultMap">
        SELECT * FROM user WHERE id = #{id}
    </select>
    
    <!-- 查询所有 -->
    <select id="selectAll" resultMap="userResultMap">
        SELECT * FROM user
    </select>
    
    <!-- 插入 -->
    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO user(username, password, email)
        VALUES(#{username}, #{password}, #{email})
    </insert>
    
    <!-- 更新 -->
    <update id="update">
        UPDATE user 
        SET username = #{username},
            password = #{password},
            email = #{email}
        WHERE id = #{id}
    </update>
    
    <!-- 删除 -->
    <delete id="deleteById">
        DELETE FROM user WHERE id = #{id}
    </delete>
    
    <!-- 动态SQL示例 -->
    <select id="selectByCondition" resultMap="userResultMap">
        SELECT * FROM user
        <where>
            <if test="condition.username != null">
                AND username LIKE CONCAT('%', #{condition.username}, '%')
            </if>
            <if test="condition.email != null">
                AND email = #{condition.email}
            </if>
        </where>
    </select>
</mapper>

2. 参数与结果配置

参数传递方式
  1. 单个基本类型参数:直接使用#{参数名}
  2. 多个参数:使用@Param注解或Map/POJO传递
  3. 集合参数:使用foreach标签处理
结果映射方式
  1. resultType:直接指定返回类型(适用于字段名与属性名一致)
  2. resultMap:自定义结果映射(适用于复杂映射关系)
  3. 自动映射:开启驼峰命名转换(mapUnderscoreToCamelCase=true
高级特性
  1. 动态SQLif, choose, when, otherwise, trim, where, set, foreach
  2. 缓存配置<cache/>标签开启二级缓存
  3. 关联查询<association>, <collection>处理一对一、一对多关系

三、Mapper接口与XML的绑定规则

  1. 命名空间:XML中的namespace必须与Mapper接口全限定名一致
  2. 方法名匹配:XML中的SQL语句id必须与接口方法名一致
  3. 参数类型匹配:XML中的参数类型必须与接口方法参数类型一致
  4. 返回类型匹配:XML中的resultType/resultMap必须与接口方法返回类型一致

四、常见问题解决方案

  1. 绑定异常:检查namespace和id是否与接口对应
  2. 参数为null:检查@Param注解或参数名是否正确
  3. 字段映射失败:检查resultMap配置或开启驼峰命名转换
  4. SQL语法错误:先在数据库客户端测试SQL语句

五、最佳实践

  1. 保持接口与XML文件同名且在同一目录下
  2. 复杂的SQL写在XML中,简单的可以使用注解方式
  3. 使用resultMap处理复杂的字段映射关系
  4. 合理使用动态SQL提高SQL复用性
  5. 对频繁查询但不常修改的数据使用缓存

思维导图

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java自学之旅

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值