SpringMVC原理详解

SpringMVC是一种基于Java实现的MVC设计模式的轻量级Web框架,它是Spring框架的一部分,主要用于简化Web开发。‌ SpringMVC采用MVC架构模式,将应用程序分为模型(Model)、视图(View)和控制器(Controller)三个层次,从而实现业务逻辑、数据和界面的分离。模型层负责处理数据,视图层负责展示数据,而控制器层则负责根据输入调用相应的模型和视图。‌

SpringMVC的优点包括结构松散、易于扩展和与Spring框架无缝集成。它支持多种视图技术,如JSP,并且各个模块分离,耦合度低,使得系统更加灵活和易于维护。在开发过程中,SpringMVC通过前端控制器DispatcherServlet来接收请求,通过HandlerMapping查找相应的处理器(Controller),然后通过适配器(Adapter)调用控制器方法,最终返回ModelAndView对象,由视图解析器解析并渲染视图,最终向用户响应结果。

springmvc与servlet的关系:Springmvc对servlet进行了封装,servlet的性能较好,springmvc的开发效率较高。

HandlerExecutionChain mappedHandler=getHandler(processedRequest,false);
HandlerAdapter ha=getHandlerAdapter(mappedHandler.getHandler);
ModelAndView mv=ha.handle(processedRequest,response,mappedHandler.getHandler());

1、用户通过浏览器发送url请求(如http://localhost:8080/spring-mvc/queryItems.action),前端控制器DispatcherServlet接收到用户发送的url请求,通过DispatcherServlet类中的doService方法调用doDispatch方法处理请求。

2、在doDispatch方法中调用getHandler方法,进而调用处理器映射器HandlerMapping的getHandler方法,根据url请求获取处理器执行链HandlerExecutionChain,其中包括能处理url请求的handler(例如此处处理映射路径为/queryItems.action的handler)和多个HandlerInterceptor拦截器。

3、在doDispatch方法中调用DispatcherServlet对象的getHandlerAdapter方法获取能够执行handler的处理器适配器HandlerAdapter对象。在doDispatch方法中调用HandlerAdapter对象的handle方法执行handler,并返回ModelAndView对象给前端控制器。ModelAndView对象中存储了用户请求的数据,以及显示数据的视图。通过ModelAndView对象的addObject方法将用户请求的数据存储到model属性(实际上是Map)中,通过ModelAndView对象的setViewName方法或构造器将要显示数据的逻辑视图存储到view属性中。

4、在doDispatch方法中调用DispatcherServlet对象的processDispatchResult方法,在processDispatchResult方法中调用DispatcherServlet对象的render方法,在render方法中调用DispatcherServlet对象的resolveViewName方法,进而调用视图解析器ViewResolver对象的resolveViewName方法,通过视图解析器ViewResolver将逻辑视图名拼接得到物理视图view,即前缀prefix+逻辑视图名+后缀suffix。

5、前端控制器得到物理视图view后,将模型数据填充到request域,进行视图渲染,并将渲染结果输出给Response对象。在DispatcherServlet对象的render方法中调用视图view对象的render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)方法,该方法的职责就是接收model对象、Request对象、Response对象,并渲染输出结果给Response对象。其中,在view对象的render方法中调用view对象的renderMergedOutputModel方法,接着调用view对象的exposeModelAsRequestAttributes方法,该方法没有被子类重写,所以调用的是父类AbstractView的方法,即调用被传递到了AbstractView类的exposeModelAsRequestAttributes方法中,就是在这个方法中,模型数据被一个一个的放入到了HttpServletRequest对象中。

6、前端控制器将响应Response对象返回给用户,用户可以看到请求的页面。

在后端开发过程中,开发人员只需要编写controller层和model层(包括service层和mapper层)代码,view层的代码是由前端开发人员编写的(在前后端分离之前,view层的代码是通过后端人员编写的jsp文件实现的)。

jsp与servlet有什么区别?

答:jsp与servlet差不多,都可以加载动态页面。jsp是在HTML页面中加入java代码,侧重于视图,servlet是在java代码中加入HTML标签,侧重于业务逻辑,jsp完成的功能,都可以通过servlet来完成。

1、前端控制器DispatcherServlet源码

2、ModelAndView

public class ModelAndView {  
    private Object view;//该属性用来存储返回的视图信息
	private ModelMap model;//该属性用来存储处理后的结果数据
	private boolean cleared = false;  
	public ModelAndView() {  
	}  
	public ModelAndView(String viewName) {  
		this.view = viewName;  
	}  
	public ModelAndView(View view) {  
		this.view = view;  
	}
	public void setViewName(String viewName) {  
		this.view = viewName;  
	}  
	public String getViewName() {  
		return (this.view instanceof String ? (String) this.view : null);  
	} 
	public void setView(View view) {  
		this.view = view;  
	}  
	public View getView() {  
		return (this.view instanceof View ? (View) this.view : null);  
	}
	/*如果视图是通过名称指定的,并且要由DispatcherServlet通过ViewResolver解析,则返回true*/
	public boolean isReference() {
		return (this.view instanceof String);
	}
	......
}

3、视图解析器

在Spring MVC 中,controller不会负责具体的页面渲染,它仅仅是调用业务逻辑并返回model数据给view层,至于view层具体怎么用HTML展现,由专门的view层具体负责,这就是MVC模式,业务层与展示层是松耦合的。那么,Spring MVC是如何解耦合请求处理逻辑和页面渲染的呢?controller在处理业务逻辑之后会返回一个逻辑view的字符串,那么Spring MVC是怎么根据这个逻辑view名找到真正的物理view页面呢?这个工作就由Spring的视图解析器ViewResolvers负责。

对于普通的JSP页面,最常用到的view resolver就是InternalResourceViewResolver,它有两个属性,一个是匹配物理view的前缀,一个是后缀。前缀一般就是view页面的路径位置,后缀就是文件的格式,而前缀后缀之间的就是逻辑view名称。

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/" />
    <property name="suffix" value=".jsp" />
</bean>

比如按照上面的配置,如果controller返回的逻辑view名称是home的话,InternalResourceViewResolver会根据这个逻辑view名home找到其对应的实际物理view:/WEB-INF/views/home.jsp。

@Controller
@RequestMapping(value = "/")
public class HomeController {
    @RequestMapping(method= RequestMethod.GET)
    public String home() {
        return "home";
    }
}

ViewResolver相关接口

ViewResolver接口:

public interface ViewResolver {
    View resolveViewName(String viewName, Locale locale) throws Exception;
}

这个接口很简单,当我们传入一个逻辑view名和Locale对象,就会返回一个View实例。

View接口:

public interface View {
    String getContentType();
    void render(Map<String, ?> var1, HttpServletRequest var2, HttpServletResponse var3) throws Exception;
}

View接口的职责就是接收model对象、Request对象、Response对象,并渲染结果输出给Response对象。如果我们要自己写一个ViewResolver也很简单,我们只需要实现ViewResolver和View两个接口,在View接口中把需要渲染的内容输出到Response对象中去就可以了。

4、将模型model中的数据放到request中

作为存储模型数据以及视图名称的ModelAndView对象会在DispatcherServlet中被取出,然后DispatcherServlet会先把模型数据存储在request对象中,接着通过视图解析器转发到具体的视图上。

虽然Model是个接口,不过我们并不需要去实现Model接口,只需要在方法参数上进行声明,SpringMVC就会自动帮我们把Model对象传递过来,然后调用相应的方法存储数据即可。

代码示例:

package org.zero01.test;
import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class Test {

    @RequestMapping("/test.do")
    public String testModel(Model model) {
        model.addAttribute("name","Jon");
        model.addAttribute("age","15");
        model.addAttribute("address","USA");

        return "index";
    }
}

我们来看一下详细的执行过程,看看模型数据最后是否真的会被存储在request对象中。在以上代码中的 return "index"; 那一行打个断点,然后通过debug运行。如下,我们可以看到,在DispatcherServlet的doDispatch方法中视图名称以及模型数据是存储在ModelAndView对象中的,而不是Model中:

而ModelAndView对象中的模型数据则会被存储在HttpServletRequest对象中,所以我们才可以在视图上直接获取结果数据。这一点我们也可以通过debug来看到:

1.首先ModelAndView对象被拿出来之后,就会调用processDispatchResult方法,将ModelAndView对象传递到该方法中进行处理:

2.在processDispatchResult方法中,如果ModelAndView对象不为空的话,就会调用render方法,并把ModelAndView对象传递过去:

3.在render方法中,会把ModelAndView对象中的模型数据拿出来,传递到View对象中的render方法中(这个View的实现类是AbstractView):

4.在view对象中的render方法中,会把模型数据传递到createMergedOutputModel方法中进行合并:

5.在createMergedOutputModel方法中会把几个数据合并到一个集合里,但是这里除了model之外其他都为空,所以只合并了model数据:

在控制台中可以看到mergedModel对象里的数据如下:

6.得到mergedModel对象后,继续往下执行,接着就会调用renderMergedOutputModel方法,把mergedModel、request以及response对象都传递过去:

7.但是renderMergedOutputModel是一个抽象方法,所以该方法的调用被传递到了它的一个子类中(该子类是InternalResourceView),这个子类实现的renderMergedOutputModel方法中调用了exposeModelAsRequestAttributes方法并把模型数据和request对象传递了过去:

8.而exposeModelAsRequestAttributes方法没有被子类重写,所以调用的是父类的,也就是AbstractView类的,所以调用被传递到了AbstractView类的exposeModelAsRequestAttributes方法中。就是在这个方法中,模型数据被一个一个的放入到了HttpServletRequest对象中:

我们可以来看看将模型数据添加到request对象中的具体过程:
第一个数据:

控制台:

第二个数据:

控制台:

第三个数据:

控制台:

以上的一系列复杂的流程走完之后,我们在视图中,才可以直接使用EL表达式进行拿值:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
	<head>
		<title>Test</title>
	</head>
	<body>
		<div>
			<p>name::
				<span>${requestScope.name}</span>
			</p>
			<p>age::
				<span>${requestScope.age}</span>
			</p>
			<p>address::
				<span>${requestScope.address}</span>
			</p>
		</div>
	</body>
</html>

浏览器访问结果:

5、拦截器

过滤器中的chain.doFilter(request, response);这个方法的调用作为分水岭。事实上调用Servlet的doService()方法是在这个方法中进行的。

拦截器与过滤器的区别:

两者都是AOP编程思想的实现,都能够实现权限控制和日志记录等问题的处理,但是两者粒度不同,拦截对象不一样。

1、适用范围不同:Filter是servlet的规范,只能用于web程序,但是拦截器可以用于application等程序。

2、规范不同:Filter是servlet的规范。但是Interceptor是spring容器支撑,有spring框架支持。

3、使用资源不一样:spring的拦截器由于依赖spring,也是spring的一个组件,因此能够在拦截器中使用spring的任何资源和对象。例如service对象,数据源,事务管理等,通过ioc注入拦截器即可,而filter不能。

4、粒度不同:Filter只能在servlet的前后起作用,而拦截器能在方法前后和异常前后执行,更加灵活,粒度更小,spring框架程序优先使用拦截器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值