SpirngMVC架构图

开发第一个SpringMVC应用
总结:
1、用法发起请求,到前端控制器(DispatchServlet)
2、前端控制器没有处理业务逻辑的能力,通过HandlerMapping查找Handler
3、返回执行链,包含了2部分内容,
a)Handler对象(模型对象)
b)拦截器(数组)
4、通过HandlerAdapter进行包装Handler
5、通过适配器执行Handler,进行业务逻辑的处理
6、业务逻辑处理完成,返回ModelAndView
a)Model Data(模型数据)
b)View(ViewName) 视图名称
7、将ModelAndView返回到前端控制器
8、前端控制器通过视图名称到 视图解析器中 查找视图对象
9、视图解析器返回真正视图对象
10、前端控制器通过模型数据和视图对象进行渲染
11、渲染完成产生响应返回
12、将响应返回给用户

开发第一个SpringMVC应用

创建SpringMVC工程

开发第一个SpringMVC应用

工程目录结构

开发第一个SpringMVC应用

导入依赖

	<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.springmvc</groupId>
	<artifactId>SpringMVCTest</artifactId>
	<version>1.0.0-SNAPSHOT</version>
	<packaging>war</packaging>
	<!-- 定义依赖版本号 -->
	<properties>
		<junit.version>4.10</junit.version>
		<slf4j.version>1.6.4</slf4j.version>
		<spring.version>4.1.3.RELEASE</spring.version>
		<jstl.version>1.2</jstl.version>
		<servlet-api.version>2.5</servlet-api.version>
		<jsp-api.version>2.0</jsp-api.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>${slf4j.version}</version>
		</dependency>
		<!-- JSP相关 -->
		<dependency>
			<groupId>jstl</groupId>
			<artifactId>jstl</artifactId>
			<version>${jstl.version}</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>${servlet-api.version}</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jsp-api</artifactId>
			<version>${jsp-api.version}</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<!-- 配置Tomcat插件 -->
			<plugin>
				<groupId>org.apache.tomcat.maven</groupId>
				<artifactId>tomcat7-maven-plugin</artifactId>
				<configuration>
					<!-- 端口 -->
					<port>80</port>
					<!-- /:直接访问,无需加项目名 -->
					<path>/</path>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

配置web.xml

在src/main/webapp下创建WEB-INF目录以及web.xml
配置DispatcherServlet

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	id="WebApp_ID" version="2.5">
	<display-name>spingmvc</display-name>

	<!-- 配置SpringMVC框架入口 -->
	<servlet>
		<servlet-name>springmvc</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<!-- 
			tomcat启动时完成初始化
			不配置,在第一次请求后完成初始化
		 -->
		<load-on-startup>1</load-on-startup>
		<!-- 加载springmvc配置文件 -->
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:springmvc-servlet.xml</param-value>
		</init-param>
	</servlet>

	<servlet-mapping>
		<servlet-name>springmvc</servlet-name>
		<url-pattern>*.do</url-pattern>
	</servlet-mapping>

</web-app>

配置日志文件

在src/main/resources下创建log4j.properties

log4j.rootLogger=DEBUG,A1
log4j.logger.org.mybatis = DEBUG
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n

SpringMVC配置文件

在src/main/resources下创建springmvc-servlet.xml

SpringMVC默认从WEB-INF下读取servlet配置文件,文件的命名规则是:servletName-servlet.xml
如果要自定义servlet文件,需要在servlet定义时指定contextConfigLocation。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

	<!-- 定义执行器映射器 -->
	<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
	
	<!-- 定义适配器 -->
	<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
	
	<!--定义模型对象 -->
	<bean name="/hello.do" class="com.springmvc.handler.HelloHandler"/>
	
	<!-- 
		定义视图解析器(内部资源视图解析器)
		Example: prefix="/WEB-INF/jsp/", suffix=".jsp", viewname="test" -> "/WEB-INF/jsp/test.jsp" 
	 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/views/"/>
		<property name="suffix" value=".jsp"/>
	</bean>
	
</beans>

配置文件解析:
1、配置处理器映射器HandlerMapping
2、配置处理器适配器HandlerAdapter
3、配置模型Handler
4、配置视图解析器ViewResolver

HandlerMapping:
开发第一个SpringMVC应用
HandlerAdapter:
开发第一个SpringMVC应用
ViewResolver:
开发第一个SpringMVC应用

为什么要设计适配器?

Controller是多种类型的,如果没有适配器,在DispatcherServlet中调用Hadnler,需要判断Controller类型做调用,我们想去扩展自己的Controller,需要去改源码做判断调用。违反开闭(对扩展开放,对修改关闭)原则。
如果有了适配器,如果要扩展Controller,只需要写自己的Controller和实现适配器接口即可。

编写模型对象处理器

package com.springmvc.handler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

public class HelloHandler implements Controller {

	public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// 处理业务逻辑
		ModelAndView mv = new ModelAndView();
		mv.setViewName("hello");// 设置视图名称
		mv.addObject("msg", "我的第一个SpringMVC应用!");// 设置模型数据
		// request.setAttribute("msg", "xxxxx");
		return mv;
	}

}

启动Tomcat查看效果

开发第一个SpringMVC应用
开发第一个SpringMVC应用
测试:
开发第一个SpringMVC应用

DispatcherServlet源码解析

DispatcherServlet源码初始化过程

	protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context); //文件上传组件
		initLocaleResolver(context); //国际化
		initThemeResolver(context);// 主题
		initHandlerMappings(context);// HandlerMapping 处理器映射
		initHandlerAdapters(context);// HandlerAdapter 适配器
		initHandlerExceptionResolvers(context); //异常相关组件
		initRequestToViewNameTranslator(context); // 转化
		initViewResolvers(context); // ViewResolvers 视图解析器
		initFlashMapManager(context); // 闪存
	}

初始化映射器
开发第一个SpringMVC应用
初始化适配器
开发第一个SpringMVC应用
SpringMVC的默认配置文件
org.springframework.web.servlet.DispatcherServlet.properties开发第一个SpringMVC应用

发现默认的配置文件中已经配置了默认的映射器和适配器,所以可以简化配置,无需配置映射器和适配器。
开发第一个SpringMVC应用

在DispatcherServlet中的doDispatch方法是SpringMVC的执行流程的控制方法,源码如下:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		// 定义执行链
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

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

			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.
				// 获取执行链
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				// 获取Handler的适配器
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (logger.isDebugEnabled()) {
						logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
					}
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				// 执行链中拦截器的前置方法,如果返回false则终止执行。
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				try {
					// Actually invoke the handler.
					// 通过Handler适配器执行Handler
					mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
				}
				finally {
					if (asyncManager.isConcurrentHandlingStarted()) {
						return;
					}
				}

				applyDefaultViewName(request, mv);
				// 执行拦截器的后置方法
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			// 处理返回的结果,视图解析器的处理
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Error err) {
			triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				return;
			}
			// Clean up any resources used by a multipart request.
			if (multipartRequestParsed) {
				cleanupMultipart(processedRequest);
			}
		}
	}

使用注解方式开发SpringMVC应用

在第一个SpringMVC应用的基础上修改SpringMVC配置文件和编写模型对象处理器。
下面两个配置不是SpringMVC默认配置,所以不能省略。

SpringMVC配置文件

	<!-- 定义注解映射器 -->
	<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
	
	<!-- 定义注解适配器 -->
	<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>

	<!-- 配置扫描器,使得@Controller生效 -->
	<context:component-scan base-package="com.springmvc.handler" />

编写模型对象处理器

使用@RequestMapping映射请求URL
使用@Controller注解使HelloHandler 成为处理请求的控制器

package com.springmvc.handler;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/hello")
public class HelloHandler {

	@RequestMapping("/show")
	public ModelAndView show() {
		// 处理业务逻辑
		ModelAndView mv = new ModelAndView();
		mv.setViewName("hello");// 设置视图名称
		mv.addObject("msg", "我的第一个注解SpringMVC应用!");// 设置模型数据
		return mv;
	}

}

使用tomcat7启动后并测试

开发第一个SpringMVC应用

升级版注解驱动

作用:SpringMVC默认配置的升级版;

<mvc:annotation-driven />

对应的java类是:

org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser

使用<mvc:annotation-driven />后默认加入的HandlerMapping和Adapter有:
开发第一个SpringMVC应用

HandlerMapping有2个:
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping

Adapter有3个:
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter

改进后的注解SpringMVC配置

开发第一个SpringMVC应用

相关文章: