【Spring】著作阅读笔记——Spring MVC核心技术

23 篇文章 2 订阅
18 篇文章 0 订阅

前言

《Spring 揭秘》是一本09年出版的书,当时还在流行Struct 2,作者大胆预言Spring MVC有着更广阔的发展前途,并且建议新的项目采用Spring MVC进行开发。现在回过头来看,当年的Struct 2之于Spring MVC就如现今的Spring MVC 之于 Spring Webflux。当年作者熟练掌握了Struct 2才悟得Spring MVC的好,那么如今在进入Spring Webflux的探索前,还需要夯实Spring MVC的基础。短时间内Spring MVC还是很能打的,看完书后,慢慢能体会其架构上的设计哲学。框架上使用的设计模式,虽简洁但有效。

  • 本文不会提Struct 2的实现
  • 关注Spring MVC 的核心模块、看Interceptor 的作用时机。

2. 从时序图看被解耦的组件

在这里插入图片描述

  • DispatcherServlet 有着三个角色

    1. 遵循Servlet规范(间接实现了Servlet接口),可作为web应用的基本响应单元
    2. 被Spring管理(间接实现ApplicationContextAware)
    3. 在Spring容器中作为单例存在(Spring Boot自动装配会替我们做)

    DispatcherServlet 的一体两面,一为Servlet,二为Spring的上下文,就赐予它整合Servlet与Spring的使命,事实上它也是这么干的。

    在只注册了DispatcherServlet 的情况下,所有http请求都会被这个Servlet拦截,然后由它去寻找适合的控制器(Controller) -> 根据控制器返回的信息(Model)和视图逻辑标签 -> 根据视图的逻辑标签转发给视图的处理器进行渲染。这就是常说的MVC模式。

    // MVC 模式的抽象, 也是Spring的具体代码
    public interface Controller {
    	@Nullable
    	ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) 	throws Exception;
    
    }
    
  • HandlerMapping
    保存Spring IOC中注册的 "Handler" 和 客户端请求的映射关系,一般的key为url, 编程中这个key常常使用@RequestMapping("/path/...") 进行声明

  • HandlerExecutionChain
    Handler 的包装类。看到了包装逻辑有AOP的影子,并且拥有Servlet规范下的request和response

    public class HandlerExecutionChain {
        //实际的请求处理器,用来控制我们的请求到达哪个对象的哪个方法
        private final Object handler;
        
        // 拦截器
    	@Nullable
    	private HandlerInterceptor[] interceptors;
        
        public Object getHandler() {
            return this.handler;
        }
    	//处理实际请求前的一些操作
        boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {}
        
    	//请求处理完成后的一些操作
        void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
                throws Exception {}
    }
    
    

    到这里如果感到头晕了,可以直接看下面的源码解释,比较直观。 HandlerAdapter是设计模式的核心,放到后面讲。

2.1 源码解释时序图

DispatcherServlet.doDispatch()

		// 内部用HandlerMapping找到请求的Handler封装类, 封装的是Object类型
		HandlerExecutionChain mappedHandler = getHandler(processedRequest);
		
		// 获取适配器,内部调用adapter.supports(handler)匹配,返回一个适配器
		HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
			
		// 前置拦截逻辑(Interceptors是全局共享的,内部回去get)
		mappedHandler.applyPreHandle(processedRequest, response)) {
			
		// 通过适配器去生成MV
		ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
			
		// 后置拦截处理(完成了controller的业务逻辑了)
		mappedHandler.applyPostHandle(processedRequest, response, mv);

		// 获取到mv接着往下走
		processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

视图处理 processDispatchResult()

		// 使用mv渲染视图
		render(mv, request, response);
		
		// 终端拦截处理 (整个MVC流程近乎走完,视图也渲染完成)
		mappedHandler.triggerAfterCompletion(request, response, null);

2.2 HandlerAdaptor 巧用适配器模式解耦C和MV

Spring 提供的 部分Adapter

  • SimpleControllerHandlerAdapter
  • RequestMappingHandlerAdapter

Spring 提供的部分 Handler

  • Controller
  • HandlerMethod

列举以上的类,是想突出一个思想,源码部分的Handler声明为Object类型,通过新增Adapter和Handler完成MVC控制器的种类拓展,而不用修改DispatcherServlet的源码。同时关注到AdapterHandler 还可以存在多对一的关系: 言下之意是,Adapter 在内存的注册顺序需要关注。

	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		if (this.handlerAdapters != null) {
			for (HandlerAdapter adapter : this.handlerAdapters) {
				if (adapter.supports(handler)) {
					return adapter;
				}
			}
		}
	}

适配器模式简单demo

2.3 适配器模式下产生的拓展意义

就上面的AdapterHandler 的举例,产生了响应式编程也就是 Spring Webflux的影子

  • 普通的Adapter
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return ((Controller) handler).handleRequest(request, response);
	}
  • 响应式的Adapter
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
		HandlerMethod handlerMethod = (HandlerMethod) handler;
		// 省略
	}

2.4 视图的解耦(MV)

  • 上文说到的MV,V并不是已经渲染出来的视图,只作为一个key存在,把渲染的工作延时到上文提到的processDispatchResult()中的render()进行处理,具体代码如下
		String viewName = mv.getViewName()
		if (viewName != null) {
			view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
		} else {
			view = mv.getView();
		}
		if (mv.getStatus() != null) {
			response.setStatus(mv.getStatus().value());
		}
		view.render(mv.getModelInternal(), request, response);

3. 使用MVC的理由及意义

  • 可拔插的视图渲染技术选择,视图渲染可独立于Web容器发展(Servlet)
    1. Thymeleaf
    2. Freemarker (较老的技术)
    3. JSP(较老的技术)
  • 运用Interceptor 提供的面向切面编程角度,更好得组织公共代码
  • 可引用更多Model绑定技术,Spring Boot Web Starter默认是用Jackson
  • 通过Spring MVCFilter 也能被管理(相关技术用Spring Sercurity的模块讲更好)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值