您的位置:首页 > 编程语言 > Java开发

Spring2.5源码解读 之 基于annotation的Controller实现原理分析(1)

2011-09-15 22:04 1116 查看
网上已有许多关于Spring源码解读的文章,但对于SpringMVC中基于annotation的Controller这一块,目前还没发现有相关源码解读的文章,这几天,一直在研究SpringMVC,小有所获。这里,对Spring中基于annotation的Controller的实现原理作下简单分析,作为以后学习的参考资料,如果有人也对此感兴趣,也欢迎一起研究,交流心得。

快速开始SpringMVC

1、导入核心JAR,有两种导入方式

* 导入全部JAR:spring.jar

* 导入最小JAR:spring-core、spring-beans、spring-context、spring-web、spring-webmvc

第三方依赖JAR:commons-logging.jar

2、配置核心servlet:

Java代码



<servlet>
<servlet-name>SpringServlet</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringServlet</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>

Java代码

<servlet>
<servlet-name>SpringServlet</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringServlet</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>

* 3、配置包扫描列表

在[servlet-name]-servlet中配置:

Java代码



<context:component-scan base-package="com.spring.test" />

Java代码

<context:component-scan base-package="com.spring.test" />

将所有基于annotation的handler放在test包下即可。

相当简洁的配置,体现出了的Sping的强大、灵活,不过估计不会有人这样用Spring,呵呵

源码分析之旅:

SpringMVC的核心是DispatcherServlet,网上已经有对该类的简单分析,见后面参考资料。对于handler扫描、初始化映射关系等,以后有时间再详细解读,这里只是稍微提一下:

DispatcherServlet的初始化:

Java代码



protected void initStrategies(ApplicationContext context) {
//方法入参为ApplicationContext,可证明在DispatcherServlet初始化之前,IoC容器已经开始工作了

initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
}

Java代码

protected void initStrategies(ApplicationContext context) {
//方法入参为ApplicationContext,可证明在DispatcherServlet初始化之前,IoC容器已经开始工作了
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
}

该初始方法主要完成两件工作:

* 1、将容器中配置(在applicationContext.xml中定义)的HandlerMapping、LocaleResolver等等初始化

* 2、如果容器中未配置,则使用默认策略,该默认策略定义在DispatcherServlet.properties文件中

这其中有几个比较重要的组件(也称管道)需要初始化,包括HandlerMapping、HandlerAdapter、ViewResolver。

HandlerMapping

我们从DispatcherServlet的doService方法入手:

Java代码



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

...
//将WebApplicationContext放在了request中

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());

try {
doDispatch(request, response);
}
...
}

Java代码

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
...
//将WebApplicationContext放在了request中
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());

try {
doDispatch(request, response);
}
...
}

可以看出,对于请求的处理实际上是由doDispatch()完成的,这里只对与annotation相关的部分进行分析:

Java代码



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

...
try {
ModelAndView mv = null;
try {
//查找匹配的handler

mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null) {

//如果没有,写入404错误

noHandlerFound(processedRequest, response);
return;
}

//调用handler的方法

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
...

}

Java代码

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

...
try {
ModelAndView mv = null;
try {
//查找匹配的handler
mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
//如果没有,写入404错误
noHandlerFound(processedRequest, response);
return;
}

//调用handler的方法
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
...

}

再来看查找handler的过程:

Java代码



protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {

...
//初始时handlerMappings中有两个:

//1、BeanNameUrlHandlerMapping:根据bean的名字查找匹配的handler,意味我们可以在容器中将bean名以url定义,如"/order/*"

//2、DefaultAnnotationHandlerMapping:根据annotation定义查找

//每个handlerMapping中都维持有一个url-handler的HashMap,该列表在生成在初始化时完成

Iterator it = this.handlerMappings.iterator();
while (it.hasNext()) {
HandlerMapping hm = (HandlerMapping) it.next();
...
handler = hm.getHandler(request);//实际的匹配过程交由handlerMapping完成

...
}
return null;
}

Java代码

protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
...
//初始时handlerMappings中有两个:
//1、BeanNameUrlHandlerMapping:根据bean的名字查找匹配的handler,意味我们可以在容器中将bean名以url定义,如"/order/*"
//2、<span class="hilite1">DefaultAnnotationHandlerMapping</span>:根据annotation定义查找
//每个handlerMapping中都维持有一个url-handler的HashMap,该列表在生成在初始化时完成
Iterator it = this.handlerMappings.iterator();
while (it.hasNext()) {
HandlerMapping hm = (HandlerMapping) it.next();
...
handler = hm.getHandler(request);//实际的匹配过程交由handlerMapping完成
...
}
return null;
}

实际查找handler的过程由DefaultAnnotationHandlerMapping类完成。从它的继承层次可以看出,匹配的主要工作都由其父类完成了。在父类中定义了算法的骨架,具体的处理交由子类完成,这是Templet设计模式的典型应用。[[BR]]

先看父类AbstractHandlerMapping中定义的算法骨架:

Java代码



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) { //如果handler是String,即完整类名,在容器中定义

String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);//从IoC中直接获取

}
return getHandlerExecutionChain(handler, request);
}

Java代码

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) { //如果handler是String,即完整类名,在容器中定义
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);//从IoC中直接获取
}
return getHandlerExecutionChain(handler, request);
}

AbstractHandlerMapping的子类AbstractUrlHandlerMapping中getHandlerInternal的定义:

Java代码



protected Object getHandlerInternal(HttpServletRequest request) throws Exception {

Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
...
}
return handler;
}

protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {

// 直接匹配:

Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
validateHandler(handler, request);//@RequestMapping的其它属性匹配交由子类完成,如method和param的匹配

return buildPathExposingHandler(handler, urlPath);
}
// 正则表达式匹配:

String bestPathMatch = null;
for (Iterator it = this.handlerMap.keySet().iterator(); it.hasNext();) {

String registeredPath = (String) it.next();
if (getPathMatcher().match(registeredPath, urlPath) &&
(bestPathMatch == null || bestPathMatch.length() < registeredPath.length())) {
bestPathMatch = registeredPath;//可以看出,匹配原则是按照url更长则更匹配

}
}
if (bestPathMatch != null) {
handler = this.handlerMap.get(bestPathMatch);
validateHandler(handler, request);//@RequestMapping的其它属性匹配交由子类完成,如method和param的匹配

String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPathMatch, urlPath);
return buildPathExposingHandler(handler, pathWithinMapping);
}
// No handler found...

return null;
}

Java代码

protected Object getHandlerInternal(HttpServletRequest request) throws Exception {

Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
...
}
return handler;
}

protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// 直接匹配:
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
validateHandler(handler, request);//@RequestMapping的其它属性匹配交由子类完成,如method和param的匹配
return buildPathExposingHandler(handler, urlPath);
}
// 正则表达式匹配:
String bestPathMatch = null;
for (Iterator it = this.handlerMap.keySet().iterator(); it.hasNext();) {
String registeredPath = (String) it.next();
if (getPathMatcher().match(registeredPath, urlPath) &&
(bestPathMatch == null || bestPathMatch.length() < registeredPath.length())) {
bestPathMatch = registeredPath;//可以看出,匹配原则是按照url更长则更匹配
}
}
if (bestPathMatch != null) {
handler = this.handlerMap.get(bestPathMatch);
validateHandler(handler, request);//@RequestMapping的其它属性匹配交由子类完成,如method和param的匹配
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPathMatch, urlPath);
return buildPathExposingHandler(handler, pathWithinMapping);
}
// No handler found...
return null;
}

子类DefaultAnnotationHandlerMapping中@RequestMapping的匹配过程:

Java代码



protected void validateHandler(Object handler, HttpServletRequest request) throws Exception {

RequestMapping mapping = this.cachedMappings.get(handler.getClass());
if (mapping == null) {
mapping = AnnotationUtils.findAnnotation(handler.getClass(), RequestMapping.class);
}
if (mapping != null) {
validateMapping(mapping, request);//具体的匹配在validateMapping中完成

}
}

protected void validateMapping(RequestMapping mapping, HttpServletRequest request) throws Exception {

RequestMethod[] mappedMethods = mapping.method();

//请求方法是否匹配?

if (!ServletAnnotationMappingUtils.checkRequestMethod(mappedMethods, request)) {
String[] supportedMethods = new String[mappedMethods.length];
for (int i = 0; i < mappedMethods.length; i++) {

supportedMethods[i] = mappedMethods[i].name();
}
throw new HttpRequestMethodNotSupportedException(request.getMethod(), supportedMethods);//直接就抛异常了?似乎不妥,为什么不尝试下一个比较匹配的那个URL呢?也有可能是父类的算法定义有问题

}

//请求参数是否匹配?

String[] mappedParams = mapping.params();
if (!ServletAnnotationMappingUtils.checkParameters(mappedParams, request)) {
throw new ServletException("Parameter conditions {" +

StringUtils.arrayToDelimitedString(mappedParams, ", ") +
"} not met for request parameters: " + request.getParameterMap());
}
}

Java代码

protected void validateHandler(Object handler, HttpServletRequest request) throws Exception {
RequestMapping mapping = this.cachedMappings.get(handler.getClass());
if (mapping == null) {
mapping = AnnotationUtils.findAnnotation(handler.getClass(), RequestMapping.class);
}
if (mapping != null) {
validateMapping(mapping, request);//具体的匹配在validateMapping中完成
}
}

protected void validateMapping(RequestMapping mapping, HttpServletRequest request) throws Exception {
RequestMethod[] mappedMethods = mapping.method();

//请求方法是否匹配?
if (!ServletAnnotationMappingUtils.checkRequestMethod(mappedMethods, request)) {
String[] supportedMethods = new String[mappedMethods.length];
for (int i = 0; i < mappedMethods.length; i++) {
supportedMethods[i] = mappedMethods[i].name();
}
throw new HttpRequestMethodNotSupportedException(request.getMethod(), supportedMethods);//直接就抛异常了?似乎不妥,为什么不尝试下一个比较匹配的那个URL呢?也有可能是父类的算法定义有问题
}

//请求参数是否匹配?
String[] mappedParams = mapping.params();
if (!ServletAnnotationMappingUtils.checkParameters(mappedParams, request)) {
throw new ServletException("Parameter conditions {" +
StringUtils.arrayToDelimitedString(mappedParams, ", ") +
"} not met for request parameters: " + request.getParameterMap());
}
}

请求方法及参数的匹配过程由ServletAnnotationMappingUtils类的静态方法完成,逻辑比较简单:

Java代码



public static boolean checkRequestMethod(RequestMethod[] methods, HttpServletRequest request) {

if (!ObjectUtils.isEmpty(methods)) {
boolean match = false;
for (RequestMethod method : methods) {
if (method.name().equals(request.getMethod())) {
match = true;
}
}
if (!match) {
return false;
}
}
return true;
}

public static boolean checkParameters(String[] params, HttpServletRequest request) {

if (!ObjectUtils.isEmpty(params)) {
for (String param : params) {
int separator = param.indexOf('=');
if (separator == -1) {
if (param.startsWith("!")) {
if (WebUtils.hasSubmitParameter(request, param.substring(1))) {

return false;
}
}
else if (!WebUtils.hasSubmitParameter(request, param)) {

return false;
}
}
else {
String key = param.substring(0, separator);
String value = param.substring(separator + 1);
if (!value.equals(request.getParameter(key))) {
return false;
}
}
}
}
return true;
}

Java代码

public static boolean checkRequestMethod(RequestMethod[] methods, HttpServletRequest request) {
if (!ObjectUtils.isEmpty(methods)) {
boolean match = false;
for (RequestMethod method : methods) {
if (method.name().equals(request.getMethod())) {
match = true;
}
}
if (!match) {
return false;
}
}
return true;
}

public static boolean checkParameters(String[] params, HttpServletRequest request) {
if (!ObjectUtils.isEmpty(params)) {
for (String param : params) {
int separator = param.indexOf('=');
if (separator == -1) {
if (param.startsWith("!")) {
if (WebUtils.hasSubmitParameter(request, param.substring(1))) {
return false;
}
}
else if (!WebUtils.hasSubmitParameter(request, param)) {
return false;
}
}
else {
String key = param.substring(0, separator);
String value = param.substring(separator + 1);
if (!value.equals(request.getParameter(key))) {
return false;
}
}
}
}
return true;
}

至此,handler的匹配过程结束,bean的实例作为匹配的handler返回。可以看出,匹配过程并未深入到方法一级,如果类级别和方法级别都定义了url,在这一层次会忽略方法级别的。其实,spring也不推荐在类级别和方法级别同时定义url。[[BR]]

再回到DispatcherServlet中,找到匹配handler后,下一步就要去调用handler,调用的方式有许多,spring抽像出了一个接口HandlerAdapter,接口的定义:

Java代码



public interface HandlerAdapter {

boolean supports(Object handler); //是否支持此种类型的handler

ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; //具体的处理过程

long getLastModified(HttpServletRequest request, Object handler);
}

Java代码

public interface HandlerAdapter {

boolean supports(Object handler); //是否支持此种类型的handler

ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; //具体的处理过程

long getLastModified(HttpServletRequest request, Object handler);
}

DispatcherServlet中寻找合适的HandlerAdapter的过程:

Java代码



protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {

/*

spring默认提供四个HandlerAdapter:

HttpRequestHandlerAdapter:处理HttpRequestHandler接口的实例

SimpleControllerHandlerAdapter:处理Controller接口的实例

ThrowawayControllerHandlerAdapter:已经过时

AnnotationMethodHandlerAdapter:处理annotation定义的实例

*/
Iterator it = this.handlerAdapters.iterator();
while (it.hasNext()) {
HandlerAdapter ha = (HandlerAdapter) it.next();
if (logger.isDebugEnabled()) {
logger.debug("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
...
}

Java代码

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {

/*
spring默认提供四个HandlerAdapter:
HttpRequestHandlerAdapter:处理HttpRequestHandler接口的实例
SimpleControllerHandlerAdapter:处理Controller接口的实例
ThrowawayControllerHandlerAdapter:已经过时
AnnotationMethodHandlerAdapter:处理annotation定义的实例
*/
Iterator it = this.handlerAdapters.iterator();
while (it.hasNext()) {
HandlerAdapter ha = (HandlerAdapter) it.next();
if (logger.isDebugEnabled()) {
logger.debug("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
...
}

基于annotation的handler则是由AnnotationMethodHandlerAdapter进行处理,来看AnnotationMethodHandlerAdapter中相关处理代码:

Java代码



//是否支持此类型的handler?

public boolean supports(Object handler) {
return getMethodResolver(handler).hasHandlerMethods();
}

private ServletHandlerMethodResolver getMethodResolver(Object handler) {
Class handlerClass = ClassUtils.getUserClass(handler);
ServletHandlerMethodResolver resolver = this.methodResolverCache.get(handlerClass);
if (resolver == null) {
resolver = new ServletHandlerMethodResolver(handlerClass);//在父类的构造方法中完成handler的解析

this.methodResolverCache.put(handlerClass, resolver);//缓存起来,方法调用时有用

}
return resolver;
}

public final boolean hasHandlerMethods() {

return !this.handlerMethods.isEmpty();//非常简单的判断,如果该类中的方法标记有@RequestMapping就返回true,也意味着它支持此种类型的handler

}

Java代码

//是否支持此类型的handler?
public boolean supports(Object handler) {
return getMethodResolver(handler).hasHandlerMethods();
}

private ServletHandlerMethodResolver getMethodResolver(Object handler) {
Class handlerClass = ClassUtils.getUserClass(handler);
ServletHandlerMethodResolver resolver = this.methodResolverCache.get(handlerClass);
if (resolver == null) {
resolver = new ServletHandlerMethodResolver(handlerClass);//在父类的构造方法中完成handler的解析
this.methodResolverCache.put(handlerClass, resolver);//缓存起来,方法调用时有用
}
return resolver;
}

public final boolean hasHandlerMethods() {
return !this.handlerMethods.isEmpty();//非常简单的判断,如果该类中的方法标记有@RequestMapping就返回true,也意味着它支持此种类型的handler
}

好了,到这里,handler已确定,由谁去处理handler也已确定,剩下的工作就是如何调用了。来看具体的调用代码:

Java代码



public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {

...
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
return invokeHandlerMethod(request, response, handler);
}
}
}
return invokeHandlerMethod(request, response, handler);
}

protected ModelAndView invokeHandlerMethod(
HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

try {
ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);//缓存中已有

Method handlerMethod = methodResolver.resolveHandlerMethod(request);//确定具体该调用哪个方法

ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ExtendedModelMap implicitModel = new ExtendedModelMap();//方法入参ModelMap的原型

Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);//调用方法

ModelAndView mav =
methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);//构建ModelAndView

methodInvoker.updateModelAttributes(
handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);//处理ModelMap中的值

return mav;
}
catch (NoSuchRequestHandlingMethodException ex) {
return handleNoSuchRequestHandlingMethod(ex, request, response);
}
}

Java代码

public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {

...
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
return invokeHandlerMethod(request, response, handler);
}
}
}
return invokeHandlerMethod(request, response, handler);
}

protected ModelAndView invokeHandlerMethod(
HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

try {
ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);//缓存中已有
Method handlerMethod = methodResolver.resolveHandlerMethod(request);//确定具体该调用哪个方法
ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ExtendedModelMap implicitModel = new ExtendedModelMap();//方法入参ModelMap的原型

Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);//调用方法
ModelAndView mav =
methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);//构建ModelAndView
methodInvoker.updateModelAttributes(
handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);//处理ModelMap中的值
return mav;
}
catch (NoSuchRequestHandlingMethodException ex) {
return handleNoSuchRequestHandlingMethod(ex, request, response);
}
}

其中,确定具体调用哪个方法这个过程比较复杂,由ServletHandlerMethodResolver完成。

Java代码



public Method resolveHandlerMethod(HttpServletRequest request) throws ServletException {

...
for (Method handlerMethod : getHandlerMethods()) {
RequestMappingInfo mappingInfo = new RequestMappingInfo();
RequestMapping mapping = AnnotationUtils.findAnnotation(handlerMethod, RequestMapping.class);
mappingInfo.paths = mapping.value();
//如果类级别没有定义@RequestMapping,则使用方法级别定义的

if (!hasTypeLevelMapping() || !Arrays.equals(mapping.method(), getTypeLevelMapping().method())) {
mappingInfo.methods = mapping.method();
}
if (!hasTypeLevelMapping() || !Arrays.equals(mapping.params(), getTypeLevelMapping().params())) {
mappingInfo.params = mapping.params();
}
boolean match = false;
if (mappingInfo.paths.length > 0) {//先检查url是否匹配

for (String mappedPath : mappingInfo.paths) {
if (isPathMatch(mappedPath, lookupPath)) {
if (checkParameters(mappingInfo, request)) {
match = true;
targetPathMatches.put(mappingInfo, mappedPath);
}
else {
break;
}
}
}
}
else {
//如果没有定义url,则只需检查其它项是否匹配,如param、method
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: