Spring MVC REST 项目分析
1. 项目结构整理
项目采用标准的Maven Web项目结构,主要包含以下内容:
src/main/
├── java/com/zhang/ # Java源代码
│ ├── MyFilter.java # 自定义过滤器(已注释)
│ ├── bean/ # 实体类
│ │ ├── Address.java # 地址实体
│ │ └── User.java # 用户实体
│ ├── controller/ # 控制器
│ │ ├── RequestController.java # 请求处理控制器
│ │ ├── UserController.java # REST风格用户控制器
│ │ └── UserController2.java # 用户控制器(另一种实现)
│ └── dao/ # 数据访问层
│ └── UserDao.java # 用户数据访问接口实现
├── resources/ # 资源文件
│ └── springmvc.xml # Spring MVC配置文件
└── webapp/ # Web资源
├── WEB-INF/ # Web应用配置
│ ├── lib/ # 库文件
│ ├── page/ # 页面文件
│ └── web.xml # Web部署描述符
├── index.jsp # 首页
└── user.jsp # 用户页面
pom.xml # Maven项目配置文件
2. 主要文件功能分析
配置文件
pom.xml:
- 定义项目基本信息(groupId、artifactId、version)
- 设置打包方式为war
- 引入Spring Web和Spring WebMVC依赖(5.2.3.RELEASE版本)
- 配置Maven构建插件
springmvc.xml:
- 配置组件扫描(com.zhang包)
- 配置视图解析器(InternalResourceViewResolver),设置前缀和后缀
web.xml:
- 配置DispatcherServlet,指定Spring MVC配置文件位置
- 配置编码过滤器(CharacterEncodingFilter)处理乱码问题
- 配置请求方式转换过滤器(HiddenHttpMethodFilter)支持PUT/DELETE请求
- 注释了自定义过滤器MyFilter
实体类
User.java:
- 用户实体类,包含id、name、age、gender属性
- 关联Address对象
- 提供构造函数、getter/setter方法和toString方法
Address.java:
- 地址实体类,包含province、city、town属性
- 提供构造函数、getter/setter方法和toString方法
控制器
UserController.java:
- 实现RESTful风格的CRUD操作
- 使用不同的HTTP请求方法区分操作类型:POST(新增)、GET(查询)、PUT(更新)、DELETE(删除)
- 通过@RequestMapping注解指定请求路径和方法
- 注入UserDao进行数据操作
UserController2.java:
- 展示表单数据自动绑定到User对象
- 展示如何在控制器中使用原生Servlet API(HttpServletRequest、HttpServletResponse、HttpSession)
- 包含乱码问题解决方案的注释说明
RequestController.java:
- 演示请求参数获取(@RequestParam注解)
- 演示请求头信息获取(@RequestHeader注解)
- 演示Cookie值获取(@CookieValue注解)
- 详细说明了@RequestParam和@PathVariable的区别和使用场景
数据访问层
UserDao.java:
- 使用@Repository注解标识为数据访问组件
- 实现基本的CRUD方法:save、update、delete、query
- 目前为模拟实现,仅输出日志信息
3. 项目学习要点总结
通过这个项目,我们可以学习到以下Spring MVC相关知识:
Spring MVC框架基础
- 理解MVC架构模式在Web应用中的应用
- 掌握Spring MVC的核心组件(DispatcherServlet、控制器、视图解析器等)
- 学习Spring MVC的配置方式(XML配置)
RESTful API设计与实现
- 了解RESTful风格的API设计原则
- 学习使用不同HTTP方法(GET、POST、PUT、DELETE)表示不同操作
- 掌握通过@RequestMapping注解配置请求路径和方法
请求处理技术
- 学习如何获取请求参数、请求头和Cookie信息
- 掌握@RequestParam、@RequestHeader、@CookieValue等注解的使用
- 了解表单数据自动绑定到JavaBean的原理
数据访问与模型层设计
- 学习分层架构设计(控制器层、数据访问层)
- 了解实体类的设计和关联关系
- 掌握Spring的依赖注入(@Autowired注解)
Web应用常见问题解决
- 学习乱码问题的解决方案(编码过滤器)
- 了解如何支持PUT/DELETE请求方法(HiddenHttpMethodFilter)
- 掌握在Spring MVC中使用原生Servlet API
项目构建与部署
- 学习使用Maven管理项目依赖和构建
- 了解Web应用的打包方式(WAR)
- 掌握Web应用的部署描述符配置(web.xml)
这个项目为初学者提供了Spring MVC框架的入门实践,涵盖了从基础配置到控制器实现、数据访问的完整流程,是学习RESTful Web服务开发的良好案例。
代码
bean
public class Address {
private String province;
private String city;
private String town;
}
public class User {
private Integer id;
private String name;
private Integer age;
private String gender;
private Address address;
}
模拟dao
import com.zhang.bean.User;
import org.springframework.stereotype.Repository;
import javax.servlet.http.HttpServletRequest;
@Repository
public class UserDao {
public void save(User user) {
System.out.println(this.getClass().getName() + "save");
System.out.println("save();");
}
public void update(Integer id) {
System.out.println("updete();");
System.out.println(id);
}
public void delete(Integer id) {
System.out.println("delete();");
System.out.println(id);
}
public void query() {
System.out.println("query();");
}
}
过滤器
package com.zhang;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(filterName = "MyFilter")
public class MyFilter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
System.out.println("init();");
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println(this.getClass().getName() + "--------satrt");
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
chain.doFilter(req, resp);/*放行*/
System.out.println(this.getClass().getName() + "--------stop");
}
@Override
public void destroy() {
System.out.println("destroy();");
}
}
controller 层
package com.zhang.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class RequestController {
/**
* request.getParameter("name")
*
*
* 当发送请求的时候,找到对应的处理方法之后,会根据参数的名称从request中获取对应的参数值,并封装到方法中
* 此时要求,方法的名字和url中参数的名字必须一致,如果不一致,设置不成功
*
*
* 如果设置的值不同,同时又想让参数获取到对应的属性值,可以通过@RequestParam来使用
* 经常跟@PathVariable混淆,主要注意,两个注解有不同的用处
*此注解的参数;
* value:获取的参数值
* required:表示当前属性值是否是必须存在的,默认值是true,表示请求中必须要包含此参数,如果没有,400,bad request
*defaultValue:如果传递参数了,那么使用传递进来的参数,如果没有使用默认值
*
* 相信大家可能注意到了,@RequestParam和@PathVariable都能够完成类似的功能——因为本质上,它们都是用户的输入,只不过输入的部分不同,一个在URL路径部分,另一个在参数部分,简单的说就是url写法不同,如下:
*
* 使用@RequestParam时,URL是这样的:http://host:port/path?参数名=参数值
*
* 使用@PathVariable时,URL是这样的:http://host:port/path/参数值
* @param name
* @return
*/
@RequestMapping("/testRequest")
public String testRequest(@RequestParam(value = "username",required = false, defaultValue = "lisi")String name) {
System.out.println(name);
return "success";
}
/**
*获取请求头信息
* 通过@RequestHeader注解来表示
* request. getHeader ( User-Agent)
*
* 同时也包含了几个参数
* value:
* required:
* defaultValue :
* 同@RequestParan
*/
@RequestMapping("/testRequestHeader")
public String testRequestHeader(@RequestHeader("User-Agent") String userAgent) {
System.out.println(userAgent);
return "success";
}
/**
* 获取cookie中的值,使用@Cookielalue注解
*
* 同时也包含了几个参数
* value:
* required:
* defaultValue :
* 同@RequestParan
* Cookie[] cookies = request.getCookies();
* @param jsid
* @return
*/
@RequestMapping("/testCookie")
public String testCookie(@CookieValue("JSESSIONID") String jsid) {
System.out.println(jsid);
return "success";
}
}
package com.zhang.controller;
import com.zhang.bean.User;
import com.zhang.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.http.HttpServletRequest;
/**
* localhost:8080/web_project/save
* localhost:8080/web_project/update?id=1
* localhost:8080/web_project/delete?id=1
* localhost:8080/web_project/query
*
*我们在发送请求的时候有不同的请求方式,能不能通过请求方式来做一次转换
*
* PosT: 新增 /user
* GET: 获取 / user
* PUT: 更新 user/1
* DELETE: 删除 /user/1
*
*
*/
@Controller
public class UserController {
@Autowired
private UserDao userDao;
@RequestMapping(value = "/user" ,method = RequestMethod.POST)
// @RequestMapping("/save")
public String save(){
System.out.println(this.getClass().getName()+"save");
userDao.save(new User());
return "success";
}
@RequestMapping(value = "/user" ,method = RequestMethod.PUT)
public String update(Integer id){
System.out.println(this.getClass().getName()+"update");
userDao.update(id);
return "success";
}
@RequestMapping(value = "/user" ,method = RequestMethod.DELETE)
public String delete(Integer id){
System.out.println(this.getClass().getName()+"delete");
userDao.delete(id);
return "success";
}
@RequestMapping(value = "/user" ,method = RequestMethod.GET)
public String query(){
System.out.println(this.getClass().getName()+"query");
userDao.query();
return "success";
}
}
package com.zhang.controller;
import com.zhang.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* 乱码问趣解决;
* 我们需要设置request和response的编码方式,可以自己手动编写过滤器,也可以由现成的框架来实现
* post:必须要分别设置request和response的编码格式
* get:在tomcat的server.xml文件中,添加URIEncoding=utf-8
*
*在一个应用程序中可能会包含N多个过滤器, 这N多个过德器一般是没有顺序的要求的,
* 但是如果你设置了编码过滤器,那么要求必须要将编码过滤器设置到最上面,保证编码过滤器优先执行
*/
@Controller
public class UserController2 {
@RequestMapping("/testUser")
public String testUser(User user){
System.out.println(user);
return "success";
}
/**
* Controller中也支持原生Servlet的对象,需要在参数中给出
*
*
* HttpServletRequest
* HttpServletResponseHt tpSession
* Locale:设置区域信息,国际化的操作
* InputStream :
* OutputStream
* Reader:
* Writer.
*
*
* @param request
* @param response
* @param session
* @return
*/
@RequestMapping("/api")
public String servletAPI(HttpServletRequest request, HttpServletResponse response, HttpSession session){
// response.getWriter().write();
request.setAttribute("request","request");
session.setAttribute("session","session");
return "success";
}
}
前端
<!-- index.jsp -->
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
pageContext.setAttribute("ctp",request.getContextPath());
// 加虚拟目录报错
%>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/user" method="post">
<input type="submit" value="新增">
</form>
<form action="/user" method="post">
<input type="hidden" name="_method" value="put">
<input type="submit" value="更新">
</form>
<form action="/user" method="post">
<input type="hidden" name="_method" value="delete">
<input type="submit" value="删除">
</form>
<a href="/user" >查询</a>
</body>
</html>
<!-- user.jsp -->
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%pageContext.setAttribute("ctp", request.getContextPath());%>
<html>
<head>
<title>User</title>
</head>
<body>
<%--<form action="${ctp}/testUser" method="post">--%>
<form action="/testUser" method="post">
编号:<input type="text" name="id"><br>
姓名:<input type="text" name="name"><br>
年龄:<input type="text" name="age"><br>
性别:<input type="text" name="gender"><br>
省份:<input type="text" name="address.province"><br>
城市:<input type="text" name="address.city"><br>
区域:<input type="text" name="address.town"><br>
<input type="submit" value="提交"><br>
</form>
</body>
</html>
<!-- WEN_INF/lib/success.jsp -->
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
操作成功<br>
request:${requestScope.get("request")}<br>
session:${sessionScope.get("session")}<br>
${requestScope.request}
<input value="${requestScope.get("request")}">
<input value="${requestScope.request}">
</body>
</html>
配置文件
web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<!--<?xml version="1.0" encoding="utf-8" ?>-->
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--自定义过滤器-->
<!-- <filter>-->
<!-- <filter-name>myFilter</filter-name>-->
<!-- <filter-class>com.zhang.MyFilter</filter-class>-->
<!-- </filter>-->
<!-- <filter-mapping>-->
<!-- <filter-name>myFilter</filter-name>-->
<!-- <url-pattern>/*</url-pattern>-->
<!-- </filter-mapping>-->
<!--springMVC提供的编码过滤器-->
<filter>
<filter-name>encoding</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>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 此滤器完成请求方式的转换,将post请求转换为put或者delete-->
<filter>
<filter-name>hidden</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hidden</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
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>com.zhang</groupId>
<artifactId>springmvc_rest</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>springmvc_rest Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
</dependencies>
<build>
<finalName>springmvc_rest</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
springmvc.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="com.zhang"></context:component-scan>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/page/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
用到的jar
jsp-api.jar
servlet-api.jar