web 篇二(springMVC)

 

MVC简介

  • MVC是模型(Model)、视图(View)、控制器(Controller)的简写,是一种软件设计规范。

  • 是将业务逻辑、数据、显示分离的方法来组织代码。

  • MVC主要作用是降低了视图与业务逻辑间的双向偶合

  • MVC不是一种设计模式,MVC是一种架构模式。当然不同的MVC存在差异。

MVC实现 

如下代码是使用servlet 写的一个简单例子,可以看出,在servlet 下这边所有逻辑基本在servlet的service 方法中,做完逻辑交给jsp 做数据渲染,在当前项目中,假如一个方法逻辑较为复杂,并且存在较多的sql 的时候,我们的servlet+javaBean 代码是否会较为庞大,臃肿。而且在servlet 映射下,当servlet较多的时候,url 重映射又如何处理? 另外看如下servlet 代码,request 对象,response 对象,如何和客户端交互,是我们需要手写代码去操作的事情,我们给他们数据,是json 格式还是给映射jsp,或者其他数据格式,都是我们需要代码来编写。正是有了这一系列问题,所以人们才引出了相对的解决方案,框架。

web.xml,

  <servlet>
    <servlet-name>MyServlet</servlet-name>
    <servlet-class>servlet.MyServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>/helloWorld</url-pattern>
  </servlet-mapping>

servlet 代码

package servlet;

import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

public class MyServlet implements Servlet {

    public MyServlet() {
        super();
        System.out.println("实例化servlet ");
    }

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("执行servlet init()");
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest request, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("执行servlet 的 service()");
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        PrintWriter out = response.getWriter();
        response.setContentType("text/html;charset=UTF-8");

        String title = "使用 GET 方法读取表单数据";
        // 处理中文
        String name =new String("小王".getBytes("ISO-8859-1"),"UTF-8");
        String docType = "<!DOCTYPE html> \n";
        out.println(docType +
                "<html>\n" +
                "<head><title>" + title + "</title></head>\n" +
                "<body bgcolor=\"#f0f0f0\">\n" +
                "<h1 align=\"center\">" + title + "</h1>\n" +
                "<ul>\n" +
                "  <li><b>站点名</b>:"
                + name + "\n" +
                "  <li><b>网址</b>:"
                + request.getParameter("url") + "\n" +
                "</ul>\n" +
                "</body></html>");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {
        System.out.println("执行servlet destroy() ");
    }
}

springMVC 解析

下图是一个springMVC 的执行流程,接下来我们通过该图,分析springMVC 的执行流程

众所周知,servlet 在处理一个请求时,是请求到达servlet, 然后执行servlet 的doget/dopost 方法,然后返回给客户端。springMVC 同样如此。接下来我们分析下springMVC 的调用流程

我调用的路径为 http://localhost:8081/login   post 请求,调用了 FrameworkServlet  dopost方法,我们发现不管是dopost,还是doget ,请求都交由processRequest() 这个方法来处理了,里面有很多代码,相信还是有很多朋友看不大懂的,不过没关系,先看看这个方法名  processRequest 意思是 加工请求/处理请求,那我们看看他是如何处理请求的呢?(下边源码分析参照一大神博客,限于题主水平有限,无法将springMVC 所有流程全理解,看官们可直接阅读原博客)地址为 https://cloud.tencent.com/developer/article/1497797


 protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.processRequest(request, response);
    }


protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;
        //拿到之前的LocaleContext上下文(因为可能在Filter里已经设置过了)
        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        以当前的request创建一个Local的上下文,后面会继续处理
        LocaleContext localeContext = this.buildLocaleContext(request);
        
        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        // 这里面build逻辑注意:previousAttributes若为null,或者就是ServletRequestAttributes类型,那就new ServletRequestAttributes(request, response);
		// 若不为null,就保持之前的绑定结果,不再做重复绑定了(尊重原创)
        ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);
        // 拿到异步管理器。这里是首次获取,会new WebAsyncManager(),然后放到request的attr里面
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        //这里需要注意:给异步上下文恒定注册了RequestBindingInterceptor这个拦截器(作用:绑定当前的request、response、local等)        
        asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor(null));
        //这句话很明显,就是吧request和Local上下文、RequestContext绑定
        this.initContextHolders(request, localeContext, requestAttributes);

        try {
            this.doService(request, response);
        } catch (IOException | ServletException var16) {
            failureCause = var16;
            throw var16;
        } catch (Throwable var17) {
            failureCause = var17;
            throw new NestedServletException("Request processing failed", var17);
        } finally {
            this.resetContextHolders(request, previousLocaleContext, previousAttributes);
            if (requestAttributes != null) {
                requestAttributes.requestCompleted();
            }

            this.logResult(request, response, (Throwable)failureCause, asyncManager);
            this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
        }

    }

publishRequestHandledEvent()发布请求处理完后的事件源码

private void publishRequestHandledEvent(HttpServletRequest request, HttpServletResponse response,
			long startTime, @Nullable Throwable failureCause) {
		//当publishEvents设置为true和 webApplicationContext 不为空就会处理这个事件的发布
		if (this.publishEvents && this.webApplicationContext != null) {
			// 计算出处理该请求花费的时间
			long processingTime = System.currentTimeMillis() - startTime;
			this.webApplicationContext.publishEvent(
					//ServletRequestHandledEvent这个事件:目前来说只有这里会发布
					new ServletRequestHandledEvent(this,
							request.getRequestURI(), request.getRemoteAddr(),
							request.getMethod(), getServletConfig().getServletName(),
							WebUtils.getSessionId(request), getUsernameForRequest(request),
							processingTime, failureCause, response.getStatus()));
		}
	}

接下来就分析我们的核心方法了

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {

		// 如果该请求是include的请求(请求包含) 那么就把request域中的数据保存一份快照版本
		// 等doDispatch结束之后,会把这个快照版本的数据覆盖到新的request里面去
		Map<String, Object> attributesSnapshot = null;
		if (WebUtils.isIncludeRequest(request)) {
			attributesSnapshot = new HashMap<>();
			Enumeration<?> attrNames = request.getAttributeNames();
			while (attrNames.hasMoreElements()) {
				String attrName = (String) attrNames.nextElement();
				if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
					attributesSnapshot.put(attrName, request.getAttribute(attrName));
				}
			}
		}

		// Make framework objects available to handlers and view objects.
		// 说得很清楚,把一些常用对象放进请求域  方便Handler里面可以随意获取
		request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); //这个是web子容器哦
		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
		request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

		// 如果是重定向,放置得更多一些~~~~
		if (this.flashMapManager != null) {
			FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
			if (inputFlashMap != null) {
				request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
			}
			request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
			request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
		}

		try {
			// DispatcherServlet最重要的方法,交给他去分发请求你、找到handler处理等等
			doDispatch(request, response);
		} finally {
			if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
				// Restore the original attribute snapshot, in case of an include.
				//如果是include请求  会上上面的数据快照,重新放置到request里面去
				if (attributesSnapshot != null) {
					restoreAttributesAfterInclude(request, attributesSnapshot);
				}
			}
		}
	}

下面到我们的doDispatch() ,这里就是分发到我们的controller 了,关于下图所示代码,相关地方还有些深奥,这里我重点分析我所理解的部分:

1.  先判断请求是否是文件请求,然后找打该次请求的执行器链 HandlerExecutionChain ,执行器链里边除了执行器,还有拦截器,然后根据执行器链找到执行器,现在问题来了,执行器里边存在很多种执行器,每种类型的执行器器的执行方法都不一样,比方说servlet 执行器,执行是调用service() 方法,而我们的controller 执行器 执行是调用 handleRequest 方法,这种情况看官们想想该如何处理呢?  下面题主贴一下我当初的思路代码如下,这是题主个人思路,与springMVC 无关

这个是springMVC 的思路

HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());

这个是题主个人的思路,写下伪代码

//取到所有执行器类型,比如controller类型,servlet 类型(还有其他类型,这里以这两种类型为例)
for(执行器 handler:handerList){
    if(handler instance servlet){
        // servlet 的执行方法
        handler.service() 
    }else if(handler instance controller){
        //controller 的执行方法
        handler.handlerMapping()
    }

}

看官们是否认可上面题主的思路呢? 好像也没什么问题哈, 但是现在问题来了我们springMVC 的handler 类型是Object 类型所有的handler 并不能用同一个list 放在一起呀,那如果是List<Object> 呢?

list<Object> 如何调用呢?我们的controller,servlet 都是接口,两者没有什么联系。那个我们的spring 又是怎么处理的呢? 接下来我们就引入我们的适配器模式

public interface Servlet {
    void init(ServletConfig var1) throws ServletException;
    ServletConfig getServletConfig();
    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
    String getServletInfo();
    void destroy();
}

//这是controller 
@FunctionalInterface
public interface Controller {
    @Nullable
    ModelAndView handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws Exception;
}

 适配器模式

顾名思义,即做适配的,在两种不同的类型的类之间做适配,使其在api 调用方需要那个的时候,提供方就给他那个。那么如何实现一个适配器呢? 比如有A,B两个接口,B要适配A,  那么写一个B 的实现类,引入A 的实例,重写B接口的方法,使用A 的实现,这些只是两个之间的适配,如果说涉及多个之间的适配,则需要写一个适配器接口,在适配器内部持有需要适配的引用,把对目标的调用转换成对适配器的调用。有设计模式的看官们,这种是否也有点类似我们的代理呀?

如springMvc 这个例子,controller,servlet都是不同的接口,调用方具体给什么类型只有在运行期才能知道。怎么办呢?我们看看spring怎么处理的

1. 一个通用的适配器接口

public interface HandlerAdapter {
    boolean supports(Object var1);

    @Nullable
    ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;

    long getLastModified(HttpServletRequest var1, Object var2);
}

2. 不同的适配器实现

public class SimpleControllerHandlerAdapter implements HandlerAdapter {
    public SimpleControllerHandlerAdapter() {
    }

    public boolean supports(Object handler) {
        return handler instanceof Controller;
    }

    @Nullable
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return ((Controller)handler).handleRequest(request, response);
    }

    public long getLastModified(HttpServletRequest request, Object handler) {
        return handler instanceof LastModified ? ((LastModified)handler).getLastModified(request) : -1L;
    }
}

 3. 获取适配器类型

    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (this.handlerAdapters != null) {
            Iterator var2 = this.handlerAdapters.iterator();

            while(var2.hasNext()) {
                HandlerAdapter adapter = (HandlerAdapter)var2.next();
                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");
    }

 4. 适配器执行方法

适配器的优缺点

1. 更好的复用性,如果功能是已有了的,只是接口不兼容,那么通过适配器模式可以让这些功能得到更好的复用

2. 更好的扩展性,在实现适配器功能的时候,可以调用自己开发的功能

缺点

1. 过多的使用适配器,会使得系统非常凌乱,比如说明明看到的是调用A 接口,却在内部被换成了B 实现,这种代码过多,无异于一场灾难。

      

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            try {
                ModelAndView mv = null;
                Object dispatchException = null;

                try {
                    //检查是否是文件请求
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    //找到其执行器链
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }
                    //找到适配器
                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    if (isGet || "HEAD".equals(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }

                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }
                    //执行方法,返回moduleAndView 对象
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }

                    this.applyDefaultViewName(processedRequest, mv);
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var20) {
                    dispatchException = var20;
                } catch (Throwable var21) {
                    dispatchException = new NestedServletException("Handler dispatch failed", var21);
                }

                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } catch (Exception var22) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
            } catch (Throwable var23) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
            }

        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }

        }
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值