springMVC的执行流程
当用户向服务器发送请求时。
在servlet阶段,用户发送请求后,servlet端会接收用户端传来的数据,并通过service层进行业务的处理,再将处理后的结果,通过请求转发或者重定向的方式,反馈给用户。
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
System.out.println(username);
req.setAttribute("username", username);
req.getRequestDispatcher("/test/a.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
在整个过程中,我们需要再web.xml文件中配置相应的Servlet。当用户发送不同的请求,我们就需要创建不同的Servlet程序,并且在xml文件中进行配置。
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/helloServlet</url-pattern>
</servlet-mapping>
但是,在实际的开发过程中,一个web工程有很多不同的功能,如果这样,用户发送不一样的请求,就要新建一个servlet,就要配置一个servlet,这样会让代码冗余,而且工作量也十分大。那么怎么解决这个问题呢? SpringMVC很好的解决了这个问题。
在SpringMVC中,你不需要编写servlet程序,因此也不需要在 web.xml中对其进行配置。springMVC对其进行了分离,当用户发送请求时,SpringMVC会通过前端控制器(DispatcherServlet)对用户的请求进行拦截和过滤,分别通过处理器映射、处理器适配、视图解析器处理之后,将结果渲染到视图上。而在此过程中,在处理器适配完成之后,会找到相应的控制器(Controller),进行业务的执行。整个springMVC的执行看下图:
根据上图可以得出具体的执行原理:
1.用户发送请求之后,请求由前端控制器(DispatcherServlet)接收,前端控制器是整个SpringMVC框架的核心。
2.HandlerMapping,即处理器映射,会根据用户的请求来映射对应的Handler,即处理器。
3.HandlerExecution,即处理器执行,通过处理器的执行来找到相应的控制器。
4.将得到的控制器返回
5.HandlerAdapter,处理器适配,根据特定的规则去查找适配的处理器。
6.Controller,控制器,执行适配的处理器,并处理具体的业务,再将ModelAndView返回给前端控制器
7.ViewResolver,视图解析器,将ModelAndView进行解析,并渲染到视图上
其实,在使用springMVC进行web开发的过程中,我们不需要关心前端控制器是如何来进行过滤拦截等操作的,我们只需要关注业务层的相关代码以及Controller,其他SpringMVC已经帮我们在底层实现了,我们直接用就可以了。但是为了搞清楚整个执行过程是很有必要的,可以帮助我们更好的去理解SpringMVC的优点。
编写一个实例如下:
前面说到,一切的工作都是由前端控制器来执行的,因此需要再web.xml配置DispatcherServlet的参数信息,设置参数,绑定到相对应的spring配置文件中
<!-- 用户发送请求,会通过DispatcherServlet 前端控制器,请求分发器 这是整个MVC的核心 -->
<!-- 配置DispatcherServlet -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!-- 配置参数, 需要绑定的spring的配置文件 -->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!-- 设置启动级别,1: 服务器启动就开启 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
配置好前端控制器,那么当用户发送请求的时候,就会被它所过滤和拦截。
接下来,按照图中的信息,我们需要三个部分的处理,分别是:处理器映射、处理器适配、视图解析。这些事情全部都在spring的配置文件中去配置,先上代码:
<!-- 处理器映射器 前端控制器会根据用户请求,调用处理器映射,根据请求的url来查找对应的Handler
查到对应的处理器之后,会调用处理器你执行器,查找对应的控制器,并返回给前端控制器-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
<!-- 处理器适配器 前端控制器会调用处理器适配,并执行处理器,在将具体的业务交给适配的控制器去处理-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
<!-- 视图解析器, 控制器处理相关业务之后,就会返回一个ModelAndView,视图解析器就会对其进行解析-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<!-- 视图解析器会根据返回的参数,将数据拼接成一个完整的url,返回给用户 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
先不管上述代码,前面说到,我们只需要编写Controller以及业务层的相关代码就可以了,那Controller程序怎么编写呢?其实spring为我们提供了一个Controller接口,我们只需要去实现这个接口就可以了。
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.ModelAndView;
@FunctionalInterface
public interface Controller {
@Nullable
ModelAndView handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws Exception;
}
可以看出,Controller是一个函数型接口, 里面只有一个方法,而该方法刚好返回的就是一个ModelAndView对象,正是我们所需要的,
//适配器进过处理后,适配相对应的Controller控制器
public class HelloController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//创建一个ModelAndView实例
ModelAndView modelAndView = new ModelAndView();
//具体业务
String result = "HelloSpringMVC";
modelAndView.addObject("msg", result);
//视图跳转到具体的那个页面
//视图解析器会自动拼接成完整的url
modelAndView.setViewName("test");
//返回一个模型视图
return modelAndView;
}
}
我们需要先new一个ModelAndView对象,然后调用业务方法完成业务操作,紧接着设置要跳转的页面,最后将视图返回。假设我们的业务是在对应的页面上打印“HelloSpringMVC”,addObject方法类似于req.serAttribute()方法, 参数是K-V的形式。最后,在spring配置文件中,初始化bean对象
<!-- 初始化控制器bean对象 -->
<bean id="/hello" class="com.springmvc.controller.HelloController" />
想必你还是不是很理解springMVC到底是怎么执行的,那么就对应着具体代码来分析。
首先,我们需要一个前端控制器,而这个前端控制器需要与网页进行交互,因此需要再web.xml文件中配置,而这个类springMVC已经为我们提供好了。并且,有上面的大致分析可以看出,前端控制器需要通过处理器映射、处理器适配对用户的请求进行相关的业务操作,最后再由视图解析对结果进行渲染。那么,通过一个spring的配置文件来实现一系列的操作。
处理器的映射与执行:假定用户现在访问的是这个url: http://localhost:8080/hello, 那么通过处理器的映射后拿到url,在通过处理器的执行器来查找对应的控制器,即“/hello”
<!-- 处理器映射器 前端控制器会根据用户请求,调用处理器映射,根据请求的url来查找对应的Handler
查到对应的处理器之后,会调用处理器你执行器,查找对应的控制器,并返回给前端控制器-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
处理器适配; 前端控制器会将处理器进行适配,找到符合要求的控制器,即Controller,再由它进行业务操作
<!-- 处理器适配器 前端控制器会调用处理器适配,并执行处理器,在将具体的业务交给适配的控制器去处理-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
通过上面两个步骤,就可以准确的找到对应的控制器,而这个控制器就是spring中配置的这个bean对象
<!-- 初始化控制器bean对象 -->
<bean id="/hello" class="com.springmvc.controller.HelloController" />
//适配器进过处理后,适配相对应的Controller控制器
public class HelloController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//创建一个ModelAndView实例
ModelAndView modelAndView = new ModelAndView();
//具体业务
String result = "HelloSpringMVC";
modelAndView.addObject("msg", result);
//视图跳转到具体的那个页面
//视图解析器会自动拼接成完整的url
modelAndView.setViewName("test");
//返回一个模型视图
return modelAndView;
}
}
HelloController经过操作以后返回了一个ModelAndView实例,上面的代码可以看出当完成业务之后,视图会跳转到指定的页面,而其中只设置了一个"test" , 很显然,这不是一个完整的页面url,因此,视图解析器需要出场了:
<!-- 视图解析器, 控制器处理相关业务之后,就会返回一个ModelAndView,视图解析器就会对其进行解析-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<!-- 视图解析器会根据返回的参数,将数据拼接成一个完整的url,返回给用户 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
视图解析器有两个参数,分别表示的是前缀和后缀,意思就是,视图解析器会自动的帮你拼接url即: /WEB-INF/jsp/test.jsp, 那么现在就是一个完整的url了
最后就会将结果渲染在视图上