-------------------------------------------------------------------------------------------------------------------------------------------------------------
使用maven快速的搭建一个web项目。
引入springmvc或者springweb。
这里出现了。
注意包的区别。
测试,这个启动的时候就创建一个单例的bean了:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="date" class="java.util.Date"></bean>
</beans>
package com.liban.demo1;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTest {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
Object date = ctx.getBean("date");
System.out.println(date);
}
}
-----测试1----
package com.liban.demo2;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class BeanController implements Controller {
@Override
public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
System.out.println("BeanController run.......");
return null;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="date" class="java.util.Date"></bean>
<bean name="/test1" class="com.liban.demo2.BeanController"></bean>
</beans>
单单是这样只是放在容器中的并不能生效的。
配置tomcat的插件,也可以不配置:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>8080</port>
<path>/</path>
<uriEncoding>UTF-8</uriEncoding>
<server>tomcat7</server>
</configuration>
</plugin>
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
总结:用户请求,控制器DispatcherServler获取Handler,handlerAdapter执行handler,返回ModelAndView。
框架的图:
可以看出来其实DispatcherServlet就是一个servlet。
看下UML图:https://blog.csdn.net/shifters/article/details/7498287
所以我们要在web.xml指定下前端的控制器,注意servlet的话都是在web.xml进行设置的。
知识点:注意servlet要在哪里配置,比如控制器,过滤器,拦截器,要统一在web.xml配置,直接和容器说话。
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
查看DispatcherServlet的父类。
任意地址都会触发DispatchServlet去找xml配置文件然后启动前端控制器,可以访问了:http://localhost:8080/spring/test1
----测试2------
DispatchServlet是集成自FrameworkServlet的。
看源码:
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);//这个方法
}
else {
super.service(request, response);
}
}
这个方法。
还是这个类:
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);//这个方法
}
/**
* Delegate POST requests to {@link #processRequest}.
* @see #doService
*/
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
这两个类都有一个共同得方法:processRequest
进入
processRequest(request, response);
找到关键的方法:
try {
doService(request, response);
}
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
}
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
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.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
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 {
doDispatch(request, response);//关键代码
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
关键代码,这个是核心的方法。
这个是这篇博客最核心的方法:
doDispatch(request, response);//关键代码
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 {
//1.处理二进制的判断是不是二进制的请求,是二进制就处理文件上传相关的
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
//2.流程图的获取请求的handler
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
//3.这个也是核心的方法
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;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
注意上面的注解2进入下面的方法,这个hanlerMappings是已经在容器中的了。
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping hm : this.handlerMappings) {//循环这个东西这个要加断点取调下。
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;//虽然是for循环但是只要拿到了就return
}
}
}
return null;
}
返回的是执行链。
拿到这个handlerMappings是一个集合,hm找到指定的控制器封装为控制器:
看到这个是我们自己想要的控制器这个控制器,此时必须匹配我们的路径/test1,request里面带的路径的,和一个拦截器,这个拦截器是系统定义好的也可以是我们定义的。
1.发送请求
2.获取请求得handler
3.返回handler
往下走:
进入getHandlerAdapter,这个将上次返回的调用链传进去。
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
看下这三个适配器,针对不同得hadler使用不同得adapter。
看这个配置文件。
这里面定义了两个了:
可以看到handlerMapper和handlerMapper是已经定义好了。
这个就是为什么一上来就能加载三个得原因。
拿到handler的目的就是想拿到我们的控制器,比如我们定义的/test就拿到了我们自己定义的控制器,在哪里,就在handler链里面。
控制器在创建的时候有三种方式:
继承Controller 也可以使用注解的方式 在web.xml配置的。
下一步就是,返回一个ModelAndView给DispatchServlet。
当前的mapperHandler去匹配。
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
之后就是如下几步。
这个就是在xml定义的controller的流程。
----------------------------------------------------------------------------------------------------------------------------------------------
再次新建一个类,加controller注解:
package com.liban.demo2;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class AnotationController {
@RequestMapping("/test2")
public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
System.out.println("BeanController run.......");
return null;
}
}
此时不用在容器配置Bean了,不配置bean你就得配置扫包,如何能识别呢?要在application.xml这样配置:
开启的是mvc扫包。
扫包的代码:
<context:component-scan base-package="com.liban.demo2"></context:component-scan>
<mvc:annotation-driven></mvc:annotation-driven>
修改下:
这里可以看到:
-----------------------------------------------------------------------------
开始调试test1:
1.进入核心的方法doDispatch
走到这个方法。 看下注解,通过当前的请求去拿handler。 进入gethandler方法。
看到handlerMappinig不为空。
在配置文件加载的。
调试下拿到的是BeanNameUrl。。。。。。控制器的调用链。
就是控制器。
---
接下来进入第二个非常重要的方法,这个方法,去取适配器。
三个适配器也是在配置文件里面读取的。
拿到匹配的返回适配器。
适配器拿到就说明他知道怎么执行我们之前拿到的控制器了。
往下走到这个方法,这个是执行拦截器的方法是十分重要的。
这个方法:这个就是在handler执行之前执行一堆拦截器的方法。
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
接下来:
返回model and view
进入这个方法,注意是simpleControll。。。。。。实现的
注意这里的强转,所以要求test1实现Controller的。
-------------------------------------------------------------------------------------
接下来测试test2,我们重点去看这两个核心的方法:
进去getHandler方法:
注意这次匹配的是什么?
--
注意这个HandlerMapping,集合存放两个,哪个匹配就返回。
两个:一个匹配xml的控制器,一个匹配注解的控制器。
拿到这个handler其实就是拿到了我们自己写的控制器,把控制器和过滤器一起打包放在执行chain里面。
再往下走:
之前用的是simple适配器;
这次用的是Request。
可以看到这个已经拿到了。
封装了我们的控制器和拦截器。
在执行之前先处理下拦截器。
进去:
进入这里:
这个方法和之前的有什么不一样呢?在里面没有强转。
这个就是看到底打了什么注解。
之前的没打注解@Controller是这样的:
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
--------------------------------------------
注意是找到ModelAndView制定的视图。
第三步就是对应流程图的handlerAdapter。
---------------------------------------------
源码位置:luban/mvc