今日给大家分享spring mvc相关内容,看来这篇文章对这一块知识复习和深入有一定帮助,本文通过容易理解的方式给大家分享。
一、Spring MVC 概述
1.1 Spring MVC是什么?
Spring MVC 是 Spring 框架的一个模块(提升:spirng框架有哪些核心模块呢?),它是基于 Java 实现的一个轻量级 Web 框架,遵循 MVC(Model-View-Controller,模型 - 视图 - 控制器)设计模式(分层思想出现)。MVC 模式将一个应用程序分为三个核心部分:模型(Model)、视图(View)和控制器(Controller),它们各自处理不同的职责,从而实现代码的解耦和可维护性,这里主要弄清楚有哪几层,明白分层思想出现。
模型(Model):负责封装数据和业务逻辑。例如,在一个电商系统中,商品信息就是一个模型,它包含商品的名称、价格、描述等属性,同时可能还包含一些与商品相关的业务逻辑,如计算商品的折扣价格,通俗易懂一点就是对应项目中的实体嘛。
视图(View):负责将模型中的数据呈现给用户。常见的视图技术有 JSP、Thymeleaf 等。以电商系统为例,商品列表页面就是一个视图,它将商品模型中的数据以列表的形式展示给用户,其实就是指的页面,前端这一块,比如html,jsp,vue等等。
控制器(Controller):负责接收用户的请求,调用模型处理业务逻辑,并选择合适的视图将结果展示给用户。在电商系统中,当用户点击 “查看商品列表” 按钮时,控制器会接收这个请求,调用商品模型获取商品列表数据,然后选择商品列表视图将数据展示给用户,控制层就是我们项目中的Controller ,也就是入口,Controller 在项目中会根据业务模块分为若干个Controller,比如:
@Api(tags = "小程序")
@RestController
@RequestMapping("/xxx")
public class MiniProgramController {
}
@Api(tags = "商品")
@RestController
@RequestMapping("/xxx")
public class MiniProgramController {
}
...........
我们是不是已经弄清楚了mvc三层分别是什么了?然后有分层的思想了?我们继续...
1.2 为什么出现 Spring MVC
我们要弄清楚为什么出现spring MVC,肯定要弄清楚为什么对吧?在 Spring MVC 出现之前,Java Web 开发主要使用 Servlet 和 JSP 技术。虽然 Servlet 和 JSP 可以实现 Web 应用的开发,但它们存在一些问题,但是很多程序员没有使用过JSP吧,很多都是后面直接使用springboot的吧,没有经历过这个过程。使用 Servlet 和 JSP 技术存在以下几个问题:
代码耦合度高:Servlet 和 JSP 通常会混合处理业务逻辑和视图展示,导致代码难以维护和扩展。例如,一个 Servlet 可能既处理用户登录的业务逻辑,又负责将登录结果展示给用户,当业务需求发生变化时,修改代码会变得非常困难,我记得我以前写JSP代码的时候把连接数据库的代码,业务代码都会写入到不同的JSP页面中,大家知道这样的问题了吧,因为那时还没spring mvc出现。
开发效率低:使用 Servlet 和 JSP 开发 Web 应用需要编写大量的样板代码,如处理请求参数、转发请求等,这会增加开发人员的工作量,降低开发效率。
缺乏统一的开发规范:不同的开发人员可能采用不同的方式来实现 Web 应用,导致代码风格不一致,不利于团队协作和项目管理,其实问题还不止这几项,大家想想还有什么问题?
Spring MVC 的出现解决了上述问题,它提供了一种更加优雅、高效的方式来开发 Java Web 应用。通过遵循 MVC 设计模式,Spring MVC 实现了代码的解耦,提高了代码的可维护性和可扩展性;同时,它还提供了丰富的注解和工具,减少了开发人员编写样板代码的工作量,提高了开发效率。
1.3 历史背景
大概了解一下背景,只是了解就行,Spring 框架最初由 Rod Johnson 在 2003 年发布,它的目标是为 Java 企业级应用提供一个轻量级的开发框架。随着 Spring 框架的不断发展,为了满足 Web 开发的需求,Spring 团队在 Spring 框架中引入了 Spring MVC 模块。
Spring MVC 借鉴了许多其他 Web 框架的优秀设计思想,如 Struts、WebWork 等。它在设计上更加注重灵活性和可扩展性,同时提供了与 Spring 框架其他模块的无缝集成,使得开发人员可以更加方便地构建企业级 Web 应用。这里重点提到了几个关键词:模块,灵活性,可扩展性,无缝集成,这些是不是我们应该关注的和提升自己的关键词?在我们的项目中是不是真正做到了?
二、Spring MVC 核心知识点
2.1 核心组件
Spring MVC 的核心组件包括 DispatcherServlet、HandlerMapping、Controller、ModelAndView、ViewResolver 等,下面分别介绍这些组件的作用:
2.1.1 DispatcherServlet
DispatcherServlet 是 Spring MVC 的核心控制器,它负责接收所有的 HTTP 请求,并将请求分发给相应的处理器进行处理。可以将 DispatcherServlet 看作是一个交通枢纽,所有的请求都要经过它,然后由它将请求引导到合适的目的地。那大家思考一下DispatcherServlet是不是我们的总指挥部一样?所以的请求都要经过这里,由它分发,那这里是不是可以去看看源码了?
2.1.2 HandlerMapping
HandlerMapping 负责根据请求的 URL 找到对应的处理器(Controller)。它就像一个地图,根据请求的地址(URL)找到对应的目的地(Controller)。Spring MVC 提供了多种 HandlerMapping 实现,如 BeanNameUrlHandlerMapping、
RequestMappingHandlerMapping 等。
2.1.3 Controller
Controller 是处理请求的核心组件,它负责处理业务逻辑,并返回处理结果。在 Spring MVC 中,Controller 通常是一个普通的 Java 类,通过注解(如 @Controller、@RequestMapping)来标识。例如:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
// 使用 @Controller 注解将该类标识为一个控制器
@Controller
public class HelloController {
// 使用 @RequestMapping 注解将该方法映射到 /hello 路径
@RequestMapping("/hello")
// 使用 @ResponseBody 注解将方法的返回值直接作为响应体返回
@ResponseBody
public String hello() {
return "Hello, Spring MVC!";
}
}
2.1.4 ModelAndView
ModelAndView 是一个包含模型数据和视图信息的对象,它用于将模型数据传递给视图,并指定要使用的视图。例如:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
// 使用 @Controller 注解将该类标识为一个控制器
@Controller
public class UserController {
// 使用 @RequestMapping 注解将该方法映射到 /user 路径
@RequestMapping("/user")
public ModelAndView getUser() {
// 创建一个 ModelAndView 对象
ModelAndView modelAndView = new ModelAndView();
// 向模型中添加数据
modelAndView.addObject("username", "John");
// 设置要使用的视图名称
modelAndView.setViewName("user");
return modelAndView;
}
}
2.1.5 ViewResolver
ViewResolver 负责根据视图名称找到对应的视图对象。它就像一个翻译器,将视图名称翻译成实际的视图对象。Spring MVC 提供了多种 ViewResolver 实现,如
InternalResourceViewResolver、ThymeleafViewResolver 等。
写到这里我在思考一个问题:DispatcherServlet、HandlerMapping、Controller、ModelAndView、ViewResolver 他们之间有什么关系呢?我们清楚了吗?
代码示例
以下是一个简单的 Spring MVC 示例,展示了上述组件的使用:
配置 DispatcherServlet
在 web.xml 中配置 DispatcherServlet:
dispatcherServlet
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:spring-mvc.xml
1
dispatcherServlet
/
配置 Spring MVC
在 spring-mvc.xml 中配置 HandlerMapping、ViewResolver 等:
创建 Controller
package com.example.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello(Model model) {
model.addAttribute("message", "Hello, Spring MVC!");
return "hello";
}
}
创建视图
在 /WEB-INF/views/ 目录下创建 hello.jsp:
Hello Spring MVC
${message}
通过以上配置和代码,当客户端访问/hello时,DispatcherServlet会接收请求,通过HandlerMapping找到HelloController的hello方法进行处理,该方法返回视图名称hello和模型数据message,ViewResolver会将视图名称解析为hello.jsp,最后DispatcherServlet会将模型数据渲染到hello.jsp并返回给客户端,是不是很清楚了,对你有帮助吗,还是要总结一下他们的调用流程:
Spring MVC 核心组件调用流程关系
Spring MVC 是基于 Java 实现的一个经典的 Web 开发框架,其核心组件 DispatcherServlet、HandlerMapping、Controller、ModelAndView、ViewResolver 之间的调用流程如下:
客户端请求到达 DispatcherServlet
DispatcherServlet 是 Spring MVC 的核心前端控制器,负责接收所有客户端的 HTTP 请求。当客户端(如浏览器)发送一个请求到 Web 应用时,该请求首先会被 DispatcherServlet 拦截。
DispatcherServlet 借助 HandlerMapping 查找处理器
HandlerMapping 的作用是根据请求的 URL 等信息,将请求映射到具体的处理器(通常是 Controller 中的某个方法)。DispatcherServlet 会调用 HandlerMapping 来确定处理该请求的合适处理器。
DispatcherServlet 调用处理器(Controller)
找到对应的处理器后,DispatcherServlet 会将请求转发给相应的 Controller。Controller 是处理业务逻辑的组件,它会接收请求参数,调用业务服务进行处理,并返回处理结果。
Controller 返回 ModelAndView
Controller 处理完请求后,会返回一个 ModelAndView 对象。ModelAndView 包含了模型数据(Model)和视图名称(View)。模型数据用于传递给视图进行渲染,视图名称指定了要使用的视图。
DispatcherServlet 借助 ViewResolver 解析视图
ViewResolver 的作用是根据 ModelAndView 中的视图名称,解析出具体的视图对象。DispatcherServlet 会调用 ViewResolver 来完成视图解析工作。
DispatcherServlet 使用解析后的视图渲染模型数据
视图解析完成后,DispatcherServlet 会使用解析得到的视图对象,将 ModelAndView 中的模型数据渲染到视图中,并将渲染后的结果返回给客户端,写到这里我们是不是已经弄清楚了他们直接的关系了?那我们继续研究研究........
2.2 注解
Spring MVC 提供了丰富的注解,用于简化开发过程,下面介绍一些常用的注解:
2.2.1 @Controller
@Controller 注解用于将一个类标识为控制器。例如:
import org.springframework.stereotype.Controller;
// 使用 @Controller 注解将该类标识为一个控制器
@Controller
public class ProductController {
// 控制器方法
}
2.2.2 @RequestMapping
@RequestMapping 注解用于将请求映射到控制器的方法上。它可以用于类和方法上,用于指定请求的 URL 路径。例如:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
// 使用 @Controller 注解将该类标识为一个控制器
@Controller
// 将该类下的所有方法映射到 /product 路径下
@RequestMapping("/product")
public class ProductController {
// 将该方法映射到 /product/list 路径
@RequestMapping("/list")
@ResponseBody
public String getProductList() {
return "Product List";
}
}
2.2.3 @RequestParam
@RequestParam 注解用于获取请求参数。例如:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
// 使用 @Controller 注解将该类标识为一个控制器
@Controller
@RequestMapping("/user")
public class UserController {
// 将该方法映射到 /user/login 路径
@RequestMapping("/login")
@ResponseBody
public String login(@RequestParam("username") String username, @RequestParam("password") String password) {
if ("admin".equals(username) && "123456".equals(password)) {
return "Login Success";
} else {
return "Login Failed";
}
}
}
2.2.4 @PathVariable
@PathVariable 注解用于获取 URL 路径中的变量。例如:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
// 使用 @Controller 注解将该类标识为一个控制器
@Controller
@RequestMapping("/product")
public class ProductController {
// 将该方法映射到 /product/{id} 路径
@RequestMapping("/{id}")
@ResponseBody
public String getProductById(@PathVariable("id") int id) {
return "Product ID: " + id;
}
}
2.2.5 @RequestBody
@RequestBody 注解用于将请求体中的数据绑定到方法的参数上。通常用于处理 JSON 或 XML 格式的数据。例如:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
// 使用 @Controller 注解将该类标识为一个控制器
@Controller
@RequestMapping("/user")
public class UserController {
// 将该方法映射到 /user/add 路径
@RequestMapping("/add")
@ResponseBody
public String addUser(@RequestBody User user) {
// 处理用户添加逻辑
return "User Added";
}
}
class User {
private String username;
private String password;
// 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;
}
}
三、Spring MVC 的使用
3.1 环境搭建
要使用 Spring MVC,首先需要搭建开发环境,下面以 Maven 项目为例,介绍如何搭建 Spring MVC 开发环境:
创建 Maven 项目:使用 IDE(如 IntelliJ IDEA)创建一个 Maven 项目。
添加依赖:在 pom.xml 文件中添加 Spring MVC 相关的依赖:
org.springframework
spring-webmvc
5.3.10
javax.servlet
javax.servlet-api
4.0.1
provided
配置 DispatcherServlet:在 web.xml 文件中配置 DispatcherServlet:
dispatcher
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:spring-mvc.xml
1
dispatcher
/
配置 Spring MVC:在 spring-mvc.xml 文件中配置 Spring MVC:
3.2 创建控制器
创建一个控制器类,处理用户的请求:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
// 使用 @Controller 注解将该类标识为一个控制器
@Controller
public class HelloController {
// 使用 @RequestMapping 注解将该方法映射到 /hello 路径
@RequestMapping("/hello")
// 使用 @ResponseBody 注解将方法的返回值直接作为响应体返回
@ResponseBody
public String hello() {
return "Hello, Spring MVC!";
}
}
3.3 运行项目
将项目部署到 Servlet 容器(如 Tomcat)中,启动 Servlet 容器,访问
http://localhost:8080/hello,即可看到输出结果:Hello, Spring MVC!到这里我们也知道怎么使用了吧,解析是不是研究一下底层?
四、Spring MVC 底层实现
4.1 核心类和方法
Spring MVC 的底层实现涉及到许多核心类和方法,下面介绍一些重要的类和方法:
4.1.1 DispatcherServlet
DispatcherServlet 是 Spring MVC 的核心控制器,它继承自 FrameworkServlet,并实现了 Servlet 接口。DispatcherServlet 的核心方法是 doService,该方法负责处理所有的 HTTP 请求:
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 设置请求属性
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
try {
// 调用 doDispatch 方法处理请求
doDispatch(request, response);
}
finally {
// 清除请求属性
request.removeAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE);
request.removeAttribute(LOCALE_RESOLVER_ATTRIBUTE);
request.removeAttribute(THEME_RESOLVER_ATTRIBUTE);
request.removeAttribute(THEME_SOURCE_ATTRIBUTE);
}
}
4.1.2 HandlerMapping
HandlerMapping 是一个接口,它定义了根据请求查找处理器的方法:
public interface HandlerMapping {
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
4.1.3 HandlerAdapter
HandlerAdapter 是一个接口,它定义了调用处理器的方法:
public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
long getLastModified(HttpServletRequest request, Object handler);
}
4.1.4 ViewResolver
ViewResolver 是一个接口,它定义了根据视图名称解析视图的方法:
public interface ViewResolver {
View resolveViewName(String viewName, Locale locale) throws Exception;
}
4.2 底层原理
Spring MVC 的底层原理基于 Servlet 规范,通过 DispatcherServlet 作为前端控制器,将请求分发给相应的处理器进行处理。具体原理如下:
Servlet 容器启动:Servlet 容器(如 Tomcat)启动时,会加载 web.xml 文件,并创建 DispatcherServlet 实例。
初始化DispatcherServlet:DispatcherServlet 初始化时,会加载 Spring MVC 的配置文件(如 spring-mvc.xml),并创建 Spring 应用上下文。
接收请求:当客户端发送 HTTP 请求时,请求会被 DispatcherServlet 接收。DispatcherServlet 接收到请求后,会调用 HandlerMapping 来查找对应的处理器。HandlerMapping 是一个接口,它定义了根据请求查找处理器的方法 getHandler(HttpServletRequest request)。Spring MVC 提供了多种 HandlerMapping 的实现类,例如:
BeanNameUrlHandlerMapping:它会根据请求的 URL 去查找对应的 Bean 名称,将 Bean 作为处理器。例如,若请求的 URL 为 /hello,而在 Spring 容器中有一个名为 /hello 的 Bean,那么这个 Bean 就会被作为处理器。
RequestMappingHandlerMapping:这是 Spring MVC 中最常用的 HandlerMapping 实现类,它会根据 @RequestMapping 注解来匹配请求的 URL 和处理器方法。当请求到来时,它会遍历所有被 @RequestMapping 注解标注的方法,找到与请求 URL 匹配的方法作为处理器。
下面是
RequestMappingHandlerMapping 查找处理器的大致代码逻辑示例:
public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping {
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class> handlerType) {
// 从方法和类上获取 @RequestMapping 注解信息
RequestMapping methodAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);
if (methodAnnotation != null) {
RequestMapping typeAnnotation = AnnotatedElementUtils.findMergedAnnotation(handlerType, RequestMapping.class);
// 合并类和方法上的 @RequestMapping 注解信息
RequestMappingInfo info = createRequestMappingInfo(methodAnnotation, createCondition(context, typeAnnotation));
return info;
}
return null;
}
@Override
protected boolean matches(RequestMappingInfo info, HttpServletRequest request) {
// 检查请求是否与 RequestMappingInfo 匹配
return info.getPatternsCondition().getMatchingCondition(request) != null;
}
}
在
RequestMappingHandlerMapping 中,getMappingForMethod 方法用于从方法和类上获取 @RequestMapping 注解信息,并将其封装成 RequestMappingInfo 对象。matches 方法用于检查请求是否与 RequestMappingInfo 匹配。
调用处理器
当 HandlerMapping 找到对应的处理器后,DispatcherServlet 会调用 HandlerAdapter 来执行处理器。HandlerAdapter 是一个接口,它定义了调用处理器的方法 handle(HttpServletRequest request, HttpServletResponse response, Object handler)。Spring MVC 提供了多种 HandlerAdapter 的实现类,例如:
SimpleControllerHandlerAdapter:用于处理实现了 Controller 接口的处理器。
RequestMappingHandlerAdapter:用于处理使用 @RequestMapping 注解的处理器方法。
下面是
RequestMappingHandlerAdapter 调用处理器方法的大致代码逻辑示例:
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter {
@Override
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// 创建 WebDataBinderFactory,用于数据绑定
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// 创建 ModelFactory,用于管理模型数据
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 创建 WebRequestDataBinder,用于绑定请求参数到方法参数
WebRequestDataBinder binder = binderFactory.createBinder(request, null, handlerMethod.getMethod().getName());
// 绑定请求参数
binder.bind(request);
// 创建 ModelAndViewContainer,用于存储模型和视图信息
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, handlerMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
// 调用处理器方法
Object returnValue = invokeHandlerMethod(request, response, handlerMethod, mavContainer, binder);
// 处理返回值
processReturnValue(request, response, handlerMethod, returnValue, mavContainer);
if (!mavContainer.isRequestHandled()) {
// 返回 ModelAndView 对象
return getModelAndView(mavContainer, modelFactory, webRequest);
}
return null;
}
private Object invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod, ModelAndViewContainer mavContainer, WebDataBinder binder) throws Exception {
// 创建 ServletWebRequest 对象
ServletWebRequest webRequest = new ServletWebRequest(request, response);
// 创建 ServletInvocableHandlerMethod 对象
ServletInvocableHandlerMethod invocableMethod = new ServletInvocableHandlerMethod(handlerMethod);
// 设置参数解析器
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
// 设置返回值处理器
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
// 设置数据绑定工厂
invocableMethod.setDataBinderFactory(this.dataBinderFactory);
// 设置参数名称发现器
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
// 调用处理器方法
invocableMethod.invokeAndHandle(webRequest, mavContainer);
return mavContainer.getModel();
}
}
在
RequestMappingHandlerAdapter 中,handleInternal 方法是核心处理方法,它会完成数据绑定、调用处理器方法、处理返回值等操作。invokeHandlerMethod 方法用于实际调用处理器方法,它会创建
ServletInvocableHandlerMethod 对象,并设置参数解析器、返回值处理器等,最后调用 invokeAndHandle 方法执行处理器方法。
视图解析与渲染
处理器方法执行完成后,会返回一个 ModelAndView 对象,包含模型数据和视图信息。DispatcherServlet 会调用 ViewResolver 来解析视图名称,得到实际的 View 对象。ViewResolver 是一个接口,它定义了根据视图名称解析视图的方法 resolveViewName(String viewName, Locale locale)。Spring MVC 提供了多种 ViewResolver 的实现类,例如:
InternalResourceViewResolver:用于解析 JSP 视图,它会将视图名称加上前缀和后缀,得到 JSP 文件的实际路径。
ThymeleafViewResolver:用于解析 Thymeleaf 视图,它会根据 Thymeleaf 的模板引擎来渲染视图。
下面是
InternalResourceViewResolver 解析视图的大致代码逻辑示例:
public class InternalResourceViewResolver extends UrlBasedViewResolver {
@Override
protected Class> requiredViewClass() {
return InternalResourceView.class;
}
@Override
protected View createView(String viewName, Locale locale) throws Exception {
// 检查是否需要重定向
if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
return new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());
}
// 检查是否需要转发
if (viewName.startsWith(FORWARD_URL_PREFIX)) {
String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
return new InternalResourceView(forwardUrl);
}
// 创建 InternalResourceView 对象
return super.createView(viewName, locale);
}
}
在
InternalResourceViewResolver 中,createView 方法会根据视图名称的前缀判断是否需要重定向或转发,如果不需要,则创建 InternalResourceView 对象。
当 ViewResolver 解析出 View 对象后,DispatcherServlet 会调用 View 对象的 render 方法将模型数据渲染成 HTML 页面,并返回给客户端。例如,InternalResourceView 的 render 方法会使用 RequestDispatcher 转发请求到 JSP 文件进行渲染:
public class InternalResourceView extends AbstractUrlBasedView {
@Override
protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 将模型数据添加到请求属性中
exposeModelAsRequestAttributes(model, request);
// 转发请求到 JSP 文件
RequestDispatcher dispatcher = getRequestDispatcher(request, getUrl());
if (dispatcher == null) {
throw new ServletException("Could not get RequestDispatcher for [" + getUrl() + "]: Check that the corresponding file exists within your web application archive!");
}
// 设置转发属性
if (useInclude(request, response)) {
response.setContentType(getContentType());
dispatcher.include(request, response);
} else {
dispatcher.forward(request, response);
}
}
}
4.3 底层代码详细解释
类和方法解释
DispatcherServlet:
doService 方法:该方法是 DispatcherServlet 的核心服务方法,它会设置请求属性,然后调用 doDispatch 方法处理请求。在请求处理完成后,会清除请求属性。
doDispatch 方法:该方法负责整个请求的分发过程,包括查找处理器、调用处理器、解析视图、渲染视图等操作。
HandlerMapping:getHandler 方法:根据请求的 HttpServletRequest 对象查找对应的处理器,并返回一个 HandlerExecutionChain 对象,该对象包含处理器和拦截器。
HandlerAdapter:supports 方法:用于判断该 HandlerAdapter 是否支持处理指定的处理器。
handle 方法:调用处理器处理请求,并返回一个 ModelAndView 对象。
resolveViewName 方法:根据视图名称和 Locale 对象解析出实际的 View 对象。
参数解释
HttpServletRequest:封装了客户端的请求信息,包括请求方法、请求参数、请求头、请求体等。
HttpServletResponse:用于向客户端发送响应信息,包括响应状态码、响应头、响应体等。
HandlerExecutionChain:包含处理器和拦截器,拦截器可以在处理器执行前后进行一些额外的处理。
ModelAndView:包含模型数据和视图信息,用于将模型数据传递给视图,并指定要使用的视图。
View:表示一个视图对象,它的 render 方法用于将模型数据渲染成 HTML 页面。
五、Spring MVC 项目实战
5.1 项目需求分析
假设我们要开发一个简单的图书管理系统,该系统需要实现以下功能:
显示图书列表
添加图书
删除图书
5.2 数据库设计
我们可以设计一个简单的图书表,包含以下字段:
5.3 项目结构搭建
使用 Maven 创建一个 Spring MVC 项目,项目结构如下:
src
├── main
│ ├── java
│ │ └── com
│ │ └── example
│ │ ├── controller
│ │ │ └── BookController.java
│ │ ├── dao
│ │ │ └── BookDao.java
│ │ ├── model
│ │ │ └── Book.java
│ │ └── service
│ │ └── BookService.java
│ ├── resources
│ │ └── spring-mvc.xml
│ └── webapp
│ ├── WEB-INF
│ │ ├── views
│ │ │ ├── bookList.jsp
│ │ │ └── addBook.jsp
│ │ └── web.xml
│ └── index.jsp
└── test
└── java
5.4 代码实现
实体类
package com.example.model;
// 图书实体类
public class Book {
private int id;
private String title;
private String author;
// 构造方法
public Book() {
}
public Book(int id, String title, String author) {
this.id = id;
this.title = title;
this.author = author;
}
// getter 和 setter 方法
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
DAO 层
package com.example.dao;
import com.example.model.Book;
import java.util.ArrayList;
import java.util.List;
// 图书 DAO 类,模拟数据库操作
public class BookDao {
private static List books = new ArrayList<>();
// 获取所有图书
public List getAllBooks() {
return books;
}
// 添加图书
public void addBook(Book book) {
books.add(book);
}
// 删除图书
public void deleteBook(int id) {
books.removeIf(book -> book.getId() == id);
}
}
服务层
package com.example.service;
import com.example.dao.BookDao;
import com.example.model.Book;
import java.util.List;
// 图书服务类
public class BookService {
private BookDao bookDao = new BookDao();
// 获取所有图书
public List getAllBooks() {
return bookDao.getAllBooks();
}
// 添加图书
public void addBook(Book book) {
bookDao.addBook(book);
}
// 删除图书
public void deleteBook(int id) {
bookDao.deleteBook(id);
}
}
控制器层
package com.example.controller;
import com.example.model.Book;
import com.example.service.BookService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
// 图书控制器类
@Controller
@RequestMapping("/book")
public class BookController {
private BookService bookService = new BookService();
// 显示图书列表
@RequestMapping("/list")
public String showBookList(Model model) {
// 获取所有图书
List books = bookService.getAllBooks();
// 将图书列表添加到模型中
model.addAttribute("books", books);
// 返回视图名称
return "bookList";
}
// 显示添加图书页面
@RequestMapping("/add")
public String showAddBookPage() {
return "addBook";
}
// 处理添加图书请求
@RequestMapping(value = "/add", method = RequestMethod.POST)
public String addBook(@ModelAttribute("book") Book book) {
// 添加图书
bookService.addBook(book);
// 重定向到图书列表页面
return "redirect:/book/list";
}
// 处理删除图书请求
@RequestMapping("/delete/{id}")
public String deleteBook(@PathVariable("id") int id) {
// 删除图书
bookService.deleteBook(id);
// 重定向到图书列表页面
return "redirect:/book/list";
}
}
视图层
5.5 项目配置
web.xml 配置
- bookList.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
图书列表
图书列表
添加图书
ID
标题
作者
操作
${book.id}
${book.title}
${book.author}
删除
addBook.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
添加图书
添加图书
返回图书列表
代码解释
页面基本信息:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>:这是 JSP 页面的指令,指定了页面使用的编程语言为 Java,内容类型为 HTML,字符编码为 UTF - 8。
、
和 标签是标准的 HTML 标签,用于构建页面的结构。表单部分:
和 :这是一个输入框,用于输入图书的标题。label 标签的 for 属性与 input 标签的 id 属性对应,点击 label 时可以聚焦到对应的 input 框。name="title" 表示该输入框的数据在提交时会以 title 作为参数名传递。required 属性表示该输入框为必填项。
类似地, 和 用于输入图书的作者信息。
:这是一个提交按钮,点击该按钮会将表单数据提交到指定的 URL。
返回链接:
返回图书列表:这是一个超链接,点击后会跳转到图书列表页面,其 href 属性同样使用了 EL 表达式获取上下文路径,结合 /book/list 构成完整的跳转地址。
5.5 项目配置
web.xml 配置
BookManagementSystem
dispatcher
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:spring-mvc.xml
1
dispatcher
/
代码解释
org.springframework.web.servlet.DispatcherServlet。
spring-mvc.xml 配置
代码解释
InternalResourceViewResolver,用于解析 JSP 视图。
可以使用 Spring Initializr(https://start.spring.io/)来创建一个 Spring Boot 项目,选择 Spring Web 依赖,该依赖包含了 Spring MVC 和嵌入式 Tomcat。
6.2.2 编写控制器
5.6 项目测试与部署
测试
将项目部署到 Servlet 容器(如 Tomcat)中,启动容器后,访问
http://localhost:8080/BookManagementSystem/book/list(假设上下文路径为 BookManagementSystem),可以看到图书列表页面。点击 “添加图书” 链接,进入添加图书页面,输入图书标题和作者信息,点击 “添加” 按钮,表单数据会被提交到服务器,添加成功后会重定向到图书列表页面,此时可以看到新添加的图书。点击图书列表中的 “删除” 链接,可以删除对应的图书。
部署
可以将项目打包成 WAR 文件,然后将 WAR 文件部署到生产环境的 Servlet 容器中。部署过程通常包括将 WAR 文件复制到 Servlet 容器的 webapps 目录下,Servlet 容器会自动解压并部署该应用,到这里是不是把spring MVC复习、理解了一遍,有收获吗?那我们继续研究研究....
六、Spring Boot 整合 Spring MVC 和 Tomcat
6.1 为什么要整合
在传统的 Spring MVC 开发中,需要手动配置 Servlet 容器(如 Tomcat),并进行大量的 XML 配置,开发和部署过程较为繁琐。而 Spring Boot 提供了一种更加便捷的方式来开发和部署 Spring MVC 应用,它通过自动配置的方式,将 Spring MVC 和嵌入式 Servlet 容器(如 Tomcat)整合在一起,大大简化了开发和部署流程。
6.2 整合步骤
6.2.1 创建 Spring Boot 项目
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
// 使用 @RestController 注解将该类标识为一个 RESTful 控制器
@RestController
public class HelloController {
// 使用 @GetMapping 注解将该方法映射到 /hello 路径
@GetMapping("/hello")
public String hello() {
return "Hello, Spring Boot with Spring MVC!";
}
}
代码解释
@RestController:这是一个组合注解,相当于 @Controller 和 @ResponseBody 的组合,用于将该类标识为一个 RESTful 控制器,方法的返回值会直接作为响应体返回。
@GetMapping("/hello"):这是 @RequestMapping(method = RequestMethod.GET) 的缩写,用于将该方法映射到 /hello 路径,处理 HTTP GET 请求。
6.2.3 启动 Spring Boot 应用
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// 使用 @SpringBootApplication 注解标注主类
@SpringBootApplication
public class Application {
public static void main(String[] args) {
// 启动 Spring Boot 应用
SpringApplication.run(Application.class, args);
}
}
代码解释
@SpringBootApplication:这是一个组合注解,包含了 @Configuration、@EnableAutoConfiguration 和 @ComponentScan 注解,用于启用 Spring Boot 的自动配置和组件扫描功能。
SpringApplication.run(Application.class, args):这是启动 Spring Boot 应用的入口方法,它会创建并启动 Spring 应用上下文,同时启动嵌入式 Tomcat 服务器。
WebMvcAutoConfiguration 关键代码
6.3 底层原理
Spring Boot 的自动配置机制是整合 Spring MVC 和 Tomcat 的关键。当在项目中引入 Spring Web 依赖时,Spring Boot 会根据类路径下的依赖自动配置 Spring MVC 和嵌入式 Tomcat。
6.3.1 自动配置 Spring MVC
Spring Boot 通过 WebMvcAutoConfiguration 类来自动配置 Spring MVC。该类会根据条件自动配置 DispatcherServlet、HandlerMapping、HandlerAdapter、ViewResolver 等核心组件。例如,当类路径下存在 Thymeleaf 依赖时,会自动配置 ThymeleafViewResolver;当类路径下存在 Jackson 依赖时,会自动配置 JSON 数据的序列化和反序列化。
6.3.2 自动配置嵌入式 Tomcat
Spring Boot 通过
TomcatServletWebServerFactoryAutoConfiguration 类来自动配置嵌入式 Tomcat。该类会创建一个
TomcatServletWebServerFactory 实例,用于创建和启动嵌入式 Tomcat 服务器。在启动过程中,会将 DispatcherServlet 注册到 Tomcat 中,从而实现 Spring MVC 和 Tomcat 的整合。
6.3.3 源码分析
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
// 配置 Web MVC 的各种组件
}
}
代码解释
@
ConditionalOnWebApplication(type =
ConditionalOnWebApplication.Type.SERVLET):表示只有在 Servlet 类型的 Web 应用中才会进行配置。
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class }):表示只有当类路径下存在 Servlet、DispatcherServlet 和 WebMvcConfigurer 类时才会进行配置。
TomcatServletWebServerFactoryAutoConfiguration 关键代码@ConditionalOnMissingBean(
WebMvcConfigurationSupport.class):表示只有当 Spring 容器中不存在
WebMvcConfigurationSupport 类型的 Bean 时才会进行配置。
@Import(
EnableWebMvcConfiguration.class):导入 EnableWebMvcConfiguration 类,该类会配置 Spring MVC 的核心组件。
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(WebServerFactory.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class TomcatServletWebServerFactoryAutoConfiguration {
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory(
ObjectProvider connectorCustomizers,
ObjectProvider contextCustomizers,
ObjectProvider> protocolHandlerCustomizers) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.getTomcatConnectorCustomizers()
.addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatContextCustomizers()
.addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatProtocolHandlerCustomizers()
.addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
return factory;
}
}
代码解释
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }):表示只有当类路径下存在 Servlet、Tomcat 和 UpgradeProtocol 类时才会进行配置。
@ConditionalOnMissingBean(WebServerFactory.class):表示只有当 Spring 容器中不存在 WebServerFactory 类型的 Bean 时才会进行配置。
@
ConditionalOnWebApplication(type =
ConditionalOnWebApplication.Type.SERVLET):表示只有在 Servlet 类型的 Web 应用中才会进行配置。
@Bean 注解的
tomcatServletWebServerFactory 方法会创建一个
TomcatServletWebServerFactory 实例,并将自定义的 TomcatConnectorCustomizer、TomcatContextCustomizer 和
TomcatProtocolHandlerCustomizer 添加到工厂中,最后返回该工厂实例。
七、从不同角色角度分析和思考
7.1 Java 开发工程师角度
优势
开发效率高:Spring MVC 提供了丰富的注解和工具,如 @Controller、@RequestMapping 等,大大减少了开发人员编写样板代码的工作量。同时,Spring Boot 的自动配置机制进一步简化了开发流程,开发人员可以更专注于业务逻辑的实现。
代码可维护性强:遵循 MVC 设计模式,将业务逻辑、数据和视图分离,使得代码结构清晰,易于维护和扩展。例如,在图书管理系统中,BookController 负责处理请求,BookService 负责业务逻辑,BookDao 负责数据访问,各个模块职责明确。
生态丰富:Spring 框架拥有庞大的生态系统,开发人员可以方便地集成其他组件,如数据库访问框架(MyBatis、Hibernate)、缓存框架(Redis)等。
挑战
学习成本较高:Spring MVC 和 Spring Boot 涉及到大量的概念和配置,对于初学者来说,需要花费一定的时间来学习和掌握。例如,理解 DispatcherServlet 的工作原理、HandlerMapping 的匹配规则等。
调试难度大:由于 Spring 框架的自动配置机制,在出现问题时,可能很难定位问题的根源。例如,当视图解析失败时,需要检查视图解析器的配置、视图文件的路径等多个方面。
7.2 架构师角度
优势
灵活性高:Spring MVC 具有高度的灵活性,架构师可以根据项目的需求选择合适的组件和配置。例如,可以选择不同的 HandlerMapping、HandlerAdapter 和 ViewResolver 实现,以满足不同的业务场景。
可扩展性强:Spring 框架的模块化设计使得系统具有良好的可扩展性。架构师可以根据业务的发展,方便地添加新的功能模块。例如,在上述图书管理系统的基础上,如果业务需求扩展,需要增加图书分类管理和用户评论功能,架构师可以轻松地进行如下扩展:
增加图书分类管理模块
实体类扩展:创建 Category 实体类,用于表示图书分类信息。
package com.example.model;
// 图书分类实体类
public class Category {
private int id;
private String name;
// 构造方法
public Category() {
}
public Category(int id, String name) {
this.id = id;
this.name = name;
}
// getter 和 setter 方法
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- DAO 层扩展:创建 CategoryDao 类,用于处理图书分类的数据访问。
package com.example.dao;
import com.example.model.Category;
import java.util.ArrayList;
import java.util.List;
// 图书分类 DAO 类,模拟数据库操作
public class CategoryDao {
private static List categories = new ArrayList<>();
// 获取所有图书分类
public List getAllCategories() {
return categories;
}
// 添加图书分类
public void addCategory(Category category) {
categories.add(category);
}
// 删除图书分类
public void deleteCategory(int id) {
categories.removeIf(category -> category.getId() == id);
}
}
服务层扩展:创建 CategoryService 类,封装图书分类的业务逻辑。
package com.example.service;
import com.example.dao.CategoryDao;
import com.example.model.Category;
import java.util.List;
// 图书分类服务类
public class CategoryService {
private CategoryDao categoryDao = new CategoryDao();
// 获取所有图书分类
public List getAllCategories() {
return categoryDao.getAllCategories();
}
// 添加图书分类
public void addCategory(Category category) {
categoryDao.addCategory(category);
}
// 删除图书分类
public void deleteCategory(int id) {
categoryDao.deleteCategory(id);
}
}
控制器层扩展:创建 CategoryController 类,处理图书分类的请求。
package com.example.controller;
import com.example.model.Category;
import com.example.service.CategoryService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
// 图书分类控制器类
@Controller
@RequestMapping("/category")
public class CategoryController {
private CategoryService categoryService = new CategoryService();
// 显示图书分类列表
@RequestMapping("/list")
public String showCategoryList(Model model) {
// 获取所有图书分类
List categories = categoryService.getAllCategories();
// 将图书分类列表添加到模型中
model.addAttribute("categories", categories);
// 返回视图名称
return "categoryList";
}
// 显示添加图书分类页面
@RequestMapping("/add")
public String showAddCategoryPage() {
return "addCategory";
}
// 处理添加图书分类请求
@RequestMapping(value = "/add", method = RequestMethod.POST)
public String addCategory(@ModelAttribute("category") Category category) {
// 添加图书分类
categoryService.addCategory(category);
// 重定向到图书分类列表页面
return "redirect:/category/list";
}
// 处理删除图书分类请求
@RequestMapping("/delete/{id}")
public String deleteCategory(@PathVariable("id") int id) {
// 删除图书分类
categoryService.deleteCategory(id);
// 重定向到图书分类列表页面
return "redirect:/category/list";
}
}
视图层扩展:创建相应的 JSP 页面,如 categoryList.jsp 和 addCategory.jsp,用于显示图书分类列表和添加图书分类。
八、总结
Spring MVC 作为一个经典的 Java Web 框架,以其遵循 MVC 设计模式、代码解耦、开发效率高和生态丰富等优点,在 Java Web 开发领域占据着重要的地位。通过对 Spring MVC 的核心组件、注解、请求处理流程等知识点的深入学习,开发人员能够构建出功能强大、可维护性高的 Web 应用。
Spring Boot 的出现进一步简化了 Spring MVC 应用的开发和部署,其自动配置机制将 Spring MVC 和嵌入式 Tomcat 完美整合,使得开发人员可以更加专注于业务逻辑的实现。从不同角色的角度来看,Java 开发工程师可以利用 Spring MVC 和 Spring Boot 提高开发效率和代码质量;架构师可以借助其灵活性和可扩展性设计出复杂而稳定的系统;开源作者可以通过开源项目分享技术和提升影响力;框架开发者则可以在其基础上进行技术创新和优化。
随着互联网技术的不断发展,Spring MVC 和 Spring Boot 也将不断演进和完善,为 Java Web 开发带来更多的便利和可能性。开发人员需要不断学习和掌握这些技术,以适应不断变化的市场需求。同时,也可以积极参与开源社区,为技术的发展贡献自己的力量。
增加用户评论功能模块
实体类扩展:创建 Comment 实体类,用于表示用户评论信息。
package com.example.model;
// 用户评论实体类
public class Comment {
private int id;
private int bookId;
private String content;
private String username;
// 构造方法
public Comment() {
}
public Comment(int id, int bookId, String content, String username) {
this.id = id;
this.bookId = bookId;
this.content = content;
this.username = username;
}
// getter 和 setter 方法
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getBookId() {
return bookId;
}
public void setBookId(int bookId) {
this.bookId = bookId;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
DAO 层扩展:创建 CommentDao 类,用于处理用户评论的数据访问。
package com.example.dao;
import com.example.model.Comment;
import java.util.ArrayList;
import java.util.List;
// 用户评论 DAO 类,模拟数据库操作
public class CommentDao {
private static List comments = new ArrayList<>();
// 根据图书 ID 获取所有评论
public List getCommentsByBookId(int bookId) {
List bookComments = new ArrayList<>();
for (Comment comment : comments) {
if (comment.getBookId() == bookId) {
bookComments.add(comment);
}
}
return bookComments;
}
// 添加评论
public void addComment(Comment comment) {
comments.add(comment);
}
}
服务层扩展:创建 CommentService 类,封装用户评论的业务逻辑。
package com.example.service;
import com.example.dao.CommentDao;
import com.example.model.Comment;
import java.util.List;
// 用户评论服务类
public class CommentService {
private CommentDao commentDao = new CommentDao();
// 根据图书 ID 获取所有评论
public List getCommentsByBookId(int bookId) {
return commentDao.getCommentsByBookId(bookId);
}
// 添加评论
public void addComment(Comment comment) {
commentDao.addComment(comment);
}
}
控制器层扩展:在 BookController 中添加处理用户评论的方法。
package com.example.controller;
import com.example.model.Book;
import com.example.model.Comment;
import com.example.service.BookService;
import com.example.service.CommentService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
// 图书控制器类
@Controller
@RequestMapping("/book")
public class BookController {
private BookService bookService = new BookService();
private CommentService commentService = new CommentService();
// 显示图书详情页面,包含评论信息
@RequestMapping("/detail/{id}")
public String showBookDetail(@PathVariable("id") int id, Model model) {
// 获取图书信息
Book book = bookService.getBookById(id);
model.addAttribute("book", book);
// 获取图书的评论信息
List comments = commentService.getCommentsByBookId(id);
model.addAttribute("comments", comments);
return "bookDetail";
}
// 处理添加评论请求
@RequestMapping(value = "/addComment", method = RequestMethod.POST)
public String addComment(@ModelAttribute("comment") Comment comment) {
// 添加评论
commentService.addComment(comment);
// 重定向到图书详情页面
return "redirect:/book/detail/" + comment.getBookId();
}
}
视图层扩展:在 bookDetail.jsp 中添加显示评论和提交评论的表单。
通过以上扩展示例可以看出,Spring 框架的模块化设计使得架构师能够方便地根据业务需求添加新的功能模块,各个模块之间相互独立,耦合度低,易于维护和扩展。
架构设计复杂度增加:随着系统功能的不断扩展,架构设计变得更加复杂。架构师需要考虑模块之间的交互、数据的一致性、性能的优化等问题。例如,在添加图书分类和用户评论功能后,需要考虑如何确保图书和分类、评论之间的数据关联正确,以及如何优化查询性能。
系统性能管理:系统规模的扩大可能会导致性能问题。架构师需要对系统进行性能评估和优化,如使用缓存技术、数据库优化等。例如,在高并发情况下,频繁的数据库查询可能会导致性能瓶颈,架构师可以考虑使用 Redis 缓存来减轻数据库的压力。
社区支持强大:Spring 是一个开源的框架,拥有庞大的社区。开源作者可以借助社区的力量,获取反馈和建议,不断改进和完善自己的开源项目。例如,开源作者可以在 GitHub 上发布自己基于 Spring MVC 的开源项目,吸引其他开发者参与贡献和讨论。
技术影响力提升:开发基于 Spring MVC 的开源项目可以提升开源作者的技术影响力。如果项目得到广泛的使用和认可,开源作者将在技术社区中获得更高的声誉。例如,一些知名的 Spring 开源项目的作者,因其优秀的作品而成为技术领域的专家。
知识共享与学习:开源项目是知识共享的平台,开源作者可以与其他开发者分享自己的技术经验和见解,同时也可以从其他开发者那里学习到新的技术和思路。例如,在开发过程中,开源作者可以参考其他优秀的 Spring MVC 开源项目的代码和设计思路,不断提升自己的技术水平。
代码质量和规范:作为开源项目,代码质量和规范至关重要。开源作者需要遵循一定的代码规范,编写高质量、易读、易维护的代码。例如,使用有意义的变量名、添加详细的注释、进行单元测试等。
项目维护和更新:开源项目需要持续的维护和更新。开源作者需要及时处理用户的反馈和问题,修复漏洞,添加新功能。例如,随着 Spring 框架的不断升级,开源作者需要确保自己的项目与新版本的 Spring 框架兼容。
技术创新:作为框架开发者,可以在 Spring MVC 的基础上进行技术创新,引入新的特性和功能。例如,开发新的 HandlerMapping 实现,提供更灵活的请求映射规则;开发新的 ViewResolver 实现,支持更多的视图技术。
对底层原理的深入理解:框架开发者需要深入理解 Spring MVC 的底层原理,这有助于他们更好地进行框架的开发和优化。例如,了解 DispatcherServlet 的工作流程、HandlerAdapter 的调用机制等,能够在开发过程中避免一些潜在的问题。
推动技术发展:优秀的框架开发可以推动整个技术领域的发展。框架开发者可以通过不断改进和优化 Spring MVC 框架,为开发者提供更好的开发体验和工具,促进 Java Web 开发技术的进步。
兼容性问题:框架的升级和更新需要考虑与现有系统的兼容性。框架开发者需要确保新的版本不会影响到已有的项目,同时还要支持新的特性和功能。例如,在引入新的注解或配置方式时,需要考虑如何让旧项目能够平滑过渡。
性能优化:作为基础框架,性能是至关重要的。框架开发者需要不断对框架进行性能优化,减少内存占用、提高响应速度。例如,优化 HandlerMapping 的匹配算法,减少请求处理的时间。
附:流程图、思维图和架构图
8.1 Spring MVC 请求处理流程图
8.2 Spring MVC 核心组件思维图
8.3 Spring Boot 整合 Spring MVC 和 Tomcat 架构图
以上文章详细介绍了 Spring MVC 的相关知识,包括概念、历史背景、核心知识点、使用方法、底层实现、项目实战以及 Spring Boot 对其的整合,并从不同角色的角度进行了分析和思考,同时附上了相应的流程图、思维图和架构图,希望能对读者有所帮助,花了一天时间写这遍文章,希望大家多多支持点赞!