1. 实践场景
通常情况下。在jsp页面给<a>标签加链接,会直接给出一个具体的URL。例1:
<a href="<c:url value='/demo/demo' />">按钮1</a>
<a href="${pageContext.request.contextPath}/demo/demo">按钮2</a>
然而,SpringMVC4还提供了另外一种方式,可以通过Controller类名的大写字符+“#”+方法名,获得路径。例2:
<a href="${s:mvcUrl('DC#demo').build()}">按钮3</a>
2. 代码分析
例1是我们绝大部分人的操作。这里不再细讲。
例2是SpringMVC给我们的提供的一个方便操作。需要用到这个标签。
<%@ taglib prefix="s" uri="http://www.springframework.org/tags" %>
标签里面定义了一个方法:mvcUrl。映射到MvcUriComponentsBuilder.MethodArgumentBuilder.fromMappingName 。方法的作用很简单。首先获得Spring中的RequestMappingInfoHandlerMapping实体类,接着拿到父类的内部类AbstractHandlerMethodMapping.MappingRegistry。MappingRegistry里面有多个map,用来保存URL和Handler的对应关系。当前需要用到的是nameLookup。里面保存的是name和handlerMethos的对应关系,name的获取规则是 类名的大写字母+“#”+方法名。spring4.2之前的版本,用的是MultiValueMap,这个是一Key对多Value的Map集合。spring4.2之后,做了代码优化,使用 ConcurrentHashMap用来提高效率。
// org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry
class MappingRegistry {
private final Map<T, MappingRegistration<T>> registry = new HashMap<T, MappingRegistration<T>>();
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<T, HandlerMethod>();
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<String, T>();
private final Map<String, List<HandlerMethod>> nameLookup =
new ConcurrentHashMap<String, List<HandlerMethod>>();
private final Map<HandlerMethod, CorsConfiguration> corsLookup =
new ConcurrentHashMap<HandlerMethod, CorsConfiguration>();
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
}
使用nameLookUp解析输入的字符串,获得对应的handlerMethod,拼装handlerMethod中controller和method的requestMapping中的值然后输出。具体操作是通过MvcUriComponentsBuilder中内部类MethodArgumentBuilder的bulid()调用外部类的 fromMethodInternal 方法。详见代码:
//org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder.MethodArgumentBuilder#build
public String build() {
return fromMethodInternal(this.baseUrl, this.controllerType, this.method,
this.argumentValues).build(false).encode().toUriString();
}
//org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder#fromMethodInternal
private static UriComponentsBuilder fromMethodInternal(UriComponentsBuilder baseUrl,
Class<?> controllerType, Method method, Object... args) {
baseUrl = getBaseUrlToUse(baseUrl);
String typePath = getTypeRequestMapping(controllerType);
String methodPath = getMethodRequestMapping(method);
String path = pathMatcher.combine(typePath, methodPath);
baseUrl.path(path);
UriComponents uriComponents = applyContributors(baseUrl, method, args);
return UriComponentsBuilder.newInstance().uriComponents(uriComponents);
}
3. 优缺点分析
下面详细分析一下当前使用例2的优缺点。
优点:
1.前端页面显示路径与@RequestMapping注解解耦。可以随意更改类名和方法的@RequestMapping注解。只要保证类名和方法名不修改即可
2.springMVC4.2以后,为了提高nameMap的使用效率,将nameMap的实现方式,从MultiValueMap改成ConcurrentHashMap。使用并发响应速度大大提高。
缺点:
1. 使用不方便,需要手动写类大写和方法名
2. 响应速度慢。需要从NameMap中获得。如果项目较大,方法较多并且页面中的链接较多。会降低响应速度。
3. 需要将前端页面URL和类名的@RequestMapping解耦的地方不多,可用性不高。
综上所述,需要结合项目的实际情况,综合考虑使用。推荐使用例1的俩种方法。
SpringMVC使用版本:4.2.4
本文参考<看透SpringMVC源代码分析与实践>一书。