这里继续续写上一章博客(111章博客):
Spring MVC 源码深度剖析:
既然我们自行写出了一个,那么我们可以选择看看mvc源码:
前端控制器 DispatcherServlet 继承结构:
前面我们知道mvc是操作同一个方法,也就是processRequest,只是当时我们没有细说,现在我们开始细说,如果前面自己仔细学习过,那么这个图就能非常明白,并且如果手动的去看看结构,自然也会明白的
为了源码的解析,我们还需要创建一个项目来进行看看:
对应的依赖(也就是配置):
< packaging> war</ packaging> < dependencies> < dependency> < groupId> org.springframework</ groupId> < artifactId> spring-webmvc</ artifactId> < version> 5.1.5.RELEASE</ version> </ dependency> < dependency> < groupId> javax.servlet</ groupId> < artifactId> javax.servlet-api</ artifactId> < version> 3.1.0</ version> </ dependency> < dependency> < groupId> javax.servlet.jsp</ groupId> < artifactId> jsp-api</ artifactId> < version> 2.2</ version> < scope> provided</ scope> </ dependency> < dependency> < groupId> com.fasterxml.jackson.core</ groupId> < artifactId> jackson-databind</ artifactId> < version> 2.9.8</ version> </ dependency> < dependency> < groupId> com.fasterxml.jackson.core</ groupId> < artifactId> jackson-core</ artifactId> < version> 2.9.8</ version> </ dependency> < dependency> < groupId> com.fasterxml.jackson.core</ groupId> < artifactId> jackson-annotations</ artifactId> < version> 2.9.0</ version> </ dependency> </ dependencies>
对应的web.xml:
<?xml version="1.0" encoding="UTF-8"?>
< web-app xmlns = " http://xmlns.jcp.org/xml/ns/javaee" xmlns: xsi= " http://www.w3.org/2001/XMLSchema-instance" xsi: schemaLocation= " http://xmlns.jcp.org/xml/ns/javaeehttp://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version = " 4.0" > < 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:spring-mvc.xml</ param-value> </ init-param> < load-on-startup> 2</ load-on-startup> </ servlet> < servlet-mapping> < servlet-name> dispatcherServlet</ servlet-name> < url-pattern> /</ url-pattern> </ servlet-mapping>
</ web-app>
对应的spring-mvc.xml:
< beans xmlns = " http://www.springframework.org/schema/beans" xmlns: mvc= " http://www.springframework.org/schema/mvc" xmlns: context= " http://www.springframework.org/schema/context" xmlns: xsi= " http://www.w3.org/2001/XMLSchema-instance" xsi: schemaLocation= " http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsd" > < context: component-scan base-package = " com" /> </ beans>
对应的Demo接口及其实现类:
package com. service ; public interface Demo { void fa ( String name) ;
}
package com. service. impl ; import com. service. Demo ;
import org. springframework. stereotype. Service ; @Service
public class DemoImpl implements Demo { @Override public void fa ( String name) { System . out. println ( "你好:" + name) ; }
}
对应的DemoController类:
package com. controller ; import com. service. Demo ;
import org. springframework. beans. factory. annotation. Autowired ;
import org. springframework. stereotype. Controller ;
import org. springframework. web. bind. annotation. RequestMapping ; @Controller
@RequestMapping ( "/demo" )
public class DemoController { @Autowired private Demo dome; @RequestMapping ( "/name" ) public void fa ( String name) { dome. fa ( name) ; }
}
现在自行配置服务器,然后启动后,访问http://localhost:8080/mvcdome/demo/name?name=2(mvcdome是设置的项目名),如果打印了"你好:2",说明操作成功(视图没有处理,所以只需要看打印即可),即项目创建完毕,现在我们就以这个项目来学习源码或者阅读解析一下源码,当然,这样的处理并不非常好,因为我们并不能随时修改细节,所以与spring一样需要下载源码当然,在前面说明spring时,我们已经下载好源码了,具体可以到107章博客进行下载(对应的spring源码是包括mvc的),然后进行编译操作,当然,这里会给出步骤,虽然我已经操作一次了(107章博客),但是对应的只是叫我们自己去博客中操作,所以这里我们自己也来给出一个步骤:
首先就是拿取对应的spring源码:
下载地址:
相关文件的下载地址:
链接:https://pan.baidu.com/s/1I1S_vut_-VRhsNj_hH7e1A
提取码:alsk
我们需要学习gradle,看这个地址:
https://blog.csdn.net/qq_59609098/article/details/135358948
拿取里面的低版本,使用idea打开对应的项目,刷新即可(前提选择gradle一下,jdk选择1.8吧,可以试着选择其他高版本)
学习好后,配置一下gradle作用与这个项目(刷新),虽然在107章博客中,我们手动的进行了编译,但是其实并不需要的,我们直接对父项目进行编译即可(虽然会使得里面的所有都进行编译(build自然是包括测试的),甚至可能你不用的,一般来说spring的对应源码只需要操作测试即可(使用的过程中,其实也是操作的测试,在107章博客就是如此)),然后我们在对应的项目中,创建子项目(模块)spring-grTest(依赖对应maven的三个地方即可(groupId,artifactId,version)):
直接给出依赖吧:
plugins { id 'java' id 'war'
} group 'org.springframework'
version '5.1.21.BUILD-SNAPSHOT' repositories { mavenCentral ( )
} dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' implementation 'org.springframework:spring-webmvc:5.1.5.RELEASE' implementation 'javax.servlet:javax.servlet-api:3.1.0' providedCompile 'javax.servlet.jsp:jsp-api:2.2' implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8' implementation 'com.fasterxml.jackson.core:jackson-core:2.9.8' implementation 'com.fasterxml.jackson.core:jackson-annotations:2.9.0'
} test { useJUnitPlatform ( )
}
在这个项目中,我们先不使用本地的东西,然后创建上面的项目案例,也就是前面的DemoController,然后再该gradle中进行部署,然后访问,若出现了信息,说明处理成功
当你处理成功后,我们准备进行源码的分析:
首先还是前端控制器DispatcherServlet的分析,这个分析首先需要说明前面的图,按照源码来看:
一般,我们操作的原生基本都是处理service方法,在HttpServlet 中重写了他,他里面调用了this.service(request, response);,他区分了get和post来进行调用get方法和post方法,而前端控制器DispatcherServlet所继承的类就重写了这两个方法
前端控制器具体操作关系如下(全部的流程关系,建议看完):
前面我们知道有这些主要的组件:
可以在后面的代码中搜索看看是哪个,很明显,大多数中doService定义初始,doDispatch操作映射调用关系,render操作视图,其他的基本都是中间数据转移过程
public class DispatcherServlet extends FrameworkServlet { protected void doService ( HttpServletRequest request, HttpServletResponse response) throws Exception { this . logRequest ( request) ; Map < String , Object > attributesSnapshot = null ; if ( WebUtils . isIncludeRequest ( request) ) { attributesSnapshot = new HashMap ( ) ; Enumeration attrNames = request. getAttributeNames ( ) ;
label95: while ( true ) { String attrName; do { if ( ! attrNames. hasMoreElements ( ) ) { break label95; }
attrName = ( String ) attrNames. nextElement ( ) ; } while ( ! this . cleanupAfterInclude && ! attrName. startsWith ( "org.springframework.web.servlet" ) ) ; attributesSnapshot. put ( attrName, request. getAttribute ( attrName) ) ; } } request. setAttribute ( WEB_APPLICATION_CONTEXT_ATTRIBUTE , this . getWebApplicationContext ( ) ) ; request. setAttribute ( LOCALE_RESOLVER_ATTRIBUTE , this . localeResolver) ; request. setAttribute ( THEME_RESOLVER_ATTRIBUTE , this . themeResolver) ; request. setAttribute ( THEME_SOURCE_ATTRIBUTE , this . 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 { this . doDispatch ( request, response) ; } finally { if ( ! WebAsyncUtils . getAsyncManager ( request) . isConcurrentHandlingStarted ( ) && attributesSnapshot != null ) { this . restoreAttributesAfterInclude ( request, attributesSnapshot) ; } } } 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 ; } 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) ; } } } private void processDispatchResult ( HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { boolean errorView = false ; if ( exception != null ) { if ( exception instanceof ModelAndViewDefiningException ) { this . logger. debug ( "ModelAndViewDefiningException encountered" , exception) ; mv = ( ( ModelAndViewDefiningException ) exception) . getModelAndView ( ) ; } else { Object handler = mappedHandler != null ? mappedHandler. getHandler ( ) : null ; mv = this . processHandlerException ( request, response, handler, exception) ; errorView = mv != null ; } } if ( mv != null && ! mv. wasCleared ( ) ) { this . render ( mv, request, response) ; if ( errorView) { WebUtils . clearErrorRequestAttributes ( request) ; } } else if ( this . logger. isTraceEnabled ( ) ) { this . logger. trace ( "No view rendering, null ModelAndView returned." ) ; } if ( ! WebAsyncUtils . getAsyncManager ( request) . isConcurrentHandlingStarted ( ) ) { if ( mappedHandler != null ) { mappedHandler. triggerAfterCompletion ( request, response, ( Exception ) null ) ; } } } protected void render ( ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { Locale locale = this . localeResolver != null ? this . localeResolver. resolveLocale ( request) : request. getLocale ( ) ; response. setLocale ( locale) ; String viewName = mv. getViewName ( ) ; View view; if ( viewName != null ) { view = this . resolveViewName ( viewName, mv. getModelInternal ( ) , locale, request) ; if ( view == null ) { throw new ServletException ( "Could not resolve view with name '" + mv. getViewName ( ) + "' in servlet with name '" + this . getServletName ( ) + "'" ) ; } } else { view = mv. getView ( ) ; if ( view == null ) { throw new ServletException ( "ModelAndView [" + mv + "] neither contains a view name nor a View object in servlet with name '" + this . getServletName ( ) + "'" ) ; } } if ( this . logger. isTraceEnabled ( ) ) { this . logger. trace ( "Rendering view [" + view + "] " ) ; } try { if ( mv. getStatus ( ) != null ) { response. setStatus ( mv. getStatus ( ) . value ( ) ) ; } view. render ( mv. getModelInternal ( ) , request, response) ; } catch ( Exception var8) { if ( this . logger. isDebugEnabled ( ) ) { this . logger. debug ( "Error rendering view [" + view + "]" , var8) ; } throw var8; } } @Nullable protected View resolveViewName ( String viewName, @Nullable Map < String , Object > model, Locale locale, HttpServletRequest request) throws Exception { if ( this . viewResolvers != null ) { Iterator var5 = this . viewResolvers. iterator ( ) ; while ( var5. hasNext ( ) ) { ViewResolver viewResolver = ( ViewResolver ) var5. next ( ) ; View view = viewResolver. resolveViewName ( viewName, locale) ; if ( view != null ) { return view; } } } return null ; }
} public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware { protected void service ( HttpServletRequest request, HttpServletResponse response) throws ServletException , IOException { HttpMethod httpMethod = HttpMethod . resolve ( request. getMethod ( ) ) ; if ( httpMethod != HttpMethod . PATCH && httpMethod != null ) { super . service ( request, response) ; } else { this . processRequest ( request, response) ; } } protected final void doGet ( HttpServletRequest request, HttpServletResponse response) throws ServletException , IOException { this . processRequest ( request, response) ; } 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 previousLocaleContext = LocaleContextHolder . getLocaleContext ( ) ; LocaleContext localeContext = this . buildLocaleContext ( request) ; RequestAttributes previousAttributes = RequestContextHolder . getRequestAttributes ( ) ; ServletRequestAttributes requestAttributes = this . buildRequestAttributes ( request, response, previousAttributes) ; WebAsyncManager asyncManager = WebAsyncUtils . getAsyncManager ( request) ; asyncManager. registerCallableInterceptor ( FrameworkServlet . class . getName ( ) , new FrameworkServlet. RequestBindingInterceptor ( ) ) ; 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) ; } } protected abstract void doService ( HttpServletRequest var1, HttpServletResponse var2) throws Exception ; }
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable , EnvironmentAware {
} public abstract class HttpServlet extends GenericServlet { protected void doGet ( HttpServletRequest req, HttpServletResponse resp) throws ServletException , IOException { String protocol = req. getProtocol ( ) ; String msg = lStrings. getString ( "http.method_get_not_supported" ) ; if ( protocol. endsWith ( "1.1" ) ) { resp. sendError ( 405 , msg) ; } else { resp. sendError ( 400 , msg) ; } } protected void doPost ( HttpServletRequest req, HttpServletResponse resp) throws ServletException , IOException { String protocol = req. getProtocol ( ) ; String msg = lStrings. getString ( "http.method_post_not_supported" ) ; if ( protocol. endsWith ( "1.1" ) ) { resp. sendError ( 405 , msg) ; } else { resp. sendError ( 400 , msg) ; } } / . . protected void service ( HttpServletRequest req, HttpServletResponse resp) throws ServletException , IOException { String method = req. getMethod ( ) ; long lastModified; if ( method. equals ( "GET" ) ) { lastModified = this . getLastModified ( req) ; if ( lastModified == - 1L ) { this . doGet ( req, resp) ; } else { long ifModifiedSince = req. getDateHeader ( "If-Modified-Since" ) ; if ( ifModifiedSince < lastModified) { this . maybeSetLastModified ( resp, lastModified) ; this . doGet ( req, resp) ; } else { resp. setStatus ( 304 ) ; } } } else if ( method. equals ( "HEAD" ) ) { lastModified = this . getLastModified ( req) ; this . maybeSetLastModified ( resp, lastModified) ; this . doHead ( req, resp) ; } else if ( method. equals ( "POST" ) ) { this . doPost ( req, resp) ; } else if ( method. equals ( "PUT" ) ) { this . doPut ( req, resp) ; } else if ( method. equals ( "DELETE" ) ) { this . doDelete ( req, resp) ; } else if ( method. equals ( "OPTIONS" ) ) { this . doOptions ( req, resp) ; } else if ( method. equals ( "TRACE" ) ) { this . doTrace ( req, resp) ; } else { String errMsg = lStrings. getString ( "http.method_not_implemented" ) ; Object [ ] errArgs = new Object [ ] { method} ; errMsg = MessageFormat . format ( errMsg, errArgs) ; resp. sendError ( 501 , errMsg) ; } } public void service ( ServletRequest req, ServletResponse res) throws ServletException , IOException { if ( req instanceof HttpServletRequest && res instanceof HttpServletResponse ) { HttpServletRequest request = ( HttpServletRequest ) req; HttpServletResponse response = ( HttpServletResponse ) res; this . service ( request, response) ; } else { throw new ServletException ( "non-HTTP request or response" ) ; } }
}
public abstract class GenericServlet implements Servlet , ServletConfig , Serializable { public abstract void service ( ServletRequest var1, ServletResponse var2) throws ServletException , IOException ;
}
package javax. servlet ; import java. io. IOException ; public interface Servlet { void init ( ServletConfig var1) throws ServletException ; ServletConfig getServletConfig ( ) ; void service ( ServletRequest var1, ServletResponse var2) throws ServletException , IOException ; String getServletInfo ( ) ; void destroy ( ) ;
}
最终被doDispatch进行处理(并且,get和post都是调用同一个方法,也就是说,或者验证了,get和post之前请求方式的一种区别,最终操作的地方是一致的),也由于调用相同的,所以考虑了只用一个servlet来完成,即载一个servlet中判断请求和请求方式,而不是分开,简单来说就是聚合了,当然了,tomcat其实是让继承了Servlet相关类或者他这个接口来接收请求的,所以前端控制器的父必然是Servlet,而具体的路径除了配置文件外,也可以通过注解,这些都是设置某些东西来的,而mvc内部自然也是如此处理,只不过是否是自定义的就不清楚了(如前面我们自定义的找路径操作,而不是使用servlet的注解相关路径的操作,自带的,具体可以参照50章博客),那么我们需要进行调试才会明白
上面基本说明了mvc的执行流程了,自己看看吧
当然,我们通常是需要用调试来确定调用的,而不是单纯的分析(虽然分析也基本正确),所以为了操作跳转,我们也需要操作一下视图,然后验证上面的说明:
在spring-mvc.xml中加上如下:
< bean id = " viewResolver" class = " org.springframework.web.servlet.view.InternalResourceViewResolver" > < property name = " prefix" value = " /WEB-INF/jsp/" > </ property> < property name = " suffix" value = " .jsp" > </ property> </ bean> < mvc: annotation-driven> </ mvc: annotation-driven>
然后在WEB-INF中创建jsp目录,然后创建index.jsp文件:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>
<%System.out.println("跳转到页面");%>
跳转成功,${date}
</body>
</html>
然后修改DemoController里面的fa方法:
@RequestMapping ( "/name" ) public String fa ( String name, Map < String , Object > map) { dome. fa ( name) ; Date date = new Date ( ) ; map. put ( "date" , date) ; return "index" ; }
然后,我们启动,访问http://localhost:8080/gradle/demo/name?name=2,项目名设置一般人都会知道的,当然这里还是提一下:
当然,他是本质的,而他的变化会导致这里的变化:
当然,他只是定义一个初始,其实前面那个图才是本质,也要注意,如果前面哪个图中,有多个项目部署的,虽然这个初始可能只能操作一个,但是其实都可以操作的,因为他们最终还是在tomcat上处理,而tomcat本身就可以操作多个项目(或者说web项目)
看看页面的变化吧,若有数据了,说明操作成功,那么我们给这个地方打上断点:
@RequestMapping ( "/name" ) public String fa ( String name, Map < String , Object > map) { dome. fa ( name) ; Date date = new Date ( ) ; map. put ( "date" , date) ; return "index" ; }
进行调试,我们看看调用栈:
与前面注释说明是一样的,是用来得到返回值的
我们可以看看他是什么时候进行渲染,又或者说,是什么时候开始解析或者编译对应的jsp:
所以我们给这里进行打上断点:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>
<!--下面一行打上断点-->
<%System.out.println("跳转到页面");%>
跳转成功,${date}
</body>
</html>
直接访问,可以看到这里(与前面注释说明几乎类似,后面就不提示了):
前面注释基本只是一个大致流程,首先,我们看这里:
mappedHandler = this . getHandler ( processedRequest) ;
虽然我们操作过模拟,但是始终是自己操作的,具体源码怎么样我们并不知道,那么他是怎么获取的呢,我们给他打上断点
进入到这里:
@Nullable protected HandlerExecutionChain getHandler ( HttpServletRequest request) throws Exception { if ( this . handlerMappings != null ) { for ( HandlerMapping mapping : this . handlerMappings) { HandlerExecutionChain handler = mapping. getHandler ( request) ; if ( handler != null ) { return handler; } } } return null ; }
this.handlerMappings一般会存在两个对象(可能随着版本而改变):
看名称我们可以知道,基本上我们主要操作第一个,或者说,是第一个最后会使得进行返回
这里我们可能需要修改一下源码,所以我们修改依赖,来使用本地的东西:
dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' implementation project ( ":spring-webmvc" ) implementation 'javax.servlet:javax.servlet-api:3.1.0' providedCompile 'javax.servlet.jsp:jsp-api:2.2' implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8' implementation 'com.fasterxml.jackson.core:jackson-core:2.9.8' implementation 'com.fasterxml.jackson.core:jackson-annotations:2.9.0'
}
然后我们重新启动,继续到上面的断点,然后进入,在HandlerExecutionChain handler = mapping.getHandler(request);的这个方法前面加上如下:
System.out.println(mapping);
一般我得到的是org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping@653d1c3c
我们进入到第一个类里面查看,也就是进入上面的类中:
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping implements MatchableHandlerMapping , EmbeddedValueResolverAware { } public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping < RequestMappingInfo > {
} public abstract class AbstractHandlerMethodMapping < T > extends AbstractHandlerMapping implements InitializingBean {
}
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping , Ordered , BeanNameAware { @Override @Nullable public final HandlerExecutionChain getHandler ( HttpServletRequest request) throws Exception { Object handler = getHandlerInternal ( request) ; if ( handler == null ) { handler = getDefaultHandler ( ) ; } if ( handler == null ) { return null ; } if ( handler instanceof String ) { String handlerName = ( String ) handler; handler = obtainApplicationContext ( ) . getBean ( handlerName) ; } HandlerExecutionChain executionChain = getHandlerExecutionChain ( handler, request) ; if ( logger. isTraceEnabled ( ) ) { logger. trace ( "Mapped to " + handler) ; } else if ( logger. isDebugEnabled ( ) && ! request. getDispatcherType ( ) . equals ( DispatcherType . ASYNC ) ) { logger. debug ( "Mapped to " + executionChain. getHandler ( ) ) ; } if ( CorsUtils . isCorsRequest ( request) ) { CorsConfiguration globalConfig = this . corsConfigurationSource. getCorsConfiguration ( request) ; CorsConfiguration handlerConfig = getCorsConfiguration ( handler, request) ; CorsConfiguration config = ( globalConfig != null ? globalConfig. combine ( handlerConfig) : handlerConfig) ; executionChain = getCorsHandlerExecutionChain ( request, executionChain, config) ; } return executionChain; }
}
这样我们基本说明完毕,对应的映射器是怎么拿取的了,那么适配器呢,也就是这里:
HandlerAdapter ha = this . getHandlerAdapter ( mappedHandler. getHandler ( ) ) ;
protected HandlerAdapter getHandlerAdapter ( Object handler) throws ServletException { if ( this . handlerAdapters != null ) { for ( HandlerAdapter adapter : this . handlerAdapters) { 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" ) ; }
操作的适配器是这三个:
一般我得到的是这一个(与前面一样,操作打印即可):org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter@6cacb95e
他一般是如下的:
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware , InitializingBean { @Override protected boolean supportsInternal ( HandlerMethod handlerMethod) { return true ; } } public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter , Ordered {
@Override public final boolean supports ( Object handler) { return ( handler instanceof HandlerMethod && supportsInternal ( ( HandlerMethod ) handler) ) ; }
}
至此基本说明完毕,一般在对应的类中:
@SuppressWarnings ( "serial" )
public class DispatcherServlet extends FrameworkServlet { @Nullable private MultipartResolver multipartResolver; @Nullable private LocaleResolver localeResolver; @Nullable private ThemeResolver themeResolver; @Nullable private List < HandlerMapping > handlerMappings; @Nullable private List < HandlerAdapter > handlerAdapters; @Nullable private List < HandlerExceptionResolver > handlerExceptionResolvers; @Nullable private RequestToViewNameTranslator viewNameTranslator; @Nullable private FlashMapManager flashMapManager; @Nullable private List < ViewResolver > viewResolvers; }
对比一下这个:
对比:
正好与九大组件完全一致,这里知道就可以了(因为九大组件的说明,我们基本说明了两个,映射器和适配器,当然,并非要将所有组件都进行说明)
他们基本都是接口,而接口定义规范,自然是有好处的(基本上,框架中都会要这样处理),他们的初始化通常在这里:
@SuppressWarnings ( "serial" )
public class DispatcherServlet extends FrameworkServlet { @Override protected void onRefresh ( ApplicationContext context) { initStrategies ( context) ; } protected void initStrategies ( ApplicationContext context) { initMultipartResolver ( context) ; initLocaleResolver ( context) ; initThemeResolver ( context) ; initHandlerMappings ( context) ; initHandlerAdapters ( context) ; initHandlerExceptionResolvers ( context) ; initRequestToViewNameTranslator ( context) ; initViewResolvers ( context) ; initFlashMapManager ( context) ; } }
我们可以给initStrategies(context);加上断点:
启动应该可以看到这里:
左下角是调用栈,我们往下滑,是可以看到这个的:
在Spring中(107章博客)的IOC容器初始化中就存在这个
所以在操作之前,Spring的相关扫描已经操作完毕,正好与我们模拟的一样,在对应的映射器操作之前,首先需要操作Spring的容器初始化
我们就打开映射器的初始化操作:
private void initHandlerMappings ( ApplicationContext context) { this . handlerMappings = null ; if ( this . detectAllHandlerMappings) { Map < String , HandlerMapping > matchingBeans = BeanFactoryUtils . beansOfTypeIncludingAncestors ( context, HandlerMapping . class , true , false ) ; if ( ! matchingBeans. isEmpty ( ) ) { this . handlerMappings = new ArrayList < > ( matchingBeans. values ( ) ) ; AnnotationAwareOrderComparator . sort ( this . handlerMappings) ; } } else { try { HandlerMapping hm = context. getBean ( HANDLER_MAPPING_BEAN_NAME , HandlerMapping . class ) ; this . handlerMappings = Collections . singletonList ( hm) ; } catch ( NoSuchBeanDefinitionException ex) { } } if ( this . handlerMappings == null ) { this . handlerMappings = getDefaultStrategies ( context, HandlerMapping . class ) ; if ( logger. isTraceEnabled ( ) ) { logger. trace ( "No HandlerMappings declared for servlet '" + getServletName ( ) + "': using default strategies from DispatcherServlet.properties" ) ; } } }
我们也可进入initMultipartResolver(context);:
private void initMultipartResolver ( ApplicationContext context) { try { this . multipartResolver = context. getBean ( MULTIPART_RESOLVER_BEAN_NAME , MultipartResolver . class ) ; if ( logger. isTraceEnabled ( ) ) { logger. trace ( "Detected " + this . multipartResolver) ; } else if ( logger. isDebugEnabled ( ) ) { logger. debug ( "Detected " + this . multipartResolver. getClass ( ) . getSimpleName ( ) ) ; } } catch ( NoSuchBeanDefinitionException ex) { this . multipartResolver = null ; if ( logger. isTraceEnabled ( ) ) { logger. trace ( "No MultipartResolver '" + MULTIPART_RESOLVER_BEAN_NAME + "' declared" ) ; } } }
这里只有一个关键代码:this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
this . multipartResolver = context. getBean ( MULTIPART_RESOLVER_BEAN_NAME , MultipartResolver . class ) ; public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver" ;
这也是为什么操作文件时,mvc基本上必须需要设置文件的解析器(通常不设置就会报错),并且名称也基本需要multipartResolver,这也是为什么有些地方的名称必须固定的原因(在DispatcherServlet中也有其他写死的,具体可以看看)
我们继续对原来的DispatcherServlet类的doDispatch方法里面进行分析,前面既然我们得到了适配器,现在需要进行获得结果了,所以我们到这里,这里对适配器进行操作:
mv = ha. handle ( processedRequest, response, mappedHandler. getHandler ( ) ) ;
适配器只是判断是否支持,并没有保存映射器,所以这里需要传递映射器,所以是操作对应适配器的方法(甚至包括了执行方法处理,而不只是操作参数位置(当然,可能还有其他处理,如参数存在注解操作等等)),他传递了请求和响应(这个请求是处理了对应的文件操作的,响应是为了判断是否直接的返回,因为没有操作视图的话,可能是需要直接处理响应的内容的,而不用操作视图转化的响应了,因为视图最终也是需要给响应的,响应是根本),这样处理后,自然会根据这个结果来判断是否操作视图,如果没有,响应里面自然有信息,自然显示给前端,如果有,自然操作视图,最后再给响应,简单来说,他在里面确定的返回值保证是直接操作响应,或者是操作返回值,操作视图,然后操作响应,都可以根据这个返回值是否为空来判断的
调试时还是访问:http://localhost:8080/gradle/demo/name?name=2
那么我们进去看看他做了什么:
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware , InitializingBean { @Override protected ModelAndView handleInternal ( HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ModelAndView mav; checkRequest ( request) ; if ( this . synchronizeOnSession) { HttpSession session = request. getSession ( false ) ; if ( session != null ) { Object mutex = WebUtils . getSessionMutex ( session) ; synchronized ( mutex) { mav = invokeHandlerMethod ( request, response, handlerMethod) ; } } else { mav = invokeHandlerMethod ( request, response, handlerMethod) ; } } else { mav = invokeHandlerMethod ( request, response, handlerMethod) ; } if ( ! response. containsHeader ( HEADER_CACHE_CONTROL ) ) { if ( getSessionAttributesHandler ( handlerMethod) . hasSessionAttributes ( ) ) { applyCacheSeconds ( response, this . cacheSecondsForSessionAttributeHandlers) ; } else { prepareResponse ( response) ; } } return mav; } } public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter , Ordered { @Override @Nullable public final ModelAndView handle ( HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return handleInternal ( request, response, ( HandlerMethod ) handler) ; } }
很明显,得到结果的方法在于:mav = invokeHandlerMethod(request, response, handlerMethod);,我们进入:
@Nullable
protected ModelAndView invokeHandlerMethod ( HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ServletWebRequest webRequest = new ServletWebRequest ( request, response) ; try { WebDataBinderFactory binderFactory = getDataBinderFactory ( handlerMethod) ; ModelFactory modelFactory = getModelFactory ( handlerMethod, binderFactory) ; ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod ( handlerMethod) ; if ( this . argumentResolvers != null ) { invocableMethod. setHandlerMethodArgumentResolvers ( this . argumentResolvers) ; } if ( this . returnValueHandlers != null ) { invocableMethod. setHandlerMethodReturnValueHandlers ( this . returnValueHandlers) ; } invocableMethod. setDataBinderFactory ( binderFactory) ; invocableMethod. setParameterNameDiscoverer ( this . parameterNameDiscoverer) ; ModelAndViewContainer mavContainer = new ModelAndViewContainer ( ) ; mavContainer. addAllAttributes ( RequestContextUtils . getInputFlashMap ( request) ) ; modelFactory. initModel ( webRequest, mavContainer, invocableMethod) ; mavContainer. setIgnoreDefaultModelOnRedirect ( this . ignoreDefaultModelOnRedirect) ; AsyncWebRequest asyncWebRequest = WebAsyncUtils . createAsyncWebRequest ( request, response) ; asyncWebRequest. setTimeout ( this . asyncRequestTimeout) ; WebAsyncManager asyncManager = WebAsyncUtils . getAsyncManager ( request) ; asyncManager. setTaskExecutor ( this . taskExecutor) ; asyncManager. setAsyncWebRequest ( asyncWebRequest) ; asyncManager. registerCallableInterceptors ( this . callableInterceptors) ; asyncManager. registerDeferredResultInterceptors ( this . deferredResultInterceptors) ; if ( asyncManager. hasConcurrentResult ( ) ) { Object result = asyncManager. getConcurrentResult ( ) ; mavContainer = ( ModelAndViewContainer ) asyncManager. getConcurrentResultContext ( ) [ 0 ] ; asyncManager. clearConcurrentResult ( ) ; LogFormatUtils . traceDebug ( logger, traceOn -> { String formatted = LogFormatUtils . formatValue ( result, ! traceOn) ; return "Resume with async result [" + formatted + "]" ; } ) ; invocableMethod = invocableMethod. wrapConcurrentResult ( result) ; } invocableMethod. invokeAndHandle ( webRequest, mavContainer) ; if ( asyncManager. isConcurrentHandlingStarted ( ) ) { return null ; } return getModelAndView ( mavContainer, modelFactory, webRequest) ; } finally { webRequest. requestCompleted ( ) ; } }
上面操作了数据的开始就是invocableMethod.invokeAndHandle(webRequest, mavContainer);,他基本是本质上的处理结果,我们进入他:
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod { public void invokeAndHandle ( ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object . . . providedArgs) throws Exception { Object returnValue = invokeForRequest ( webRequest, mavContainer, providedArgs) ; setResponseStatus ( webRequest) ; if ( returnValue == null ) { if ( isRequestNotModified ( webRequest) || getResponseStatus ( ) != null || mavContainer. isRequestHandled ( ) ) { disableContentCachingIfNecessary ( webRequest) ; mavContainer. setRequestHandled ( true ) ; return ; } } else if ( StringUtils . hasText ( getResponseStatusReason ( ) ) ) { mavContainer. setRequestHandled ( true ) ; return ; } mavContainer. setRequestHandled ( false ) ; Assert . state ( this . returnValueHandlers != null , "No return value handlers" ) ; try { this . returnValueHandlers. handleReturnValue ( returnValue, getReturnValueType ( returnValue) , mavContainer, webRequest) ; } catch ( Exception ex) { if ( logger. isTraceEnabled ( ) ) { logger. trace ( formatErrorForReturnValue ( returnValue) , ex) ; } throw ex; } } }
很明显,上面的Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);是对应的处理,我们继续进入:
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
} public class InvocableHandlerMethod extends HandlerMethod { @Nullable public Object invokeForRequest ( NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object . . . providedArgs) throws Exception { Object [ ] args = getMethodArgumentValues ( request, mavContainer, providedArgs) ; if ( logger. isTraceEnabled ( ) ) { logger. trace ( "Arguments: " + Arrays . toString ( args) ) ; } return doInvoke ( args) ; } @Nullable protected Object doInvoke ( Object . . . args) throws Exception { ReflectionUtils . makeAccessible ( getBridgedMethod ( ) ) ; try { return getBridgedMethod ( ) . invoke ( getBean ( ) , args) ; } catch ( IllegalArgumentException ex) { assertTargetBean ( getBridgedMethod ( ) , getBean ( ) , args) ; String text = ( ex. getMessage ( ) != null ? ex. getMessage ( ) : "Illegal argument" ) ; throw new IllegalStateException ( formatInvokeError ( text, args) , ex) ; } catch ( InvocationTargetException ex) { Throwable targetException = ex. getTargetException ( ) ; if ( targetException instanceof RuntimeException ) { throw ( RuntimeException ) targetException; } else if ( targetException instanceof Error ) { throw ( Error ) targetException; } else if ( targetException instanceof Exception ) { throw ( Exception ) targetException; } else { throw new IllegalStateException ( formatInvokeError ( "Invocation failure" , args) , targetException) ; } } } protected Object [ ] getMethodArgumentValues ( NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object . . . providedArgs) throws Exception { MethodParameter [ ] parameters = getMethodParameters ( ) ; if ( ObjectUtils . isEmpty ( parameters) ) { return EMPTY_ARGS ; } Object [ ] args = new Object [ parameters. length] ; for ( int i = 0 ; i < parameters. length; i++ ) { MethodParameter parameter = parameters[ i] ; parameter. initParameterNameDiscovery ( this . parameterNameDiscoverer) ; args[ i] = findProvidedArgument ( parameter, providedArgs) ; if ( args[ i] != null ) { continue ; } if ( ! this . resolvers. supportsParameter ( parameter) ) { throw new IllegalStateException ( formatArgumentError ( parameter, "No suitable resolver" ) ) ; } try { args[ i] = this . resolvers. resolveArgument ( parameter, mavContainer, request, this . dataBinderFactory) ; } catch ( Exception ex) { if ( logger. isDebugEnabled ( ) ) { String exMsg = ex. getMessage ( ) ; if ( exMsg != null && ! exMsg. contains ( parameter. getExecutable ( ) . toGenericString ( ) ) ) { logger. debug ( formatArgumentError ( parameter, exMsg) ) ; } } throw ex; } } return args; } @Nullable protected Object doInvoke ( Object . . . args) throws Exception { ReflectionUtils . makeAccessible ( getBridgedMethod ( ) ) ; try { return getBridgedMethod ( ) . invoke ( getBean ( ) , args) ; } catch ( IllegalArgumentException ex) { assertTargetBean ( getBridgedMethod ( ) , getBean ( ) , args) ; String text = ( ex. getMessage ( ) != null ? ex. getMessage ( ) : "Illegal argument" ) ; throw new IllegalStateException ( formatInvokeError ( text, args) , ex) ; } catch ( InvocationTargetException ex) { Throwable targetException = ex. getTargetException ( ) ; if ( targetException instanceof RuntimeException ) { throw ( RuntimeException ) targetException; } else if ( targetException instanceof Error ) { throw ( Error ) targetException; } else if ( targetException instanceof Exception ) { throw ( Exception ) targetException; } else { throw new IllegalStateException ( formatInvokeError ( "Invocation failure" , args) , targetException) ; } } } }
上面我们继续说明args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);,也就是赋值的处理,这里说完基本上对应方法调用的过程基本说明完毕,我们进入:
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver { @Override @Nullable public Object resolveArgument ( MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { HandlerMethodArgumentResolver resolver = getArgumentResolver ( parameter) ; if ( resolver == null ) { throw new IllegalArgumentException ( "Unsupported parameter type [" + parameter. getParameterType ( ) . getName ( ) + "]. supportsParameter should be called first." ) ; } return resolver. resolveArgument ( parameter, mavContainer, webRequest, binderFactory) ; } } public interface HandlerMethodArgumentResolver {
}
public abstract class AbstractNamedValueMethodArgumentResolver implements HandlerMethodArgumentResolver { @Override @Nullable public final Object resolveArgument ( MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { NamedValueInfo namedValueInfo = getNamedValueInfo ( parameter) ; MethodParameter nestedParameter = parameter. nestedIfOptional ( ) ; Object resolvedName = resolveStringValue ( namedValueInfo. name) ; if ( resolvedName == null ) { throw new IllegalArgumentException ( "Specified name must not resolve to null: [" + namedValueInfo. name + "]" ) ; } Object arg = resolveName ( resolvedName. toString ( ) , nestedParameter, webRequest) ; if ( arg == null ) { if ( namedValueInfo. defaultValue != null ) { arg = resolveStringValue ( namedValueInfo. defaultValue) ; } else if ( namedValueInfo. required && ! nestedParameter. isOptional ( ) ) { handleMissingValue ( namedValueInfo. name, nestedParameter, webRequest) ; } arg = handleNullValue ( namedValueInfo. name, arg, nestedParameter. getNestedParameterType ( ) ) ; } else if ( "" . equals ( arg) && namedValueInfo. defaultValue != null ) { arg = resolveStringValue ( namedValueInfo. defaultValue) ; } if ( binderFactory != null ) { WebDataBinder binder = binderFactory. createBinder ( webRequest, null , namedValueInfo. name) ; try { arg = binder. convertIfNecessary ( arg, parameter. getParameterType ( ) , parameter) ; } catch ( ConversionNotSupportedException ex) { throw new MethodArgumentConversionNotSupportedException ( arg, ex. getRequiredType ( ) , namedValueInfo. name, parameter, ex. getCause ( ) ) ; } catch ( TypeMismatchException ex) { throw new MethodArgumentTypeMismatchException ( arg, ex. getRequiredType ( ) , namedValueInfo. name, parameter, ex. getCause ( ) ) ; } } handleResolvedValue ( arg, namedValueInfo. name, parameter, mavContainer, webRequest) ; return arg; } }
public class MapMethodProcessor implements HandlerMethodArgumentResolver , HandlerMethodReturnValueHandler { @Override @Nullable public Object resolveArgument ( MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { Assert . state ( mavContainer != null , "ModelAndViewContainer is required for model exposure" ) ; return mavContainer. getModel ( ) ; } } public class ModelAndViewContainer { public ModelMap getModel ( ) { if ( useDefaultModel ( ) ) { return this . defaultModel; } else { if ( this . redirectModel == null ) { this . redirectModel = new ModelMap ( ) ; } return this . redirectModel; } } }
上面我们也大致明白了具体返回值的操作,实际上关于spring的,基本都是建立在spring上开发的,mvc自然也是
这里了解即可,因为如果还要深入,需要对很多类进行说明,学习框架不应如此,当然,如果需要写框架,那么可以建议深入(比如对应的参数中对应的注解操作过程是如何的等等),实际上没有很长时间,几乎不可能将一个框架的每个细节的每个代码都说明完毕,现在也基本没有,就算是开发人员,也基本不会记得,所以有些时候是只需要知道他做了什么即可,深入也是一样
既然方法调用了,我们回到之前的这里:
mv = ha. handle ( processedRequest, response, mappedHandler. getHandler ( ) ) ;
我们回到上面,继续调试,会到这里:
this . processDispatchResult ( processedRequest, response, mappedHandler, mv, ( Exception ) dispatchException) ;
给他打上断点,开始调试:
我们进入:
public class DispatcherServlet extends FrameworkServlet { private void processDispatchResult ( HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { boolean errorView = false ; if ( exception != null ) { if ( exception instanceof ModelAndViewDefiningException ) { this . logger. debug ( "ModelAndViewDefiningException encountered" , exception) ; mv = ( ( ModelAndViewDefiningException ) exception) . getModelAndView ( ) ; } else { Object handler = mappedHandler != null ? mappedHandler. getHandler ( ) : null ; mv = this . processHandlerException ( request, response, handler, exception) ; errorView = mv != null ; } } if ( mv != null && ! mv. wasCleared ( ) ) { this . render ( mv, request, response) ; if ( errorView) { WebUtils . clearErrorRequestAttributes ( request) ; } } else if ( this . logger. isTraceEnabled ( ) ) { this . logger. trace ( "No view rendering, null ModelAndView returned." ) ; } if ( ! WebAsyncUtils . getAsyncManager ( request) . isConcurrentHandlingStarted ( ) ) { if ( mappedHandler != null ) { mappedHandler. triggerAfterCompletion ( request, response, ( Exception ) null ) ; } } } protected void render ( ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { Locale locale = this . localeResolver != null ? this . localeResolver. resolveLocale ( request) : request. getLocale ( ) ; response. setLocale ( locale) ; String viewName = mv. getViewName ( ) ; View view; if ( viewName != null ) { view = this . resolveViewName ( viewName, mv. getModelInternal ( ) , locale, request) ; if ( view == null ) { throw new ServletException ( "Could not resolve view with name '" + mv. getViewName ( ) + "' in servlet with name '" + this . getServletName ( ) + "'" ) ; } } else { view = mv. getView ( ) ; if ( view == null ) { throw new ServletException ( "ModelAndView [" + mv + "] neither contains a view name nor a View object in servlet with name '" + this . getServletName ( ) + "'" ) ; } } if ( this . logger. isTraceEnabled ( ) ) { this . logger. trace ( "Rendering view [" + view + "] " ) ; } try { if ( mv. getStatus ( ) != null ) { response. setStatus ( mv. getStatus ( ) . value ( ) ) ; } view. render ( mv. getModelInternal ( ) , request, response) ; } catch ( Exception var8) { if ( this . logger. isDebugEnabled ( ) ) { this . logger. debug ( "Error rendering view [" + view + "]" , var8) ; } throw var8; } } @Nullable protected View resolveViewName ( String viewName, @Nullable Map < String , Object > model, Locale locale, HttpServletRequest request) throws Exception { if ( this . viewResolvers != null ) { Iterator var5 = this . viewResolvers. iterator ( ) ; while ( var5. hasNext ( ) ) { ViewResolver viewResolver = ( ViewResolver ) var5. next ( ) ; View view = viewResolver. resolveViewName ( viewName, locale) ; if ( view != null ) { return view; } } } return null ; } }
public class InternalResourceViewResolver extends UrlBasedViewResolver {
}
public class UrlBasedViewResolver extends AbstractCachingViewResolver implements Ordered {
} public abstract class AbstractCachingViewResolver extends WebApplicationObjectSupport implements ViewResolver { @Override @Nullable public View resolveViewName ( String viewName, Locale locale) throws Exception { if ( ! isCache ( ) ) { return createView ( viewName, locale) ; } else { Object cacheKey = getCacheKey ( viewName, locale) ; View view = this . viewAccessCache. get ( cacheKey) ; if ( view == null ) { synchronized ( this . viewCreationCache) { view = this . viewCreationCache. get ( cacheKey) ; if ( view == null ) { view = createView ( viewName, locale) ; if ( view == null && this . cacheUnresolved) { view = UNRESOLVED_VIEW ; } if ( view != null ) { this . viewAccessCache. put ( cacheKey, view) ; this . viewCreationCache. put ( cacheKey, view) ; } } } } else { if ( logger. isTraceEnabled ( ) ) { logger. trace ( formatKey ( cacheKey) + "served from cache" ) ; } } return ( view != UNRESOLVED_VIEW ? view : null ) ; } } }
public class InternalResourceViewResolver extends UrlBasedViewResolver {
} public abstract class AbstractCachingViewResolver extends WebApplicationObjectSupport implements ViewResolver {
} public class UrlBasedViewResolver extends AbstractCachingViewResolver implements Ordered { @Override protected View createView ( String viewName, Locale locale) throws Exception { if ( ! canHandle ( viewName, locale) ) { return null ; }
if ( viewName. startsWith ( REDIRECT_URL_PREFIX ) ) { String redirectUrl = viewName. substring ( REDIRECT_URL_PREFIX . length ( ) ) ; RedirectView view = new RedirectView ( redirectUrl, isRedirectContextRelative ( ) , isRedirectHttp10Compatible ( ) ) ; String [ ] hosts = getRedirectHosts ( ) ; if ( hosts != null ) { view. setHosts ( hosts) ; } return applyLifecycleMethods ( REDIRECT_URL_PREFIX , view) ; } if ( viewName. startsWith ( FORWARD_URL_PREFIX ) ) { String forwardUrl = viewName. substring ( FORWARD_URL_PREFIX . length ( ) ) ; InternalResourceView view = new InternalResourceView ( forwardUrl) ; return applyLifecycleMethods ( FORWARD_URL_PREFIX , view) ; } return super . createView ( viewName, locale) ; }
} public abstract class AbstractCachingViewResolver extends WebApplicationObjectSupport implements ViewResolver {
@Nullable protected View createView ( String viewName, Locale locale) throws Exception { return loadView ( viewName, locale) ; }
} public class UrlBasedViewResolver extends AbstractCachingViewResolver implements Ordered { private String prefix = "" ; private String suffix = "" ; protected String getPrefix ( ) { return this . prefix; } protected String getSuffix ( ) { return this . suffix; } @Override protected View loadView ( String viewName, Locale locale) throws Exception { AbstractUrlBasedView view = buildView ( viewName) ; View result = applyLifecycleMethods ( viewName, view) ; return ( view. checkResource ( locale) ? result : null ) ; } protected AbstractUrlBasedView buildView ( String viewName) throws Exception { Class < ? > viewClass = getViewClass ( ) ; Assert . state ( viewClass != null , "No view class" ) ; AbstractUrlBasedView view = ( AbstractUrlBasedView ) BeanUtils . instantiateClass ( viewClass) ; view. setUrl ( getPrefix ( ) + viewName + getSuffix ( ) ) ; String contentType = getContentType ( ) ; if ( contentType != null ) { view. setContentType ( contentType) ; } view. setRequestContextAttribute ( getRequestContextAttribute ( ) ) ; view. setAttributesMap ( getAttributesMap ( ) ) ; Boolean exposePathVariables = getExposePathVariables ( ) ; if ( exposePathVariables != null ) { view. setExposePathVariables ( exposePathVariables) ; } Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes ( ) ; if ( exposeContextBeansAsAttributes != null ) { view. setExposeContextBeansAsAttributes ( exposeContextBeansAsAttributes) ; } String [ ] exposedContextBeanNames = getExposedContextBeanNames ( ) ; if ( exposedContextBeanNames != null ) { view. setExposedContextBeanNames ( exposedContextBeanNames) ; } return view; }
} public class InternalResourceViewResolver extends UrlBasedViewResolver {
@Override protected AbstractUrlBasedView buildView ( String viewName) throws Exception { InternalResourceView view = ( InternalResourceView ) super . buildView ( viewName) ; if ( this . alwaysInclude != null ) { view. setAlwaysInclude ( this . alwaysInclude) ; } view. setPreventDispatchLoop ( true ) ; return view; }
}
ok,我们继续回到之前的:
view = this . resolveViewName ( viewName, mv. getModelInternal ( ) , locale, request) ;
在他后面可以找到:
view. render ( mv. getModelInternal ( ) , request, response) ;
我们进入:
public class InternalResourceView extends AbstractUrlBasedView {
}
public abstract class AbstractUrlBasedView extends AbstractView implements InitializingBean {
}
public abstract class AbstractView extends WebApplicationObjectSupport implements View , BeanNameAware { @Override public void render ( @Nullable Map < String , ? > model, HttpServletRequest request, HttpServletResponse response) throws Exception { if ( logger. isDebugEnabled ( ) ) { logger. debug ( "View " + formatViewName ( ) + ", model " + ( model != null ? model : Collections . emptyMap ( ) ) + ( this . staticAttributes. isEmpty ( ) ? "" : ", static attributes " + this . staticAttributes) ) ; } Map < String , Object > mergedModel = createMergedOutputModel ( model, request, response) ; prepareResponse ( request, response) ; renderMergedOutputModel ( mergedModel, getRequestToExpose ( request) , response) ; } }
public class InternalResourceView extends AbstractUrlBasedView { @Override protected void renderMergedOutputModel ( Map < String , Object > model, HttpServletRequest request, HttpServletResponse response) throws Exception { exposeModelAsRequestAttributes ( model, request) ;
exposeHelpers ( request) ;
String dispatcherPath = prepareForRendering ( request, response) ; RequestDispatcher rd = getRequestDispatcher ( request, dispatcherPath) ; if ( rd == null ) { throw new ServletException ( "Could not get RequestDispatcher for [" + getUrl ( ) + "]: Check that the corresponding file exists within your web application archive!" ) ; } if ( useInclude ( request, response) ) { response. setContentType ( getContentType ( ) ) ; if ( logger. isDebugEnabled ( ) ) { logger. debug ( "Including [" + getUrl ( ) + "]" ) ; } rd. include ( request, response) ; } else { if ( logger. isDebugEnabled ( ) ) { logger. debug ( "Forwarding to [" + getUrl ( ) + "]" ) ; } rd. forward ( request, response) ; } }
}
至此,可以说基本上mvc的源码说明完毕,那么mybatis,spring,springmvc的源码一游到此结束
当然,如果某些源码说明是错误的,那么你可以选择忽略,但是基本都是正确的(在以后深入研究时,通常会更正),其中某些地方或者某些方法会多次的给出,因为是比较重要的(大多是为了一个过程顺序,而非类里面方法中的顺序,这里基本都是按照类方法顺序的,所以有时候是后面的方法先看,具体可以在上一章博客中,即spring源码的说明中可以看到,即107章博客(记得看前缀Java的哦,后面和前面的博客就不说明了)),在最开始已经处理了mvc的基本流程,且都给出了,后面的是一个验证(在此期间可能会修改某些注释,但是意思基本相同,如上面的默认视图名称那里),当然,并不是所有的方法都会深入的,只是一个大概,如前面的return getModelAndView(mavContainer, modelFactory, webRequest);,这些了解即可(看看注释即可,这些说明也同样适用于mybatis的源码,以及spring的源码)
我们还需要进行将他们一起的处理
操作ssm整合(当然了,具体关键的需要的依赖的源码就不说了,本质上是一起操作的,即虽然整合可能需要其他依赖,但是该依赖是一起处理的,这里就不说明这个依赖的源码了,具体可以自行了解,总不能将所有依赖的源码都说明吧,而且也要注意:一个大框架的编写通常是多人开发,而非一个(很少而已),所以我们也通常只需要使用,而非完全的知道所有原理(个人开发的可以去搞一搞,当然,如果可以,多人的也行,只是不建议)):
SSM = Spring + SpringMVC + Mybatis = (Spring + Mybatis)+ SpringMVC
先整合 Spring + Mybatis,然后再整合 SpringMVC
Mybatis整合Spring:
整合目标:
数据库连接池以及事务管理都交给Spring容器来完成
SqlSessionFactory对象应该放到Spring容器中作为单例对象管理
Mapper动态代理对象交给Spring管理,我们从Spring容器中直接获得Mapper的代理对象
当然,这里在69章博客就已经说明了,只是一个复习而已
我们创建一个maven项目,如下:
对应的依赖:
<?xml version="1.0" encoding="UTF-8"?>
< 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> org.example</ groupId> < artifactId> mvc</ artifactId> < version> 1.0-SNAPSHOT</ version> < properties> < maven.compiler.source> 11</ maven.compiler.source> < maven.compiler.target> 11</ maven.compiler.target> </ properties> < packaging> war</ packaging> < dependencies> < dependency> < groupId> junit</ groupId> < artifactId> junit</ artifactId> < version> 4.12</ version> < scope> test</ scope> </ dependency> < dependency> < groupId> org.springframework</ groupId> < artifactId> spring-test</ artifactId> < version> 5.1.5.RELEASE</ version> </ dependency> < dependency> < groupId> org.mybatis</ groupId> < artifactId> mybatis</ artifactId> < version> 3.4.5</ version> </ dependency> < dependency> < groupId> org.springframework</ groupId> < artifactId> spring-context</ artifactId> < version> 5.1.12.RELEASE</ version> </ dependency> < dependency> < groupId> org.springframework</ groupId> < artifactId> spring-jdbc</ artifactId> < version> 5.1.12.RELEASE</ version> </ dependency> < dependency> < groupId> org.springframework</ groupId> < artifactId> spring-tx</ artifactId> < version> 5.1.12.RELEASE</ version> </ dependency> < dependency> < groupId> org.aspectj</ groupId> < artifactId> aspectjweaver</ artifactId> < version> 1.8.9</ version> </ dependency> < dependency> < groupId> org.mybatis</ groupId> < artifactId> mybatis-spring</ artifactId> < version> 2.0.3</ version> </ dependency> < dependency> < groupId> mysql</ groupId> < artifactId> mysql-connector-java</ artifactId> < version> 5.1.46</ version> </ dependency> < dependency> < groupId> com.alibaba</ groupId> < artifactId> druid</ artifactId> < version> 1.1.21</ version> </ dependency> </ dependencies>
</ project>
有时候一个版本的依赖,其对应的里面的依赖(如子依赖),通常版本也会与他相同,但是有些不会(基本不是同源的不会),如:
< dependency> < groupId> com.alibaba</ groupId> < artifactId> dubbo</ artifactId> < version> 2.5.7</ version> </ dependency>
在maven中,进去看看就行了这样的了解即可,只是说明一下版本并不是绝对相同而已,如图:
的确有相同的和不同的(通常同源的相同,具体是否有例子,可能需要百度查看,反正是人为的)
创建jdbc.properties文件:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/bank?characterEncoding=utf8&useSSL=false
jdbc.username=root
jdbc.password=123456
创建数据库:
CREATE DATABASE bank CHARACTER SET utf8;
USE bank;
CREATE TABLE test(
id INT ( 2 ) ,
NAME VARCHAR ( 10 )
) ;
INSERT INTO test VALUE ( 1 , '张三' ) ;
INSERT INTO test VALUE ( 2 , '李四' ) ;
INSERT INTO test VALUE ( 3 , '王五' ) ;
INSERT INTO test VALUE ( 4 , '赵六' ) ;
创建applicationContext-dao.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
< beans xmlns = " http://www.springframework.org/schema/beans" xmlns: context= " http://www.springframework.org/schema/context" xmlns: xsi= " http://www.w3.org/2001/XMLSchema-instance" xsi: schemaLocation= " http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd
" > < context: component-scan base-package = " com.mapper" /> < context: property-placeholder location = " classpath:jdbc.properties" /> < bean id = " dataSource" class = " com.alibaba.druid.pool.DruidDataSource" > < property name = " driverClassName" value = " ${jdbc.driver}" /> < property name = " url" value = " ${jdbc.url}" /> < property name = " username" value = " ${jdbc.username}" /> < property name = " password" value = " ${jdbc.password}" /> </ bean> < bean id = " sqlSessionFactory" class = " org.mybatis.spring.SqlSessionFactoryBean" > < property name = " typeAliasesPackage" value = " com.pojo" /> < property name = " dataSource" ref = " dataSource" /> </ bean> < bean class = " org.mybatis.spring.mapper.MapperScannerConfigurer" > < property name = " basePackage" value = " com.mapper" /> < property name = " sqlSessionFactoryBeanName" value = " sqlSessionFactory" /> </ bean>
</ beans>
写一个配置(只需要在测试时,读取多个即可):applicationContext-service.xml
<?xml version="1.0" encoding="UTF-8"?>
< beans xmlns = " http://www.springframework.org/schema/beans" xmlns: context= " http://www.springframework.org/schema/context" xmlns: tx= " http://www.springframework.org/schema/tx" xmlns: xsi= " http://www.w3.org/2001/XMLSchema-instance" xsi: schemaLocation= " http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd
" > < context: property-placeholderlocation = " classpath:jdbc.properties" /> < bean id = " dataSource" class = " com.alibaba.druid.pool.DruidDataSource" > < property name = " driverClassName" value = " ${jdbc.driver}" /> < property name = " url" value = " ${jdbc.url}" /> < property name = " username" value = " ${jdbc.username}" /> < property name = " password" value = " ${jdbc.password}" /> </ bean> < context: component-scan base-package = " com.service" /> < bean id = " transactionManager" class = " org.springframework.jdbc.datasource.DataSourceTransactionManager" > < property name = " dataSource" ref = " dataSource" /> </ bean> < tx: annotation-driven transaction-manager = " transactionManager" />
</ beans>
当然,通常我们建议都写在一个xml中(除非需要进行区分,要不然会有相同的代码的,如上面的数据库的配置)
编写mapper层:
创建com.mapper包,然后创建AccountMapper接口:
package com. mapper ; public interface AccountMapper { List < Account > queryAccountList ( ) throws Exception ;
}
在com包下,创建pojo包,然后创建Account类:
package com. pojo ; public class Account { int id; String name; public Account ( ) { } public Account ( int id, String name) { this . id = id; this . name = name; } @Override public String toString ( ) { return "Account{" + "id=" + id + ", name='" + name + '\'' + '}' ; } public int getId ( ) { return id; } public void setId ( int id) { this . id = id; } public String getName ( ) { return name; } public void setName ( String name) { this . name = name; }
}
然后创建文件,在资源文件夹下,创建com.mapper包(注意层级,在这个地方可能需要分开创建,因为可能不受idea或者maven的影响,一般是maven,idea可能有),然后创建AccountMapper.xml:
<! DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
< mapper namespace = " com.mapper.AccountMapper" > < select id = " queryAccountList" resultType = " com.pojo.Account" > select *from test</ select>
</ mapper>
然后在com包下创建service包,在创建AccountService接口及其实现类:
package com. service ; import com. pojo. Account ; import java. util. List ; public interface AccountService { List < Account > queryAccountList ( ) throws Exception ;
}
package com. service. impl ; import com. mapper. AccountMapper ;
import com. pojo. Account ;
import com. service. AccountService ;
import org. springframework. beans. factory. annotation. Autowired ; import java. util. List ; @Service
public class AccountServiceImpl implements AccountService { @Autowired private AccountMapper accountMapper; @Override public List < Account > queryAccountList ( ) throws Exception { return accountMapper. queryAccountList ( ) ; }
}
然后在测试文件夹的java下,创建test包,然后创建Test1类:
package test ; import com. pojo. Account ;
import com. service. AccountService ;
import org. junit. Test ;
import org. junit. runner. RunWith ;
import org. springframework. beans. factory. annotation. Autowired ;
import org. springframework. test. context. ContextConfiguration ;
import org. springframework. test. context. junit4. SpringJUnit4ClassRunner ; import java. util. List ; @RunWith ( SpringJUnit4ClassRunner . class )
@ContextConfiguration ( locations = { "classpath*:application*.xml" } )
public class Test1 { @Autowired private AccountService accountService; @Test public void testMybatisSpring ( ) throws Exception { List < Account > accounts = accountService. queryAccountList ( ) ; for ( int i = 0 ; i < accounts. size ( ) ; i++ ) { Account account = accounts. get ( i) ; System . out. println ( account) ; } }
}
启动后,若有数据,说明操作成功,至此整合完毕,现在我们来整合mvc
在pom.xml中加上如下的依赖:
< dependency> < groupId> org.springframework</ groupId> < artifactId> spring-webmvc</ artifactId> < version> 5.1.12.RELEASE</ version> </ dependency> < dependency> < groupId> javax.servlet</ groupId> < artifactId> jsp-api</ artifactId> < version> 2.0</ version> < scope> provided</ scope> </ dependency> < dependency> < groupId> javax.servlet</ groupId> < artifactId> javax.servlet-api</ artifactId> < version> 3.1.0</ version> < scope> provided</ scope> </ dependency> < dependency> < groupId> jstl</ groupId> < artifactId> jstl</ artifactId> < version> 1.2</ version> </ dependency> < dependency> < groupId> taglibs</ groupId> < artifactId> standard</ artifactId> < version> 1.1.2</ version> </ dependency> < dependency> < groupId> com.fasterxml.jackson.core</ groupId> < artifactId> jackson-core</ artifactId> < version> 2.9.0</ version> </ dependency> < dependency> < groupId> com.fasterxml.jackson.core</ groupId> < artifactId> jackson-databind</ artifactId> < version> 2.9.0</ version> </ dependency> < dependency> < groupId> com.fasterxml.jackson.core</ groupId> < artifactId> jackson-annotations</ artifactId> < version> 2.9.0</ version> </ dependency>
在资源文件夹下创建springmvc.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
< beans xmlns = " http://www.springframework.org/schema/beans" xmlns: mvc= " http://www.springframework.org/schema/mvc" xmlns: context= " http://www.springframework.org/schema/context" xmlns: xsi= " http://www.w3.org/2001/XMLSchema-instance" xsi: schemaLocation= " http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd" > < context: component-scan base-package = " com.controller" /> < mvc: annotation-driven/>
</ beans>
在com包下,创建controller包,然后创建AccountController类:
package com. controller ; import com. pojo. Account ;
import com. service. AccountService ;
import org. springframework. beans. factory. annotation. Autowired ;
import org. springframework. stereotype. Controller ;
import org. springframework. web. bind. annotation. RequestMapping ;
import org. springframework. web. bind. annotation. ResponseBody ; import javax. servlet. ServletConfig ;
import javax. servlet. ServletContext ;
import javax. servlet. http. HttpServletRequest ;
import java. util. List ; @Controller
@RequestMapping ( "/account" )
public class AccountController { @Autowired private AccountService accountService; @RequestMapping ( "/queryAll" ) @ResponseBody public List < Account > queryAll ( HttpServletRequest request) throws Exception { ServletContext context = request. getSession ( ) . getServletContext ( ) ; String displayName = context. getServletContextName ( ) ; System . out. println ( "Display Name: " + displayName) ; return accountService. queryAccountList ( ) ; }
}
然后在web.xml文件下加上这个:
<! DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" >
< web-app> < display-name> Archetype Created Web Application</ display-name> < context-param> < param-name> contextConfigLocation</ param-name> < param-value> classpath*:applicationContext*.xml</ param-value> </ context-param> < filter> < filter-name> encoding</ filter-name> < filter-class> org.springframework.web.filter.CharacterEncodingFilter</ filter-class> < init-param> < param-name> encoding</ param-name> < param-value> UTF-8</ param-value> </ init-param> < init-param> < param-name> forceEncoding</ param-name> < param-value> true</ param-value> </ init-param> </ filter> < filter-mapping> < filter-name> encoding</ filter-name> < url-pattern> /*</ url-pattern> </ filter-mapping> < listener> < listener-class> org.springframework.web.context.ContextLoaderListener</ listener-class> </ listener> < 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*:springmvc.xml</ param-value> </ init-param> < load-on-startup> 1</ load-on-startup> </ servlet> < servlet-mapping> < servlet-name> springmvc</ servlet-name> < url-pattern> /</ url-pattern> </ servlet-mapping> </ web-app>
配置tomcat,然后访问http://localhost:8080/account/queryAll,若出现数据,那么我们mvc也整合完毕
至此,全部整合完毕,当然了,乱码问题,在前面的博客中都有,并且关于:
<?xml version="1.0" encoding="UTF-8"?>
这个部分的说明,在前面的注释中可能存在(具体可以找找,其实我自己也不知道在那个注释里面的),他在某些情况下,需要与idea设置的编码一致(如文件读取的编码,这里了解即可,通常解决项目编码问题也需要到对应的idea编码中处理)
比如:
global设置是指应用于所有项目的通用设置,而 project设置是指特定项目的设置,当你在 global设置中更改编码时,它会影响所有项目,而在 project设置中更改编码只会影响当前项目,即当前项目的是GBK,下面的是配置文件的,我们可以看到默认的是对应的ISO开头的编码,在某些时候,我们需要设置这个,当然,后面的选项是操作ascii的,在某些时候也需要用到,他们都是对配置文件的编码处理,只需要编码一致,那么自然就可以解决编码问题(后面的选择ascii,通常考虑更大的跨项目,考虑国际化,虽然有时候考虑跨项目的处理,因为他的统一是更多的,即更加的底层,谁基本上都可以看懂(就如英语一样))
在pom.xml中也可以加上一些插件,操作如这样的标签:
< build> < plugins> < plugin> </ plugin> </ plugins> </ build>
这个时候,在右边对应的maven的插件选项中,会多出来一些你配置的东西,也就可以进行操作处理,这里了解即可
设计模式:
策略模式:策略模式(Strategy),就是一个问题有多种解决方案,选择其中的一种使用,这种情况下我们使用策略模式来实现灵活地选择,也能够方便地增加新的解决方案,比如做数学题,一个问题的 解法可能有多种,再比如商场的打折促销活动,打折方案也有很多种,有些商品是不参与折扣活 动要按照原价销售,有些商品打8.5折,有些打6折,有些是返现5元等
结构:
策略(Strategy): 定义所有支持算法的公共接口,Context 使用这个接口来调用某 ConcreteStrategy 定义的算法
策略实现(ConcreteStrategy) :实现了Strategy 接口的具体算法
上下文(Context): 维护一个 Strategy 对象的引用,用一个 ConcreteStrategy 对象来装配,可定义一个接口方法让 Strategy 访问它的数据
案例:
假如现在有一个商场优惠活动,有的商品原价售卖,有的商品打8.5折,有的商品打6折,有的返现 5元
我们创建项目,然后创建一个com包,在里面创建BuyGoods类:
package com ; import java. text. MessageFormat ; public class BuyGoods { private String goods; private double price; private double finalPrice; private String desc; public BuyGoods ( String goods, double price) { this . goods = goods; this . price = price; } public double calculate ( String discountType) { if ( "discount85" . equals ( discountType) ) { finalPrice = price * 0.85 ; desc = "该商品可享受8.5折优惠" ; } else if ( "discount6" . equals ( discountType) ) { finalPrice = price * 0.6 ; desc = "该商品可享受6折优惠" ; } else if ( "return5" . equals ( discountType) ) { finalPrice = price >= 5 ? price - 5 : 0 ; desc = "该商品可返现5元" ; } else { finalPrice = price; desc = "对不起,该商品不参与优惠活动" ; } System . out. println ( MessageFormat . format ( "您购买的商品为:{0},原价为: {1},{2},最终售卖价格为:{3}" , goods, price, desc, finalPrice) ) ; return finalPrice; }
}
然后在com包里面创建Test类:
package com ; public class Test { public static void main ( String [ ] args) { BuyGoods buyGoods1 = new BuyGoods ( "Java编程思想" , 99.00 ) ; buyGoods1. calculate ( "discount85" ) ; BuyGoods buyGoods2 = new BuyGoods ( "罗技⿏标" , 66 ) ; buyGoods2. calculate ( "discount6" ) ; BuyGoods buyGoods3 = new BuyGoods ( "苹果笔记本" , 15000.00 ) ; buyGoods3. calculate ( "return5" ) ; BuyGoods buyGoods4 = new BuyGoods ( "佳能相机" , 1900 ) ; buyGoods4. calculate ( null ) ; }
}
上述代码可以解决问题,但是从代码设计的角度还是存在一些问题
增加或者修改打折方案时必须修改 BuyGoods 类源代码,违反了面向对象设计的 “开闭原则”,代码的灵活性和扩展性较差
打折方案代码聚合在一起,如果其他项目需要重用某个打折方案的代码,只能复制粘贴对应代码,无法以类组件的方式进行重用,代码的复用性差
BuyGoods 类的 calculate() 方法随着优惠方案的增多会非常庞大,代码中会出现很多if分支,可维护性差
这个时候,我们可以使用策略模式了:
在com包下,创建AbstractDiscount类,他是所有打折方案的父类:
package com ; public abstract class AbstractDiscount { protected double finalPrice; protected String desc; public AbstractDiscount ( String desc) { this . desc = desc; } public abstract double discount ( double price) ; public double getFinalPrice ( ) { return finalPrice; } public void setFinalPrice ( double finalPrice) { this . finalPrice = finalPrice; } public String getDesc ( ) { return desc; } public void setDesc ( String desc) { this . desc = desc; } }
策略(Strategy): 定义所有支持算法的公共接口,Context 使用这个接口来调用某 ConcreteStrategy 定义的算法
上面也就是策略(Strategy)
创建四种打折方案的类:
package com ; public class Discount85 extends AbstractDiscount { public Discount85 ( ) { super ( "该商品可享受8.5折优惠" ) ; } @Override public double discount ( double price) { finalPrice = price * 0.85 ; return finalPrice; }
}
package com ; public class Discount6 extends AbstractDiscount { public Discount6 ( ) { super ( "该商品可享受6折优惠" ) ; } @Override public double discount ( double price) { finalPrice = price * 0.6 ; return finalPrice; }
}
package com ; public class Return5 extends AbstractDiscount { public Return5 ( ) { super ( "该商品可返现5元" ) ; } @Override public double discount ( double price) { this . finalPrice = price >= 5 ? price - 5 : 0 ; return finalPrice; }
}
package com ; public class NoDiscount extends AbstractDiscount { public NoDiscount ( ) { super ( "对不起,该商品不参与优惠活动" ) ; } @Override public double discount ( double price) { finalPrice = price; return finalPrice; }
}
策略实现(ConcreteStrategy) :实现了Strategy 接口的具体算法,也就是上面的操作
修改对应的BuyGoods类:
package com ; import java. text. MessageFormat ; public class BuyGoods { private String goods; private double price; private AbstractDiscount abstractDiscount; public BuyGoods ( String goods, double price, AbstractDiscount abstractDiscount) { this . goods = goods; this . price = price; this . abstractDiscount = abstractDiscount; } public double calculate ( ) { double finalPrice = abstractDiscount. discount ( this . price) ; String desc = abstractDiscount. getDesc ( ) ; System . out. println ( MessageFormat . format ( "商品:{0},原价:{1},{2},最 终价格为:{3}" , goods, price, desc, finalPrice) ) ; return finalPrice; }
}
上下文(Context): 维护一个 Strategy 对象的引用,用一个 ConcreteStrategy 对象来装配,可定义一个接口方法让 Strategy 访问它的数据,就是上面的操作
进行测试:
package com ; public class Test { public static void main ( String [ ] args) { BuyGoods buyGoods1 = new BuyGoods ( "Java编程思想" , 99.00 , new Discount85 ( ) ) ; buyGoods1. calculate ( ) ; BuyGoods buyGoods2 = new BuyGoods ( "罗技⿏标" , 66 , new Discount6 ( ) ) ; buyGoods2. calculate ( ) ; BuyGoods buyGoods3 = new BuyGoods ( "苹果笔记本" , 15000.00 , new Return5 ( ) ) ; buyGoods3. calculate ( ) ; BuyGoods buyGoods4 = new BuyGoods ( "佳能相机" , 1900 , new NoDiscount ( ) ) ; buyGoods4. calculate ( ) ; }
}
这样,我们以后,只需要补充类即可,但是你是否会发现,他是用空间换时间或者维护的,基本上所有的设计模式,都会使用一点空间来交换的,这基本是必然的,因为原来不使用设计模式的处理是绝对使用最少的类或者接口等等的操作的,所以使用设计模式基本必然会多用一点空间,但是这点空间交换的便利性足够可以忽略空间所带来的影响(所以建议使用设计模式,但是有些时候,因为空间,可能并不会使用,而且这个空间,在一定程度上,会导致不好维护(比如:太多的类,不好看的情况))
空间分成两种:一种是内存和硬盘,一种是显示,通常来说,我们说明的基本都是内存和硬盘,但是在设计模式中,两种都说明
当然,在mybatis,spring,springmvc中都使用了很多设计模式,他们基本都会使用工厂模式,具体在那里就不给出了,因为太多了
模板方法模式:
模板方法模式是指定义一个算法的骨架,并允许子类为一个或者多个步骤提供实现,模板方法模式使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤,属于行为型设计模式
采用模板方法模式的核心思路是处理某个流程的代码已经具备,但其中某些节点的代码暂时不能确定,此时可以使用模板方法
案例:
在com包下创建moban包,然后创建Interview类(类可以称为抽象类和接口的):
package com. moban ; public abstract class Interview { private final void register ( ) { System . out. println ( "⾯试登记" ) ; } protected abstract void communicate ( ) ; private final void notifyResult ( ) { System . out. println ( "HR⼩姐姐通知⾯试结果" ) ; } protected final void process ( ) { this . register ( ) ; this . communicate ( ) ; this . notifyResult ( ) ; }
}
然后继续创建如下的类:
package com. moban ; public class Interviewee1 extends Interview { public void communicate ( ) { System . out. println ( "我是⾯试⼈员1,来⾯试Java⼯程师,我们聊的是Java相关内容" ) ; }
}
package com. moban ; public class Interviewee2 extends Interview { public void communicate ( ) { System . out. println ( "我是⾯试⼈员2,来⾯试前端工程师,我们聊的是前端相关内容" ) ; }
}
创建InterviewTest类:
package com. moban ; public class InterviewTest { public static void main ( String [ ] args) { Interview interviewee1 = new Interviewee1 ( ) ; interviewee1. process ( ) ; Interview interviewee2 = new Interviewee2 ( ) ; interviewee2. process ( ) ; }
}
调用子类的版本,这样就是模板方法模式,当然,可以将protected abstract void communicate();修改成如下,也是调用子类的(考虑默认的执行顺序,也就相当于完成了修改其中一个步骤了):
protected void communicate ( ) { System . out. println ( 1 ) ; }
适配器模式:
使得原本由于接口不兼容而不能一起工作、不能统一管理的那些类可以一起工作、可以进行统一管理
解决接口不兼容而不能一起工作的问题,看下面一个非常经典的案例
在中国,民用电都是220v交流电,但是手机锂电池用的都是5v直流电,因此,我们给手机充电时,就需要使用电源适配器来进行转换,使用代码还原这个生活场景
在com包下,创建shipei包,然后创建AC220类,表示220v交流电:
package com. shipei ; public class AC220 { public int outputAC220V ( ) { int output = 220 ; System . out. println ( "输出交流电" + output + "V" ) ; return output; }
}
创建DC5接口,表示5V直流电:
package com. shipei ; public interface DC5 { int outputDC5V ( ) ;
}
创建电源适配器类 PowerAdapter:
package com. shipei ; public class PowerAdapter implements DC5 { private AC220 ac220; public PowerAdapter ( AC220 ac220) { this . ac220 = ac220; } public int outputDC5V ( ) { int adapterInput = ac220. outputAC220V ( ) ; int adapterOutput = adapterInput / 44 ; System . out. println ( "使用 PowerAdapter 输⼊AC:" + adapterInput + "V 输出DC:" + adapterOutput + "V" ) ; return adapterOutput; }
}
创建测试类Test:
package com. shipei ; public class Test { public static void main ( String [ ] args) { DC5 dc5 = new PowerAdapter ( new AC220 ( ) ) ; dc5. outputDC5V ( ) ; }
}
很显然,适配器使得原来的操作可以适配,即实现了二者兼容,在前面说明源码时,这个处理:if (adapter.supports(handler)) {,其实就是判断是否支持兼容,支持就使用对应的适配器,上面的代码可以实现某种判断,可以多一个方法,就可以认为是兼容的
比如:
package com. shipei ; public class PowerAdapter implements DC5 { private AC220 ac220; public PowerAdapter ( AC220 ac220) { this . ac220 = ac220; } public PowerAdapter ( ) { } public boolean is ( Object o) { return AC220 . class . isAssignableFrom ( o. getClass ( ) ) ; } public int outputDC5V ( ) { int adapterInput = ac220. outputAC220V ( ) ; int adapterOutput = adapterInput / 44 ; System . out. println ( "使用 PowerAdapter 输⼊AC:" + adapterInput + "V 输出DC:" + adapterOutput + "V" ) ; return adapterOutput; }
}
修改测试类:
package com. shipei ; public class Test { public static void main ( String [ ] args) { PowerAdapter powerAdapter = new PowerAdapter ( ) ; AC220 ac220 = new AC220 ( ) ; boolean b = powerAdapter. is ( ac220) ; if ( b == true ) { DC5 dc5 = new PowerAdapter ( ac220) ; dc5. outputDC5V ( ) ; } }
}
这样就适配成功了,设计模式也说明完成
我们现在操作一个注解,先将前面的手写的部分拿过来(可以全局搜索"手写MVC框架之注解开发"),操作好后,我们创建一个注解:
package com. mvc. framework. annotations ; import java. lang. annotation. * ; @Documented
@Target ( { ElementType . TYPE , ElementType . METHOD } )
@Retention ( RetentionPolicy . RUNTIME )
public @interface Security { String [ ] value ( ) default { } ;
}
我们最后操作这个注解来完成一些事来结束这篇博客,完成的事情有:
该注解用于添加在Controller类或者Handler方法(或者对应的方法)上,表明哪些用户拥有访问该Handler方法的权限(注解配置用户名)
当请求url中,如存在username=zhansan时,就可以进行访问,否则不行
那么怎么处理,首先我们可以找到对RequestMapping注解的处理的地方,因为该注解放的位置与该Security基本一样,他是匹配路径的,那么我们同样的可以匹配权限,那么我们可以修改这个地方(给该类加上对应的变量):
public class Handler { private String [ ] Security ; public String [ ] getSecurity ( ) { return Security ; } public void setSecurity ( String [ ] security) { Security = security; }
}
然后回到这里:
if ( aClass. isAnnotationPresent ( RequestMapping . class ) ) { String value = aClass. getAnnotation ( RequestMapping . class ) . value ( ) ; if ( "/" . equals ( value. substring ( 0 , 1 ) ) == false ) { value = "/" + value; } baseUrl += value; } String [ ] SecurityAll = new String [ 0 ] ; if ( aClass. isAnnotationPresent ( Security . class ) ) { SecurityAll = aClass. getAnnotation ( Security . class ) . value ( ) ; }
然后再在这里进行补充:
Handler handler = new Handler ( entry. getValue ( ) , method, Pattern . compile ( url) ) ; String [ ] Secu = SecurityAll ; if ( method. isAnnotationPresent ( Security . class ) ) { String [ ] Seculin = method. getAnnotation ( Security . class ) . value ( ) ; Set < String > mergedSet = new LinkedHashSet < > ( Arrays . asList ( Secu ) ) ; mergedSet. addAll ( Arrays . asList ( Seculin ) ) ; Secu = mergedSet. toArray ( new String [ 0 ] ) ; } handler. setSecurity ( Secu ) ;
然后再这个地方进行判断:
String requestURI = req. getRequestURI ( ) ; String contextPath = req. getContextPath ( ) ; String substring = requestURI. substring ( contextPath. length ( ) , requestURI. length ( ) ) ;
Map < String , String [ ] > parameterMap = req. getParameterMap ( ) ; Set < Map. Entry < String , String [ ] >> entries = parameterMap. entrySet ( ) ; Matcher matcher = handler. getPattern ( ) . matcher ( substring) ; if ( ! matcher. matches ( ) ) { continue ; } String [ ] security = handler. getSecurity ( ) ; boolean contains = false ; for ( Map. Entry < String , String [ ] > param : entries) { if ( "username" . equals ( param. getKey ( ) ) ) { contains = Arrays . asList ( security) . contains ( param. getValue ( ) [ 0 ] ) ; } } if ( contains) { return handler; }
然后我们再DemoController类上加上@Security(“zhangsan”)注解,访问:http://localhost:8080/demo/query?name=1234&username=zhangsan,看看效果,如果可以访问,将username=zhangsan修改成username=zhang,如果访问失败,返回404,说明操作成功,可以选择在方法上加上这个注解,或者都加上来进行测试,如果都成功,那么我们操作完毕
但是呢,返回的错误信息并不好,所以我们还需要进行修改(加上如下):
public class Handler { public Handler ( ) { } private String error; public String getError ( ) { return error; } public void setError ( String error) { this . error = error; } }
然后对getHandler方法全部改变一下:
package com. mvc. framework. servlet ; import com. mvc. framework. annotations. * ;
import com. mvc. framework. config. ParameterNameExtractor ;
import com. mvc. framework. pojo. Handler ;
import com. mvc. framework. util. UtilGetClassInterfaces ;
import org. dom4j. Document ;
import org. dom4j. Element ;
import org. dom4j. io. SAXReader ; import javax. servlet. ServletConfig ;
import javax. servlet. ServletException ;
import javax. servlet. http. HttpServlet ;
import javax. servlet. http. HttpServletRequest ;
import javax. servlet. http. HttpServletResponse ;
import java. io. File ;
import java. io. IOException ;
import java. io. InputStream ;
import java. lang. reflect. Field ;
import java. lang. reflect. Method ;
import java. lang. reflect. Parameter ;
import java. net. URLDecoder ;
import java. nio. charset. StandardCharsets ;
import java. util. * ;
import java. util. regex. Matcher ;
import java. util. regex. Pattern ;
public class DispatcherServlet extends HttpServlet { private static List < String > classNames = new ArrayList < > ( ) ; private static Map < String , Object > map = new HashMap < > ( ) ; private static List < String > fieldsAlreayProcessed = new ArrayList < > ( ) ; private List < Handler > handlerMapping = new ArrayList < > ( ) ; @Override public void init ( ServletConfig config) { String contextConfigLocation = config. getInitParameter ( "contextConfigLocation" ) ; String s = doLoadconfig ( contextConfigLocation) ; doScan ( s) ; doInstance ( ) ; doAutoWired ( ) ; initHandlerMapping ( config) ; System . out. println ( "初始化完成...,等待请求与映射匹配了" ) ; } private void initHandlerMapping ( ServletConfig config) { if ( map. isEmpty ( ) ) { return ; } for ( Map. Entry < String , Object > entry : map. entrySet ( ) ) { Class < ? > aClass = entry. getValue ( ) . getClass ( ) ; if ( aClass. isAnnotationPresent ( Controller . class ) ) { String baseUrl = "" ; if ( aClass. isAnnotationPresent ( RequestMapping . class ) ) { String value = aClass. getAnnotation ( RequestMapping . class ) . value ( ) ; if ( "/" . equals ( value. substring ( 0 , 1 ) ) == false ) { value = "/" + value; } baseUrl += value; } String [ ] SecurityAll = new String [ 0 ] ; if ( aClass. isAnnotationPresent ( Security . class ) ) { SecurityAll = aClass. getAnnotation ( Security . class ) . value ( ) ; } Method [ ] methods = aClass. getMethods ( ) ; for ( int j = 0 ; j < methods. length; j++ ) { Method method = methods[ j] ; if ( method. isAnnotationPresent ( RequestMapping . class ) ) { RequestMapping annotation = method. getAnnotation ( RequestMapping . class ) ; String value = annotation. value ( ) ; if ( "/" . equals ( value. substring ( 0 , 1 ) ) == false ) { value = "/" + value; } String url = baseUrl; url += value; Handler handler = new Handler ( entry. getValue ( ) , method, Pattern . compile ( url) ) ; String [ ] Secu = SecurityAll ; if ( method. isAnnotationPresent ( Security . class ) ) { String [ ] Seculin = method. getAnnotation ( Security . class ) . value ( ) ; Set < String > mergedSet = new LinkedHashSet < > ( Arrays . asList ( Secu ) ) ; mergedSet. addAll ( Arrays . asList ( Seculin ) ) ; Secu = mergedSet. toArray ( new String [ 0 ] ) ; } handler. setSecurity ( Secu ) ; Map < String , String > prmap = null ; try { String ba = aClass. getName ( ) . replace ( '.' , '/' ) + ".class" ; prmap = ParameterNameExtractor . getParameterNames ( config. getServletContext ( ) . getRealPath ( "/" ) + "WEB-INF\\classes\\" + ba, method. getName ( ) ) ; } catch ( Exception e) { e. printStackTrace ( ) ; } Parameter [ ] parameters = method. getParameters ( ) ; for ( int i = 0 ; i < parameters. length; i++ ) { Parameter parameter = parameters[ i] ; if ( parameter. getType ( ) == HttpServletRequest . class || parameter. getType ( ) == HttpServletResponse . class ) { handler. getParamIndexMapping ( ) . put ( parameter. getType ( ) . getSimpleName ( ) , i) ; } else { handler. getParamIndexMapping ( ) . put ( prmap. get ( i) , i) ; } } handlerMapping. add ( handler) ; } } } } } private void doAutoWired ( ) { if ( map. isEmpty ( ) ) { return ; } for ( Map. Entry < String , Object > entry : map. entrySet ( ) ) { try { doObjectDependancy ( entry. getValue ( ) ) ; } catch ( Exception e) { e. printStackTrace ( ) ; } } } private static void doObjectDependancy ( Object object) { Field [ ] declaredFields = object. getClass ( ) . getDeclaredFields ( ) ; if ( declaredFields == null || declaredFields. length == 0 ) { return ; } for ( int i = 0 ; i < declaredFields. length; i++ ) { Field declaredField = declaredFields[ i] ; if ( ! declaredField. isAnnotationPresent ( Autowired . class ) ) { continue ; } if ( fieldsAlreayProcessed. contains ( object. getClass ( ) . getName ( ) + "." + declaredField. getName ( ) ) ) { continue ; } Object dependObject = null ; Autowired annotation = declaredField. getAnnotation ( Autowired . class ) ; String value = annotation. value ( ) ; if ( "" . equals ( value. trim ( ) ) ) { dependObject = map. get ( declaredField. getType ( ) . getName ( ) ) ; if ( dependObject == null ) { dependObject = map. get ( lowerFirst ( declaredField. getType ( ) . getSimpleName ( ) ) ) ; } } else { dependObject = map. get ( value + declaredField. getType ( ) . getName ( ) ) ; } fieldsAlreayProcessed. add ( object. getClass ( ) . getName ( ) + "." + declaredField. getName ( ) ) ; if ( dependObject != null ) { doObjectDependancy ( dependObject) ; } declaredField. setAccessible ( true ) ; try { declaredField. set ( object, dependObject) ; } catch ( Exception e) { e. printStackTrace ( ) ; } } } private void doInstance ( ) { if ( classNames. size ( ) == 0 ) return ; if ( classNames. size ( ) <= 0 ) return ; try { for ( int i = 0 ; i < classNames. size ( ) ; i++ ) { String className = classNames. get ( i) ; Class < ? > aClass = Class . forName ( className) ; if ( aClass. isAnnotationPresent ( Controller . class ) ) { String simpleName = aClass. getSimpleName ( ) ; String s = lowerFirst ( simpleName) ; Object o = aClass. newInstance ( ) ; map. put ( s, o) ; } if ( aClass. isAnnotationPresent ( Service . class ) ) { String beanName = aClass. getAnnotation ( Service . class ) . value ( ) ; Object o = aClass. newInstance ( ) ; int ju = 0 ; if ( "" . equals ( beanName. trim ( ) ) ) { beanName = lowerFirst ( aClass. getSimpleName ( ) ) ; } else { ju = 1 ; } if ( ju == 1 ) { UtilGetClassInterfaces . getkeyClass ( beanName, aClass, map, o) ; } else { map. put ( beanName, o) ; } Class < ? > [ ] interfaces = aClass. getInterfaces ( ) ; if ( interfaces != null && interfaces. length > 0 ) { for ( int j = 0 ; j < interfaces. length; j++ ) { Class < ? > anInterface = interfaces[ j] ; map. put ( anInterface. getName ( ) , aClass. newInstance ( ) ) ; } } } } } catch ( Exception e) { e. printStackTrace ( ) ; } } private static String lowerFirst ( String str) { char [ ] chars = str. toCharArray ( ) ; if ( 'A' <= chars[ 0 ] && chars[ 0 ] <= 'Z' ) { chars[ 0 ] += 32 ; } return String . valueOf ( chars) ; } private void doScan ( String scanPackage) { try { String scanPackagePath = Thread . currentThread ( ) . getContextClassLoader ( ) . getResource ( "" ) . getPath ( ) + scanPackage. replaceAll ( "\\." , "/" ) ; scanPackagePath = URLDecoder . decode ( scanPackagePath, StandardCharsets . UTF_8 . toString ( ) ) ; File pack = new File ( scanPackagePath) ; File [ ] files = pack. listFiles ( ) ; for ( File file : files) { if ( file. isDirectory ( ) ) { doScan ( scanPackage + "." + file. getName ( ) ) ; continue ; } if ( file. getName ( ) . endsWith ( ".class" ) ) { String className = scanPackage + "." + file. getName ( ) . replaceAll ( ".class" , "" ) ; classNames. add ( className) ; } } } catch ( Exception e) { e. printStackTrace ( ) ; } } private String doLoadconfig ( String contextConfigLocation) { InputStream resourceAsStream = DispatcherServlet . class . getClassLoader ( ) . getResourceAsStream ( contextConfigLocation) ; SAXReader saxReader = new SAXReader ( ) ; try { Document document = saxReader. read ( resourceAsStream) ; Element rootElement = document. getRootElement ( ) ; Element element = ( Element ) rootElement. selectSingleNode ( "//component-scan" ) ; String attribute = element. attributeValue ( "base-package" ) ; return attribute; } catch ( Exception e) { e. printStackTrace ( ) ; } return "" ; } @Override protected void doGet ( HttpServletRequest req, HttpServletResponse resp) throws ServletException , IOException { doPost ( req, resp) ; } @Override protected void doPost ( HttpServletRequest req, HttpServletResponse resp) throws ServletException , IOException { Handler handler = getHandler ( req) ; if ( handler == null ) { resp. getWriter ( ) . write ( "404 not found" ) ; return ; } Class < ? > [ ] parameterTypes = handler. getMethod ( ) . getParameterTypes ( ) ; Object [ ] objects = new Object [ parameterTypes. length] ; int [ ] ii = new int [ parameterTypes. length] ; Map < String , String [ ] > parameterMap = req. getParameterMap ( ) ; Set < Map. Entry < String , String [ ] >> entries = parameterMap. entrySet ( ) ; for ( Map. Entry < String , String [ ] > param : entries) { String value = "" ; for ( int i = 0 ; i < param. getValue ( ) . length; i++ ) { if ( i >= param. getValue ( ) . length - 1 ) { value = param. getValue ( ) [ i] ; continue ; } value = param. getValue ( ) [ i] + "," ; } if ( ! handler. getParamIndexMapping ( ) . containsKey ( param. getKey ( ) ) ) { continue ; } Integer integer = handler. getParamIndexMapping ( ) . get ( param. getKey ( ) ) ; if ( "String" . equals ( parameterTypes[ integer] . getSimpleName ( ) ) ) { objects[ integer] = value; } if ( "Integer" . equals ( parameterTypes[ integer] . getSimpleName ( ) ) || "int" . equals ( parameterTypes[ integer] . getSimpleName ( ) ) ) { value = value. split ( "," ) [ 0 ] ; Integer i = null ; try { i = Integer . parseInt ( value) ; } catch ( Exception e) { e. printStackTrace ( ) ; throw new RuntimeException ( "String转换Integet报错,参数名称是:" + param. getKey ( ) ) ; } objects[ integer] = i; } ii[ integer] = 1 ; } Integer integer = handler. getParamIndexMapping ( ) . get ( HttpServletRequest . class . getSimpleName ( ) ) ; objects[ integer] = req; ii[ integer] = 1 ; integer = handler. getParamIndexMapping ( ) . get ( HttpServletResponse . class . getSimpleName ( ) ) ; objects[ integer] = resp; ii[ integer] = 1 ; for ( int i = 0 ; i < ii. length; i++ ) { if ( ii[ i] == 0 ) { if ( "int" . equals ( parameterTypes[ i] . getSimpleName ( ) ) ) { objects[ i] = 0 ; } } } try { handler. getMethod ( ) . invoke ( handler. getController ( ) , objects) ; } catch ( Exception e) { e. printStackTrace ( ) ; } } private Handler getHandler ( HttpServletRequest req) { if ( handlerMapping. isEmpty ( ) ) { return null ; } String requestURI = req. getRequestURI ( ) ; String contextPath = req. getContextPath ( ) ; String substring = requestURI. substring ( contextPath. length ( ) , requestURI. length ( ) ) ; Map < String , String [ ] > parameterMap = req. getParameterMap ( ) ; Set < Map. Entry < String , String [ ] >> entries = parameterMap. entrySet ( ) ; boolean ur = false ; boolean contains = false ; String pe = "" ; for ( Handler handler : handlerMapping) { Matcher matcher = handler. getPattern ( ) . matcher ( substring) ; if ( ! matcher. matches ( ) ) { continue ; } ur = true ; String [ ] security = handler. getSecurity ( ) ; for ( Map. Entry < String , String [ ] > param : entries) { if ( "username" . equals ( param. getKey ( ) ) ) { pe = param. getValue ( ) [ 0 ] ; contains = Arrays . asList ( security) . contains ( pe) ; } } if ( contains) { return handler; } } Handler handler = new Handler ( ) ; if ( ! ur) { handler. setError ( "路径错误" ) ; return handler; } if ( ! contains) { handler. setError ( pe + "没有访问权限" ) ; return handler; } return null ; }
}
然后将这个部分进行修改:
Handler handler = getHandler ( req) ; if ( handler == null ) { resp. getWriter ( ) . write ( "404 not found" ) ; return ; } Handler handler = getHandler ( req) ; if ( handler. getError ( ) != null ) { resp. setContentType ( "text/html;charset=UTF-8" ) ; resp. getWriter ( ) . write ( handler. getError ( ) ) ; return ; }
继续测试,如果都成功,那么这篇博客到此结束