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/");
}
}
最佳实践建议
- 分层清晰:严格遵循MVC分层结构,控制器只负责请求转发,业务逻辑放在service层
- 配置分离:
- 开发/测试/生产环境使用不同的配置文件
- 敏感信息(如数据库密码)放在单独的properties文件中
- 注解与XML结合:
- 现代Spring项目推荐使用Java配置+注解
- 遗留系统可以逐步从XML迁移到注解
- 模块化配置:
- 将不同功能的配置分开(如数据源配置、安全配置等)
- 使用@Import导入其他配置类
- 版本控制:
- 不要将本地开发配置提交到版本控制系统
- 使用.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. 完整请求响应流程 - 餐厅服务全过程
- 顾客点餐(用户发起请求) ->
/order/menu
- 服务员接待(DispatcherServlet接收请求)
- 查看菜单(调用OrderController的showMenu方法)
- 后厨准备(业务逻辑处理,获取菜单数据)
- 展示菜单(返回"menuPage"视图名称)
- 视图解析器找到
/WEB-INF/views/menuPage.jsp
- 渲染菜单(JSP页面填充数据并生成HTML)
- 上菜(返回响应给用户)
5. 实际项目中的最佳实践
- 控制器设计原则:
- 保持控制器精简(像优秀服务员,只负责传递信息)
- 一个控制器对应一个业务模块(如OrderController、UserController)
- 视图选择建议:
- 简单页面用JSP
- 复杂页面或需要模板继承用Freemarker/Thymeleaf
- RESTful API用@ResponseBody返回JSON
- 参数处理技巧:
- 使用
@Valid
进行参数验证(确保订单信息完整) - 复杂表单使用
@ModelAttribute
绑定对象
- 使用
通过这个餐厅的比喻,你应该能更直观地理解SpringMVC中控制器与视图的配合方式了。就像餐厅需要服务员(控制器)和展示区(视图)的完美配合才能提供优质服务一样,SpringMVC应用也需要合理设计控制器和视图解析器。
MyBatis Mapper接口与XML映射文件
一、Mapper接口编写
1. 接口命名规则
- 命名规范:一般采用
表名+Mapper
的命名方式,如UserMapper
- 位置要求:通常放在
mapper
或dao
包下 - 最佳实践:保持接口名称与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. 参数与结果配置
参数传递方式
- 单个基本类型参数:直接使用
#{参数名}
- 多个参数:使用
@Param
注解或Map/POJO传递 - 集合参数:使用
foreach
标签处理
结果映射方式
- resultType:直接指定返回类型(适用于字段名与属性名一致)
- resultMap:自定义结果映射(适用于复杂映射关系)
- 自动映射:开启驼峰命名转换(
mapUnderscoreToCamelCase=true
)
高级特性
- 动态SQL:
if
,choose
,when
,otherwise
,trim
,where
,set
,foreach
- 缓存配置:
<cache/>
标签开启二级缓存 - 关联查询:
<association>
,<collection>
处理一对一、一对多关系
三、Mapper接口与XML的绑定规则
- 命名空间:XML中的namespace必须与Mapper接口全限定名一致
- 方法名匹配:XML中的SQL语句id必须与接口方法名一致
- 参数类型匹配:XML中的参数类型必须与接口方法参数类型一致
- 返回类型匹配:XML中的resultType/resultMap必须与接口方法返回类型一致
四、常见问题解决方案
- 绑定异常:检查namespace和id是否与接口对应
- 参数为null:检查
@Param
注解或参数名是否正确 - 字段映射失败:检查resultMap配置或开启驼峰命名转换
- SQL语法错误:先在数据库客户端测试SQL语句
五、最佳实践
- 保持接口与XML文件同名且在同一目录下
- 复杂的SQL写在XML中,简单的可以使用注解方式
- 使用resultMap处理复杂的字段映射关系
- 合理使用动态SQL提高SQL复用性
- 对频繁查询但不常修改的数据使用缓存