spring mvc

MVC

Model(模型):数据模型,包含数据和行为:Value Object(数据) 和 服务层(行为)。

View(视图):负责进行模型的展示。

Controller(控制器):接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。 也就是说控制器做了个调度员的工作。

模型无法主动推数据给视图,如果用户想要视图更新,需要发送一次请求(即请求-响应模型)。

Web端开发发展历程

CGI(Common Gateway Interface):在web服务端使用的脚本技术,用于接收web用户请求并处理,最后动态产生响应给用户,但每次请求将产生一个进程,重量级

Servlet:JavaEE web组件技术,一种在服务器端执行的web组件,用于接收web用户请求并处理,最后动态产生响应给用户。每次请求只产生一个线程(而且有线程池),轻量级。在java代码里面输出html流,表现逻辑、控制逻辑、业务逻辑调用混杂

JSP(Java Server Page):一种在服务器端执行的web组件,嵌入脚本语言的模板页面技术。在html代码中嵌入java代码。JSP最终会被编译为Servlet,比纯Servlet开发页面简单、方便,但表现逻辑、控制逻辑、业务逻辑调用还是混杂

Model1:JSP+JavaBean,使用<jsp:useBean>标准动作,将请求参数封装为JavaBean组件,还须使用java脚本执行控制逻辑。

Model2:控制器采用Servlet、模型采用JavaBean、视图采用JSP。只是一种代码上物理的分离,实则依赖关系很严重。

服务到工作者:Front Controller + Application Controller + Page Controller + Context

即,前端控制器+应用控制器+页面控制器(也有称其为动作)+上下文,也是Web MVC,只是责任更加明确。

Spring Web MVC

基于Java,实现了Web MVC设计模式,请求驱动类型的轻量级Web框架(基于请求驱动:使用请求-响应模型)

前端控制器是DispatcherServlet

应用控制器拆为处理器映射器Handler Mapping进行处理器管理和视图解析器View Resolver进行视图管理;

页面控制器/动作为Controller接口(仅包含ModelAndView handleRequest(request, response) 方法)的实现(也可以是任何的POJO类);

Spring MVC执行流程

  1. 用户请求DispatcherServlet。
  2. DispatcherServlet接受到请求,将根据请求信息交给处理器映射器。
  3. 处理器映射器(HandlerMapping)根据请求路径查找匹配的Handler,并返回一个执行链。
  4. DispatcherServlet再根据执行链请求处理器适配器(HandlerAdapter)。
  5. 处理器适配器调用相应的handler进行功能处理。
  6. 对应的handler处理完成后返回ModelAndView给处理器适配器。
  7. 处理器适配器将接受的ModelAndView返回给DispatcherServlet。
  8. DispatcherServlet请求视图解析器来解析视图。
  9. 视图解析器处理完后返回View对象给DispatcherServlet。
  10. 最后前端控制器对View进行视图渲染(即将模型数据填充至视图中)。
  11. DispatcherServlet返回响应给用户

DispatcherServlet:拦截请求到Spring Web MVC

HandlerMapping:将请求映射到处理器

HandlerAdapter:支持多种类型的处理器

Handler:如Controller,进行功能处理

ViewResolver:将逻辑视图名解析为具体视图技术

View:视图

DispatcherServlet配置

1、基于xml配置
<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:spring-servlet-config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>springMVCDemo</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

其中<load-on-startup>1</load-on-startup>标志容器是否在启动时就加载该servlet。值表示被载入的顺序:>=0表示容器启动时就加载,否则被请求才加载;值越小,优先级越高。

2、基于Spring Boot配置
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

@SpringBootApplication这个注解等同于:

@Configuration	// @SpringBootConfiguration就是用的这个
@EnableAutoConfiguration
@ComponentScan

关注@EnableAutoConfiguration注解,它有一个@Import({AutoConfigurationImportSelector.class})注解。借助AutoConfigurationImportSelector@EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器。这里也是DispatcherServlet初始化的关键。

// AutoConfigurationImportSelector
	@Override
	// 该方法返回的String数组是全类名,会被纳入容器中。
	// DispatcherServlet的全类名就在这个数组中
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		// getAutoConfigurationEntry-->getCandidateConfigurations
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        // 把spring-boot-autoconfigure.jar的spring.factories中
        // 以EnableAutoConfiguration为key的value类加载到容器中
        // this.getSpringFactoriesLoaderFactoryClass()就是EnableAutoConfiguration.class
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
        ...
        return configurations;
    }

org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration就在其中,于是DispatcherServletAutoConfiguration被载入。

// SpringFactoriesLoader
	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

	public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        // EnableAutoConfiguration
		String factoryClassName = factoryClass.getName();
		return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
	}

	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        // 先看一下缓存里有没有这个map,有就直接返回了。
		...
		try {
            // 从spring.factories文件获取资源
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryClassName = ((String) entry.getKey()).trim();
					for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                        // 以键值对形式存储
						result.add(factoryClassName, factoryName.trim());
					}
				}
			}
            // 放到缓存map中
			cache.put(classLoader, result);
			return result;
		}...
	}

控制器定义

@Controller注解表明了一个类是作为控制器的角色而存在的。Spring不要求你去继承任何控制器基类,也不要求你去实现Servlet的那套API。

// 用于标识是处理器类
@Controller
public class GreetingController {
    // @GetMapping确保到/greeting的HTTP GET请求被映射到当前这个方法上
    @GetMapping("/greeting")
    // @RequestParam将请求参数绑定到方法参数上:required=false该查询参数非必须;defaultValue="World"如果没有传入该值,还有默认值
    public String greeting(@RequestParam(name="name", required=false, defaultValue="World") String name, Model model) {
        // name参数的值被加到Model对象上, 最终在视图上展现
        model.addAttribute("name", name);
        return "greeting";
    }
}

分派器(DispatcherServlet)会扫描所有注解了@Controller的类,检测其中通过@RequestMapping注解配置的方法。(RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter)

DispatcherServlet初始化

1、初始化参数

基于xml

DispatcherServlet的初始化过程中,Spring MVC会在web应用的WEB-INF目录下查找一个名为[servlet-name]-servlet.xml的配置文件,并创建其中所定义的bean。(就是IoC容器创建bean的流程!)

如果使用如上配置,Spring Web MVC框架将加载“classpath:spring-servlet-config.xml”来进行初始化上下文而不是“/WEB-INF/[servlet名字]-servlet.xml”。

基于Spring Boot

Configuring the DispatcherServlet yourself is unusual but if you really need to do it, a @Bean of type DispatcherServletPath must be provided as well to provide the path of your custom DispatcherServlet.

2、初始化流程

HttpServletBean继承HttpServlet,因此在Web容器启动时将调用它的init方法:

	// 这个方法会在容器初始化每个Servlet的时候被调用一次。方法是在GenericServlet中有一个空定义
	public final void init() throws ServletException {
        // 将Servlet初始化参数设置到该组件上
        PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
        if (!pvs.isEmpty()) {
            try {
                // 通过BeanWrapper简化设值过程,方便后续使用
                BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
                bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
                this.initBeanWrapper(bw);
                bw.setPropertyValues(pvs, true);
            }...
        }
		// 提供给子类初始化扩展点,该方法由FrameworkServlet覆盖。
        this.initServletBean();
    }

FrameworkServlet继承HttpServletBean

    // 进行Web上下文初始化
	protected final void initServletBean() throws ServletException {
        ....
        long startTime = System.currentTimeMillis();
        try {
            // 初始化web上下文
            this.webApplicationContext = this.initWebApplicationContext();
            this.initFrameworkServlet();
        }
        ...
    }
    
    protected WebApplicationContext initWebApplicationContext() {
        // ROOT上下文
        // this.getServletContext():
        // GenericServlet(this.getServletConfig().getServletContext();)
        // getWebApplicationContext():
        // getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE)
        // 就是这里得到了DispatcherServlet的上下文!
        WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
        WebApplicationContext wac = null;
        if (this.webApplicationContext != null) {
            // 1、在创建该Servlet时注入的上下文
            wac = this.webApplicationContext;
            if (wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac;
                if (!cwac.isActive()) {
                    if (cwac.getParent() == null) {
                        cwac.setParent(rootContext);
                    }

                    this.configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
		// 2、查找已经绑定的上下文
        if (wac == null) {
            wac = this.findWebApplicationContext();
        }
		// 3、如果没有找到相应的上下文,创建并指定父亲为根上下文
        if (wac == null) {
            wac = this.createWebApplicationContext(rootContext);
        }

        if (!this.refreshEventReceived) {
            synchronized(this.onRefreshMonitor) {
                // 4、刷新上下文(执行一些初始化)
            	// 提供给子类初始化扩展点
                this.onRefresh(wac);
            }
        }

        if (this.publishContext) {
            String attrName = this.getServletContextAttributeName();
            this.getServletContext().setAttribute(attrName, wac);
        }

        return wac;
    }

DispatcherServlet继承FrameworkServlet

    protected void onRefresh(ApplicationContext context) {
        this.initStrategies(context);
    }

    // 初始化默认的Spring Web MVC框架使用的策略
	protected void initStrategies(ApplicationContext context) {
        this.initMultipartResolver(context);
        this.initLocaleResolver(context);
        this.initThemeResolver(context);
        this.initHandlerMappings(context);
        this.initHandlerAdapters(context);
        this.initHandlerExceptionResolvers(context);
        this.initRequestToViewNameTranslator(context);
        this.initViewResolvers(context);
        this.initFlashMapManager(context);
    }

总结

整个DispatcherServlet初始化的过程具体主要做了如下两件事情:

1、初始化Spring Web MVC使用的Web上下文,并且可能指定父容器为根上下文;

2、初始化DispatcherServlet使用的策略,如HandlerMapping、HandlerAdapter等。

HandlerMapping初始化

SimpleUrlHandlerMapping是Spring MVC中适用性最强的HandlerMapping类,允许明确指定URL模式和Handler的映射关系。

    private void initHandlerMappings(ApplicationContext context) {
        this.handlerMappings = null;
        // 优先判断detectAllHandlerMappings的值。
        if (this.detectAllHandlerMappings) {
            // 默认情况下,Spring MVC会加载在当前系统中所有实现了HandlerMapping接口的bean
            // 再进行按优先级排序。(优先级通过Ordered接口设定)
            // Find all HandlerMappings in the ApplicationContext, 
            // including ancestor contexts.
            Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.handlerMappings = new ArrayList(matchingBeans.values());
                // We keep HandlerMappings in sorted order.
                AnnotationAwareOrderComparator.sort(this.handlerMappings);
            }
        } else {
            try {
                // detectAllHandlerMappings值设置为false
                // Spring MVC就只会查找名为“handlerMapping”的bean
                // 并作为当前系统的唯一的HandlerMapping
                HandlerMapping hm = (HandlerMapping)context.getBean("handlerMapping", HandlerMapping.class);
                this.handlerMappings = Collections.singletonList(hm);
            } catch (NoSuchBeanDefinitionException var3) {
            }
        }
        // Ensure we have at least one HandlerMapping, by registering
		// a default HandlerMapping if no other mappings are found.
        if (this.handlerMappings == null) {
            // 没有定义HandlerMapping的话
            // 按照DispatcherServlet.properties所定义的内容来加载默认的HandlerMapping。
            this.handlerMappings = this.getDefaultStrategies(context, HandlerMapping.class);
            ...
        }

    }

其中,defaultStrategies从静态代码段中获得:

ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);

HandlerAdapter初始化

HandlerAdapter可以帮助自定义各种Handler。具体实现同上。

明显是适配器模式。也就是说被适配者是handler。而适配器都实现了HandlerAdapter接口。

    private void initHandlerAdapters(ApplicationContext context) {
        this.handlerAdapters = null;
        if (this.detectAllHandlerAdapters) {
            Map<String, HandlerAdapter> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.handlerAdapters = new ArrayList(matchingBeans.values());
                AnnotationAwareOrderComparator.sort(this.handlerAdapters);
            }
        } else {
            try {
                HandlerAdapter ha = (HandlerAdapter)context.getBean("handlerAdapter", HandlerAdapter.class);
                this.handlerAdapters = Collections.singletonList(ha);
            } catch (NoSuchBeanDefinitionException var3) {
            }
        }

        if (this.handlerAdapters == null) {
            this.handlerAdapters = this.getDefaultStrategies(context, HandlerAdapter.class);
            ...
        }

    }
抽象处理器方法适配器(AbstractHandlerMethodAdapter)
  1. afterPropertiesSet方法注入ArgumentResolversReturnValueHandlers到Spring容器。

  2. ServletInvocableHandlerMethod调用invokeAndHandle方法。

  3. 两大接口:HandlerMethodArgumentResolver、HandlerMethodReturnValueHandler。

    使用HandlerMethodReturnValueComposite,使用组合模式,放入HandlerMethodReturnValueHandler的list;同理,HandlerMethodArgumentResolverComposite使用组合模式,放入HandlerMethodArgumentResolver的list。

  4. RequestResponseBodyMethodProcessor负责解析Controller里@RequestBody,支持响应类型是@ResponseBody

    RequestParamMethodArgumentResolver负责解析Controller里@RequestParam

    这两个都是resolver!都实现了两大接口!

// RequestMappingHandlerAdapter
	@Nullable
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        ...
        try {
            ...
            ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);
            if (this.argumentResolvers != null) {
                invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
            }

            if (this.returnValueHandlers != null) {
                invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
            }
            ...
            // ★
            invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);
            ...
        }...
    }
// ServletInvocableHandlerMethod
	public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        // ★方法参数处理,见下
        Object returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs);
        this.setResponseStatus(webRequest);
        if (returnValue == null) {
            if (this.isRequestNotModified(webRequest) || this.getResponseStatus() != null || mavContainer.isRequestHandled()) {
                this.disableContentCachingIfNecessary(webRequest);
                mavContainer.setRequestHandled(true);
                return;
            }
        } else if (StringUtils.hasText(this.getResponseStatusReason())) {
            mavContainer.setRequestHandled(true);
            return;
        }
        ...
        try {
            // 返回值的处理 ★ 里面是用的组合,不同子类实现不同
            this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest);
        }...
    }
// InvocableHandlerMethod
	@Nullable
    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        // 解析请求参数
        Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
        ...
        // 调用Controller中的请求方法
        return this.doInvoke(args);
    }
HTTP请求处理器适配器(HttpRequestHandlerAdapter)

仅支持对HTTP请求处理器的适配。

Handler需实现HttpRequestHandler接口,并实现其void handlerRequest(HttpRequest,HttpResponse)方法。

简单控制器处理器适配器(SimpleControllerHandlerAdapter)

将HTTP请求适配到一个控制器的实现进行处理。这里的控制器的实现是一个简单的控制器接口的实现。客户化的业务逻辑通常是在控制器接口的实现类中实现的。

Handler需实现Controller接口,并实现其ModelAndView handler(HttpRequest,HttpResponse,handler)方法。

ViewResolver初始化

具体实现同上。

    private void initViewResolvers(ApplicationContext context) {
        this.viewResolvers = null;
        if (this.detectAllViewResolvers) {
            Map<String, ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.viewResolvers = new ArrayList(matchingBeans.values());
                AnnotationAwareOrderComparator.sort(this.viewResolvers);
            }
        } else {
            try {
                ViewResolver vr = (ViewResolver)context.getBean("viewResolver", ViewResolver.class);
                this.viewResolvers = Collections.singletonList(vr);
            } catch (NoSuchBeanDefinitionException var3) {
            }
        }

        if (this.viewResolvers == null) {
            this.viewResolvers = this.getDefaultStrategies(context, ViewResolver.class);
            ...
        }

    }

DispatcherServlet分派

// DispatcherServlet
	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 {
                     // 检查请求是否是multipart(如文件上传),如果是将通过MultipartResolver解析 
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    // 2、请求到处理器(页面控制器)的映射,通过HandlerMapping进行映射
                    // HandlerMapping将会把请求映射为HandlerExecutionChain对象
                    // 包含一个Handler处理器、多个HandlerInterceptor拦截器
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }
                    // 3、处理器适配,即将处理器包装成相应的适配器(从而支持多种类型的处理器)
                    // 会轮询适配器模,查找能够处理当前请求的处理器的实现
                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    
                    // 304 Not Modified缓存支持
                    ...
                    // 执行处理器相关的拦截器的预处理(HandlerInterceptor.preHandle)
                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }
                    // 4、由适配器执行处理器(调用处理器相应功能处理方法),返回一个ModelAndView对象
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }

                    this.applyDefaultViewName(processedRequest, mv);
                    // 执行处理器相关的拦截器的后处理(HandlerInterceptor.postHandle)
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                }...
                // 5&6、解析视图并进行视图的渲染  
                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            }...
        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                // Clean up any resources used by a multipart request.  
                this.cleanupMultipart(processedRequest);
            }

        }
    }

    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
        boolean errorView = false;
        if (exception != null) {
            ...
        }
		// 5、由ViewResolver解析View
        // viewName不为空:viewResolver.resolveViewName(viewName, locale)
        // viewName为空:mv.getView()
        // 6、视图在渲染时会把Model传入
        // view.render(mv.getModelInternal(), request, response);这是个接口方法
        // 接口名为View:具体实现有StaticView、ThymeleafView、HtmlResourceView和AbstractView等
        if (mv != null && !mv.wasCleared()) {
            // ViewResolver将把逻辑视图名解析为具体的View(很容易更换其他视图技术)
            // View会根据传进来的Model模型数据进行渲染
            this.render(mv, request, response);
            if (errorView) {
                WebUtils.clearErrorRequestAttributes(request);
            }
        }....

        if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            if (mappedHandler != null) {
                mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
            }

        }
    }

posted @ 2019-07-26 10:13  白芷呀  阅读(153)  评论(0编辑  收藏  举报