上篇文章我们通过手写分析了SpringMVC中的Controller的两种实现方式。接下来我们来看看在SpringMVC中具体是如何使用的。
引入相关的依赖
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.3.18version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.0.1version>
<scope>providedscope>
dependency>
然后创建自定义的Controller
/**
* 自定义控制器
* 必须实现Controller接口
* @author dpb【波波烤鸭】
*
*/
public class UserController implements Controller{
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("本方法被调用了...");
ModelAndView view = new ModelAndView();
view.setViewName("/index.jsp");
return view;
}
}
然后添加SpringMVC的配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter">bean>
<bean name="/hello.action" class="com.boge.controller.UserController" />
beans>
我们需要在配置文件中配置对应的 HandlerMapping
和 HandlerAdapter
最后在web.xml中配置下前端控制器就可以了
test
index.html
index.htm
index.jsp
default.html
default.htm
default.jsp
springmvc
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:spring-mvc.xml
springmvc
/
然后就可以启动服务来访问了
通过上面的案例分析,我们可以看的出来在 web.xml
中配置了一个 Servlet
当用户请求到来的时候都会拦截处理。所以我们通过DispatcherServlet来作为入口分析。
既然是一个Servlet。我们就需要分析对应的生命周期的方法
init方法中会完成相关的初始化操作。我们来看看完成了哪些初始化的操作。我们可以直接进入到FrameworkServlet的 initServletBean()
方法中查看 initWebApplicationContext()
方法中。
进入后:
上面的IoC容器的初始化过程前面介绍Spring的时候重点讲解过,先不关注。直接看 onRefresh
方法
我们看下 initHandlerMapping
方法。
上面的代码我们可以看到逻辑很简单,会先读取xml文件中配置的HandlerMapping类型的信息,如果没有就读取默认的。案例中我们配置的有
如果没有配置的逻辑为就会读取 DispatcherServlet.properties 文件中内容
DispatcherServlet.properties中的信息为:
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
org.springframework.web.servlet.function.support.RouterFunctionMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
org.springframework.web.servlet.function.support.HandlerFunctionAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
到这我们就清楚了init方法做了两件事情
service方法是在用户请求到来的时候触发的。也就是具体处理请求的方法。我们来看下,直接进入到doDispatch方法中
获取对应的适配器
调用ha.handle方法
然后就进入到了自定义的控制器中了。
在destory方法中的处理就很简单。完成IoC容器的关闭操作
public void destroy() {
this.getServletContext().log("Destroying Spring FrameworkServlet '" + this.getServletName() + "'");
if (this.webApplicationContext instanceof ConfigurableApplicationContext && !this.webApplicationContextInjected) {
((ConfigurableApplicationContext)this.webApplicationContext).close();
}
}
注解方式的使用我们需要在配置文件中添加相关的标签来开启
<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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 开启注解 -->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 开启扫描 -->
<context:component-scan base-package="com.boge.controller"></context:component-scan>
</beans>
然后我们就可以在自定义控制器中通过@Controller注解和@RequestMapping注解来做方法级别的映射
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/do1")
@ResponseBody
public String doSome1(){
return "do1";
}
@RequestMapping("/do2")
@ResponseBody
public String doSome2(){
return "do2";
}
}
我们可以看到添加了一个标签后就开启了注解的使用方式,那么他的内部是怎么执行的呢?因为是自定义标签,所以在SpringMVC中会提供相关的解析器。这块我们可以看下源码中的内容
看到提供了一个处理器MvcNamespaceHandler。
可以看到,对应的标签都映射了对应的解析器,也就是解析到这个标签的时候,会找到对应的解析器来解析操作。然后我们进入到 AnnotationDrivenBeanDefinitionParser
中可以看看具体的解析逻辑。
可以看到。在这块完成了核心的 HandlerMapping
和 HandlerAdapter
注入到了容器中。那么在DispatcherServlet中处理请求的时候就会通过对应的Handler来处理了。
这块的串联是在IoC的加载解析xml文件的逻辑中处理的。
然后我们来看下载SpringBoot项目中是怎么自动装配SpringMVC框架的,首先我们找到对应的配置类
同时我们也需要关注下这个配置类
在这个配置类中注入的HandlerMapping和HandlerAdapter的具体实现类
同时也注入了DispatcherServlet