0%

SpringMVC

执行流程:

57ea9e7edeebd5ee2ec0cf27313c5fb6__2

DispatcherServlet(前端控制器):接收响应请求,调配其他组件。

handlermapping(处理器映射器):根据url查找可以处理当前请求的处理器。(可以通过xml配置方式,注解方式)。

HandlerAdaptor(处理器适配器):接收转换前端页面参数,根据特定规则执行处理器,

Handler(处理器,后端控制器):需要程序员去编写,常用注解开发方式。

​ Handler处理器执行后结果是ModelAndView,具体开发时Handler返回方法值类型包括: ModelAndView、String (逻辑视图名)、void (通过在Handler形参中添加request和response,类似原始servlet 开发方式,注意:可以通过指定response响应的结果类型实现json数据输出)。

View resolver(视图解析器):跳转到指定的页面,并将后台的值解析到jsp视图页面。

View视图:jsp页面,仅是数据展示,没有业务逻辑。

在web.xml配置前端控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 前端控制器 -->		
<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>*.action</url-pattern>
</servlet-mapping>

在springMVC.xml文件配置

1
2
3
4
5
6
7
8
<!-- 配置Handler处理器 ·-->
<bean id="MyController" name="/myssm.action" class="com.syl.controller.MyController"></bean>
<!-- 处理器映射器 1-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
<!-- 处理器适配器1 要求处理器实现Cotroller接口·-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"></bean>

处理器映射器

映射器1在springmvc.xml文件中的配置

1
2
3
4
<!-- 配置Handler处理器 ·-->
<bean name="/myssm.action" class="com.syl.controller.MyController"></bean>
<!-- 处理器映射器 1-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>

映射器2在springmvc.xml文件中的配置

1
2
3
4
5
6
7
8
9
10
11
<bean id="MyController" class="com.syl.controller.MyController"></bean>
<!-- 处理器映射器 2-->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<!-- key为映射的url 值为上面处理器的id -->
<prop key="/a.action">MyController</prop>
<prop key="/b.action">MyController</prop>
</props>
</property>
</bean>

总结:多个映射器可以并存,前端控制器判断url能让哪些映射器映射,就让正确的映射器处理

处理器适配器

实现controller接口

在springmvc.xml文件中的配置

1
2
3
4
<!-- 配置Handler处理器 ·-->
<bean id="MyController" name="/myssm.action" class="com.syl.controller.MyController"></bean>
<!-- 处理器适配器·-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>

在controller层的代码:

1
2
3
4
5
6
7
8
9
10
public class MyController implements Controller{
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
String msg="<h1>welcome springmvc!</h1>";
ModelAndView view = new ModelAndView();
view.addObject("msg",msg);
view.setViewName("index.jsp");
return view;
}
}

实现HttpRequestHandler接口

在springmvc.xml文件中的配置

1
2
3
4
<!-- 配置Handler处理器 ·-->
<bean name="/ssm2.action" class="com.syl.controller.MyController2"></bean>
<!-- 处理器适配器·-->
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"></bean>

在controller层的代码:

1
2
3
4
5
6
7
8
public class MyController2 implements HttpRequestHandler{
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String msg="<h1>welcome TO springmvc!</h1>";
request.setAttribute("msg", msg);
request.getRequestDispatcher("index.jsp").forward(request, response);
}
}

两种处理器适配器可以并存,各司其职所有处理器适配器都实现HandleAdapter接口


但是,测试发现,如果不配置处理器适配器程序也可以正常运行

原因:

​ 发现一个配置文件:/org/springframework/web/servlet/DispatcherServlet.properties

在里面发现:

1
2
3
4
5
6
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

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

注解的处理器映射器和适配器

注解的映射器和适配器必须配对使用,要么都使用注解形式,要么都使用xml配置形式

首先在springmvc.xml文件中开启springmvc注解支持以及包扫描

1
2
3
4
<!--包扫描-->
<context:component-scan base-package="com.syl"/>
<!--注解支持-->
<mvc:annotation-driven/>

然后在controller中的代码

1
2
3
4
5
6
7
8
9
10
11
12
@Controller//指定该类为处理器类
public class MyController3{

@RequestMapping("showMsg") //将url绑定到该方法,建议和方法名一致
public ModelAndView showMsg() {
String msg="<h1>welcome springmvc!</h1>";
ModelAndView view = new ModelAndView();
view.addObject("msg",msg);
view.setViewName("index.jsp");
return view;
}
}

源码分析

  • 首先前端控制器DispacherServlet调用自身的doDispatch()方法,在doDispatch()方法中执行

  • 前段控制器调用处理器映射器查找Handler

    1
    mappedHandler = getHandler(processedRequest);
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
    for (HandlerMapping mapping : this.handlerMappings) {
    HandlerExecutionChain handler = mapping.getHandler(request);
    if (handler != null) {
    return handler;
    }
    }
    }
    return null;
    }

可以得到一个处理器执行链

  • 之后执行doDispatch()方法中的下面代码可以得到一个处理器适配器

    1
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters != null) {
    for (HandlerAdapter adapter : this.handlerAdapters) {
    if (adapter.supports(handler)) {
    return adapter;
    }
    }
    }
    throw new ServletException("No adapter for handler [" + handler +
    "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    }
  • 通过得到的处理器适配器得到一个ModelAndView

    1
    2
    ModelAndView mv = null;
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  • 执行以下代码

    1
    processDispatchResult(processedRequest,response,mappedHandler, mv, dispatchException);

    然后执行processDispatchResult()方法中的render方法

    1
    render(mv, request, response);

    通过视图解析器将ModelAndVIew解析成View

    1
    2
    String viewName = mv.getViewName();
    view = resolveViewName(viewName, mv.getModelInternal(), locale, request);

    然后通过view的渲染方法

    1
    view.render(mv.getModelInternal(), request, response);

    再然后经过层层跳转来到这里将ModelAndView填充到request作用域

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    protected void exposeModelAsRequestAttributes(Map<String, Object> model,
    HttpServletRequest request) throws Exception {

    model.forEach((name, value) -> {
    if (value != null) {
    request.setAttribute(name, value);
    }
    else {
    request.removeAttribute(name);
    }
    });
    }

    发现其实ModelAndView就是一个map…

赏口饭吃吧!