以下是关于“详解SpringMVC从基础到源码”的完整攻略,其中包含两个示例。
详解SpringMVC从基础到源码
SpringMVC是一个基于MVC模式的Web框架,它提供了一种灵活、高效的方式来开发Web应用程序。在本攻略中,我们将从基础概念到源码实现,全面讲解SpringMVC的工作原理和实现细节。
SpringMVC基础概念
MVC模式
MVC模式是一种软件设计模式,它将应用程序分为三个部分:模型、视图和控制器。模型表示应用程序的数据和业务逻辑,视图表示应用程序的用户界面,控制器负责协调模型和视图之间的交互。
在SpringMVC中,模型、视图和控制器分别对应以下组件:
- 模型:业务逻辑组件和数据访问组件。
- 视图:JSP、Thymeleaf等模板引擎。
- 控制器:DispatcherServlet。
DispatcherServlet
DispatcherServlet是SpringMVC的核心组件,它负责接收所有的请求,并将请求分发给对应的控制器进行处理。在SpringMVC中,我们需要在web.xml文件中配置DispatcherServlet。
以下是一个web.xml文件的示例:
<web-app>
<servlet>
<servlet-name>Servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/dispatcherServlet-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url>
</servlet-mapping>
</web-app>
在本示例中,我们定义了一个名为dispatcherServlet的Servlet,并将其映射到根路径"/"。我们使用
HandlerMapping
HandlerMapping是SpringMVC的组件之一,它负责将请求映射到对应的控制器进行处理。在SpringMVC中,我们可以使用多种方式来配置HandlerMapping,例如注解、XML配置等。
以下是一个使用注解配置HandlerMapping的示例:
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.example.controller")
public class AppConfig implements WebMvcConfigurer {
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("/static/");
}
}
在本示例中,我们使用@Configuration注解标识AppConfig类为配置类,并使用@EnableWebMvc注解启用SpringMVC。我们使用@ComponentScan注解指定了控制器所在的包路径。在configureDefaultServletHandling()方法中,我们启用了默认的Servlet处理。在addViewControllers()方法中,我们添加了一个视图控制器,将根路径"/"映射到index视图。在addResourceHandlers()方法中,我们添加了一个资源处理器,将/static/路径下的静态资源映射到/static/**路径。
HandlerAdapter
HandlerAdapter是SpringMVC的组件之一,它负责将请求分发给对应的控制器进行处理,并将处理结果封装成ModelAndView对象返回给DispatcherServlet。在SpringMVC中,我们可以使用多种方式来配置HandlerAdapter,例如注解、XML配置等。
以下是一个使用注解配置HandlerAdapter的示例:
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.example.controller")
public class AppConfig implements WebMvcConfigurer {
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
adapter.setArgumentResolvers(Collections.singletonList(new RequestParamMethodArgumentResolver()));
adapter.setMessageConverters(Collections.singletonList(new MappingJackson2HttpMessageConverter()));
return adapter;
}
}
在本示例中,我们使用@Bean注解定义了一个名为requestMappingHandlerAdapter的HandlerAdapter,并使用setArgumentResolvers()方法和setMessageConverters()方法分别配置了请求参数解析器和消息转换器。
SpringMVC源码实现
DispatcherServlet源码实现
DispatcherServlet的源码实现位于org.springframework.web.servlet包中。以下是DispatcherServlet的核心方法doDispatch()的源码实现:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
在本方法中,我们首先调用checkMultipart()方法对请求进行处理,然后调用getHandler()方法获取对应的HandlerExecutionChain对象。如果获取不到HandlerExecutionChain对象,则调用noHandlerFound()方法进行处理。如果获取到HandlerExecutionChain对象,则调用getHandlerAdapter()方法获取对应的HandlerAdapter对象,并调用handle()方法进行处理。最后,我们调用processDispatchResult()方法对处理结果进行处理。
HandlerMapping源码实现
HandlerMapping的源码实现位于org.springframework.web.servlet包中。以下是RequestMappingHandlerMapping的源码实现:
public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo> {
@Override
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
super.registerHandlerMethod(handler, method, mapping);
}
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
info = typeInfo.combine(info);
}
}
return info;
}
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
RequestMapping requestMapping = AnnotationUtils.getAnnotation(element, RequestMapping.class);
if (requestMapping != null) {
return RequestMappingInfo.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
.methods(requestMapping.method())
.params(requestMapping.params())
.headers(requestMapping.headers())
.consumes(requestMapping.consumes())
.produces(requestMapping.produces())
.mappingName(requestMapping.name())
.options(this.config)
.build();
}
return null;
}
}
在本示例中,我们继承了AbstractHandlerMethodMapping类,并实现了registerHandlerMethod()方法和getMappingForMethod()方法。在registerHandlerMethod()方法中,我们调用了父类的registerHandlerMethod()方法。在getMappingForMethod()方法中,我们调用了createRequestMappingInfo()方法创建RequestMappingInfo对象,并使用combine()方法将RequestMappingInfo对象合并。
HandlerAdapter源码实现
HandlerAdapter的源码实现位于org.springframework.web.servlet包中。以下是RequestMappingHandlerAdapter的源码实现:
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter<RequestMappingInfo> {
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
return true;
}
@Override
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, handlerMethod);
Object result = handlerMethod.invokeForRequest(webRequest, mavContainer);
if (result == null) {
if (isRequestNotModified(request, handlerMethod.getMethod())) {
return null;
}
handleResponseBody(null, mavContainer, webRequest);
}
else if (StringUtils.hasText(mavContainer.getViewName())) {
handleResponseBody(result, mavContainer, webRequest);
}
else {
mavContainer.setRequestHandled(true);
}
return getModelAndView(mavContainer);
}
}
在本示例中,我们继承了AbstractHandlerMethodAdapter类,并实现了supportsInternal()方法和handleInternal()方法。在supportsInternal()方法中,我们返回true表示支持所有的HandlerMethod对象。在handleInternal()方法中,我们首先创建了ServletWebRequest对象和ModelAndViewContainer对象,并调用initModel()方法初始化ModelAndViewContainer对象。然后,我们调用invokeForRequest()方法执行HandlerMethod对象,并获取处理结果。如果处理结果为null,则调用handleResponseBody()方法处理响应体。如果处理结果不为null且ModelAndViewContainer对象中包含视图名称,则调用handleResponseBody()方法处理响应体。否则,我们将ModelAndViewContainer对象的requestHandled属性设置为true。最后,我们调用getModelAndView()方法获取ModelAndView对象并返回。
示例1
以下是一个使用SpringMVC的示例:
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping(value = "/list", method = RequestMethod.GET)
public ModelAndView list() {
List<User> userList = userService.getUserList();
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("userList", userList);
modelAndView.setViewName("userList");
return modelAndView;
}
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ModelAndView detail(@PathVariable("id") Long userId) {
User user = userService.getUserById(userId);
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("user", user);
modelAndView.setViewName("userDetail");
return modelAndView;
}
}
在本示例中,我们使用@Controller注解标识UserController类为控制器,并使用@RequestMapping注解指定了请求路径、请求方法、请求参数等信息。在list()方法中,我们调用了UserService的getUserList()方法获取用户列表,并将用户列表添加到ModelAndView对象中。在detail()方法中,我们调用了UserService的getUserById()方法获取指定ID的用户,并将用户信息添加到ModelAndView对象中。
示例2
以下是一个使用Thymeleaf模板引擎的示例:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>User Detail</title>
</head>
<body>
<h1>User Detail</h1>
<table>
<tr>
<td>ID:</td>
<td th:text="${user.id}"></td>
</tr>
<tr>
<td>Name:</td>
<td th:text="${user.name}"></td>
</tr>
<tr>
<td>Age:</td>
<td th:text="${user.age}"></td>
</tr>
</table>
</body>
</html>
在本示例中,我们使用Thymeleaf模板引擎来渲染视图。我们使用th:text指令来显示用户的ID、姓名和年龄。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解SpringMVC从基础到源码 - Python技术站