整理完之后发现放在一章太长了,于是截断了。上:主要是介绍HandlerMapping 接口、抽象实现类、以及基于URL实现业务的HandlerMapping
概述
顶级接口
处理器匹配接口,根据请求( handler )获得其的处理器( handler )和拦截器们( HandlerInterceptor 数组 )
其顶级接口是
public interface HandlerMapping {
String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler";
String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";
String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";
String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";
/**
* 获得请求对应的处理器和拦截器们
*/
@Nullable
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
类图
接口HandlerMapping的类图关系
实现类
AbstractHandlerMapping
继承 WebApplicationObjectSupport 抽象类,HandlerMapping抽象基类,实现了【获得请求对应的处理器和拦截器们】的骨架逻辑
构造方法
// AbstractHandlerMapping.java
/**
* 默认处理器
*/
@Nullable
private Object defaultHandler;
/**
* URL 路径工具类
*/
private UrlPathHelper urlPathHelper = new UrlPathHelper();
/**
* 路径匹配器
*/
private PathMatcher pathMatcher = new AntPathMatcher();
/**
* 配置的拦截器数组.
*
* 添加方式有两种:
*
* 1. {@link #setInterceptors(Object...)} 方法
* 2. {@link #extendInterceptors(List)} 方法
*/
private final List<Object> interceptors = new ArrayList<>();
/**
* 初始化后的拦截器 HandlerInterceptor 数组
*/
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();
/**
* 顺序,最低优先级
*/
private int order = Ordered.LOWEST_PRECEDENCE; // default: same as non-Ordered
/**
* Bean 名字
*/
@Nullable
private String beanName;
initApplicationContext
初始化拦截器
// AbstractHandlerMapping.java
@Override
protected void initApplicationContext() throws BeansException {
// 空方法。交给子类实现,用于注册自定义的拦截器到 interceptors 中。目前暂无子类实现。
extendInterceptors(this.interceptors);
// 扫描已注册的 MappedInterceptor 的 Bean 们,添加到 mappedInterceptors 中
detectMappedInterceptors(this.adaptedInterceptors);
// 将 interceptors 初始化成 HandlerInterceptor 类型,添加到 mappedInterceptors 中
initInterceptors();
}
// 扫描所有已经注册到容器中的MappedInterceptor类
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
// 扫描已注册的 MappedInterceptor 的 Bean 们,添加到 mappedInterceptors 中
// MappedInterceptor 会根据请求路径做匹配,是否进行拦截。
mappedInterceptors.addAll(
BeanFactoryUtils.beansOfTypeIncludingAncestors(
obtainApplicationContext(), MappedInterceptor.class, true, false).values());
}
initInterceptors
将 interceptors 初始化成 HandlerInterceptor 类型,添加到 mappedInterceptors 中
protected void initInterceptors() {
if (!this.interceptors.isEmpty()) {
// 遍历 interceptors 数组
for (int i = 0; i < this.interceptors.size(); i++) {
// 获得 interceptor 对象
Object interceptor = this.interceptors.get(i);
if (interceptor == null) {
throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
}
// 将 interceptors 初始化成 HandlerInterceptor 类型,添加到 mappedInterceptors 中
// 注意,HandlerInterceptor 无需进行路径匹配,直接拦截全部
this.adaptedInterceptors.add(adaptInterceptor(interceptor));
}
}
}
protected HandlerInterceptor adaptInterceptor(Object interceptor) {
// HandlerInterceptor 类型,直接返回
if (interceptor instanceof HandlerInterceptor) {
return (HandlerInterceptor) interceptor;
}
// WebRequestInterceptor 类型,适配成 WebRequestHandlerInterceptorAdapter 对象,然后返回
else if (interceptor instanceof WebRequestInterceptor) {
return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor);
}
// 错误类型,抛出 IllegalArgumentException 异常
else {
throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName());
}
}
getHandler
获得请求对应的hand执行链,这个请求在3.1中介绍请求过程的代码中会调用到,是handlerMapping 的核心之一
// AbstractHandlerMapping.java
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 获得处理器。该方法是抽象方法,由子类实现
Object handler = getHandlerInternal(request);
if (handler == null) {
// 获得不到,则使用默认处理器
handler = getDefaultHandler();
}
// 还是获得不到,则返回 null
if (handler == null) {
return null;
}
// Bean name or resolved handler?
// <4> 如果找到的处理器是 String 类型,则从容器中找到 String 对应的 Bean 类型作为处理器。
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
// 从上下文中获得handler
handler = obtainApplicationContext().getBean(handlerName);
}
// 获得 HandlerExecutionChain 对象
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;
}
getHandlerInternal是获得handler的方法,基类不实现,子类实现。
getHandlerExecutionChain
获得处理器执行链
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
// 创建 HandlerExecutionChain 对象
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
// 获得请求路径
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
// 遍历 adaptedInterceptors 数组,获得请求匹配的拦截器
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
// 需要匹配,若路径匹配,则添加到 chain 中
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
// 无需匹配,直接添加到 chain 中
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
MatchableHandlerMapping
基于HandlerMapping 接口的扩展。定义判断请求和指定 pattern 路径是否匹配的接口方法
public interface MatchableHandlerMapping extends HandlerMapping {
/**
* 判断请求和指定 `pattern` 路径是否匹配的接口方法
*/
@Nullable
RequestMatchResult match(HttpServletRequest request, String pattern);
}
RequestMatchResult
匹配结果的封装对象,包含了匹配值,被匹配值,和匹配器参数
public class RequestMatchResult {
/**
* 匹配上的路径
*/
private final String matchingPattern;
/**
* 被匹配的路径
*/
private final String lookupPath;
/**
* 路径匹配器
*/
private final PathMatcher pathMatcher;
public RequestMatchResult(String matchingPattern, String lookupPath, PathMatcher pathMatcher) {
Assert.hasText(matchingPattern, "'matchingPattern' is required");
Assert.hasText(lookupPath, "'lookupPath' is required");
Assert.notNull(pathMatcher, "'pathMatcher' is required");
this.matchingPattern = matchingPattern;
this.lookupPath = lookupPath;
this.pathMatcher = pathMatcher;
}
public Map<String, String> extractUriTemplateVariables() {
return this.pathMatcher.extractUriTemplateVariables(this.matchingPattern, this.lookupPath);
}
}
初始化
初始化的地方还是在DispatcherServlet没得说,方法是initHandlerMappings,一样的套路,优先判断扫描,然后从容器中获取,假如都没有则使用默认值
private void initHandlerMappings(ApplicationContext context) {
// 置空 handlerMappings
this.handlerMappings = null;
// 如果开启探测功能,则扫描已注册的 HandlerMapping 的 Bean 们,添加到 handlerMappings 中
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
// 扫描已注册的 HandlerMapping 的 Bean 们
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
// 添加到 handlerMappings 中,并进行排序
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
// 如果关闭探测功能,则获得 HANDLER_MAPPING_BEAN_NAME 对应的 Bean 对象,并设置为 handlerMappings
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
// 如果未获得到,则获得默认配置的 HandlerMapping 类
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");
}
}
}
实现类-基于 URL 进行匹配
AbstractUrlHandlerMapping
以 URL 作为 Handler 的 HandlerMapping 抽象类,提供 Handler 的获取
注意是org.springframework.web.servlet.handler下的
构造函数
// AbstractUrlHandlerMapping.java
/**
* 处理器
*/
@Nullable
private Object rootHandler;
/**
* 使用后置匹配
*/
private boolean useTrailingSlashMatch = false;
/**
* 是否延迟加载
*/
private boolean lazyInitHandlers = false;
/**
* 路径和处理器映射
*/
private final Map<String, Object> handlerMap = new LinkedHashMap<>();
registerHandler
注册多个URL的处理器
// AbstractUrlHandlerMapping.java
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
Assert.notNull(urlPaths, "URL path array must not be null");
// 循环路径数组
for (String urlPath : urlPaths) {
// 将路径和类进行注册
registerHandler(urlPath, beanName);
}
}
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
Assert.notNull(urlPath, "URL path must not be null");
Assert.notNull(handler, "Handler object must not be null");
Object resolvedHandler = handler;
// Eagerly resolve handler if referencing singleton via name.
// 如果非延迟加载,并且 handler 为 String 类型
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
ApplicationContext applicationContext = obtainApplicationContext();
// 如果是单例的就去容器中获得处理器
if (applicationContext.isSingleton(handlerName)) {
resolvedHandler = applicationContext.getBean(handlerName);
}
}
// 从已经加载的映射集合中获取url对应的处理器
Object mappedHandler = this.handlerMap.get(urlPath);
// 如果已经存在,并且和 resolvedHandler 不同,代表一个url映射了两个处理器,
// 则抛出 IllegalStateException 异常
if (mappedHandler != null) {
if (mappedHandler != resolvedHandler) {
throw new IllegalStateException(
"Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
}
}
else {
// 如果是根路径则设置根处理器RootHandler
if (urlPath.equals("/")) {
if (logger.isTraceEnabled()) {
logger.trace("Root mapping to " + getHandlerDescription(handler));
}
setRootHandler(resolvedHandler);
}
// 如果是/*则设置默认处理器
else if (urlPath.equals("/*")) {
if (logger.isTraceEnabled()) {
logger.trace("Default mapping to " + getHandlerDescription(handler));
}
setDefaultHandler(resolvedHandler);
}
// 将路径映射添加到集合中
else {
this.handlerMap.put(urlPath, resolvedHandler);
if (logger.isTraceEnabled()) {
logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));
}
}
}
}
在下面的实现可以发现,整个路径处理器的注册就是根据路径和bean名称,先通过bean名称获得bean然后将映射添加到映射集合中。
getHandlerInternal
获得处理器,根据路径去上面的集合中获得,路径匹配的处理器
// AbstractUrlHandlerMapping.java
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
// 获得请求路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// 通过路径去集合中获得处理器
Object handler = lookupHandler(lookupPath, request);
// 如果处理器为空
if (handler == null) {
// We need to care for the default handler directly, since we need to
// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
Object rawHandler = null;
// 假如是根路径获得根处理器
if ("/".equals(lookupPath)) {
rawHandler = getRootHandler();
}
// 还是空,则取默认处理器
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
// 假如还是没有定位到,这判断是否字符串
// 假如是就去容器中拿
if (rawHandler != null) {
// Bean name or resolved handler?
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(rawHandler, request);
// 创建处理器
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
return handler;
}
lookupHandler
获得处理器
// AbstractUrlHandlerMapping.java
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// Direct match?
// 直接在集合中获得处理器
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
// Bean name or resolved handler?
// 如果找到的处理器是 String 类型,则从容器中找到 String 对应的 Bean 类型作为处理器。
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}
// Pattern match?
// Pattern 正则匹配合适的,并添加到 matchingPatterns 中
List<String> matchingPatterns = new ArrayList<>();
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, urlPath)) {
matchingPatterns.add(registeredPattern);
}
else if (useTrailingSlashMatch()) {
if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
matchingPatterns.add(registeredPattern + "/");
}
}
}
String bestMatch = null;
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
// 假如匹配的结果不为空,进行排序然后获得第一个结果
if (!matchingPatterns.isEmpty()) {
matchingPatterns.sort(patternComparator);
if (logger.isTraceEnabled() && matchingPatterns.size() > 1) {
logger.trace("Matching patterns " + matchingPatterns);
}
bestMatch = matchingPatterns.get(0);
}
// 匹配到最佳路径
if (bestMatch != null) {
handler = this.handlerMap.get(bestMatch);
if (handler == null) {
if (bestMatch.endsWith("/")) {
handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
}
if (handler == null) {
throw new IllegalStateException(
"Could not find handler for best pattern match [" + bestMatch + "]");
}
}
// Bean name or resolved handler?
// 如果找到的处理器是 String 类型,则从容器中找到 String 对应的 Bean 类型作为处理器。
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);
// There might be multiple 'best patterns', let's make sure we have the correct URI template variables
// for all of them
// 获得路径参数集合
Map<String, String> uriTemplateVariables = new LinkedHashMap<>();
for (String matchingPattern : matchingPatterns) {
if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
uriTemplateVariables.putAll(decodedVars);
}
}
if (logger.isTraceEnabled() && uriTemplateVariables.size() > 0) {
logger.trace("URI variables " + uriTemplateVariables);
}
// 创建处理器
return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
}
// No handler found...
return null;
}
buildPathExposingHandler
主要给处理器注册两个拦截器PathExposingHandlerInterceptor,UriTemplateVariablesHandlerInterceptor;当前url实际匹配的Pattern、匹配条件和url模板参数设置到request的属性
// AbstractUrlHandlerMapping.java
protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
String pathWithinMapping, @Nullable Map<String, String> uriTemplateVariables) {
// 创建 HandlerExecutionChain 对象
HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
// 将路径和映射拼装成拦截器,添加HandlerExecutionChain中
chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
// 添加 UriTemplateVariablesHandlerInterceptor 拦截器,到 chain 中
if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
}
return chain;
}
match
执行匹配
// AbstractUrlHandlerMapping.java
public RequestMatchResult match(HttpServletRequest request, String pattern) {
// 获得请求路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// 假如匹配返回【匹配对象
if (getPathMatcher().match(pattern, lookupPath)) {
return new RequestMatchResult(pattern, lookupPath, getPathMatcher());
}
else if (useTrailingSlashMatch()) {
if (!pattern.endsWith("/") && getPathMatcher().match(pattern + "/", lookupPath)) {
return new RequestMatchResult(pattern + "/", lookupPath, getPathMatcher());
}
}
// 否则返回null
return null;
}
为handler注册的两个拦截器
- PathExposingHandlerInterceptor
设置url路径匹配数据
private class PathExposingHandlerInterceptor extends HandlerInterceptorAdapter {
/**
* 最佳路径
*/
private final String bestMatchingPattern;
/**
* 被匹配的路径
*/
private final String pathWithinMapping;
public PathExposingHandlerInterceptor(String bestMatchingPattern, String pathWithinMapping) {
this.bestMatchingPattern = bestMatchingPattern;
this.pathWithinMapping = pathWithinMapping;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 将最佳路径和被匹配路径设置到request中
exposePathWithinMapping(this.bestMatchingPattern, this.pathWithinMapping, request);
// 将handler设置到request
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, handler);
// 否支持类型级别映射
request.setAttribute(INTROSPECT_TYPE_LEVEL_MAPPING, supportsTypeLevelMappings());
return true;
}
}
- UriTemplateVariablesHandlerInterceptor
设置url模板参数
private class UriTemplateVariablesHandlerInterceptor extends HandlerInterceptorAdapter {
/**
* url 模板参数
*/
private final Map<String, String> uriTemplateVariables;
public UriTemplateVariablesHandlerInterceptor(Map<String, String> uriTemplateVariables) {
this.uriTemplateVariables = uriTemplateVariables;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 将模板参数设置到request中
exposeUriTemplateVariables(this.uriTemplateVariables, request);
return true;
}
}
SimpleUrlHandlerMapping
继承 AbstractUrlHandlerMapping 抽象类,UrlHandlerMapping 实现类
构造函数
// SimpleUrlHandlerMapping.java
/**
* url 和 处理器的映射
*/
private final Map<String, Object> urlMap = new LinkedHashMap<>();
initApplicationContext
进行初始化
// SimpleUrlHandlerMapping.java
public void initApplicationContext() throws BeansException {
super.initApplicationContext();
// 初始化路径的映射关系
registerHandlers(this.urlMap);
}
AbstractDetectingUrlHandlerMapping
继承 AbstractUrlHandlerMapping 抽象类,自动探测的 UrlHandlerMapping 抽象实现类
initApplicationContext
进行初始化,调用父类进行初始化,但是他注册映射关系使用的是
// AbstractDetectingUrlHandlerMapping.java
public void initApplicationContext() throws ApplicationContextException {
super.initApplicationContext();
// 自动探测
detectHandlers();
}
protected void detectHandlers() throws BeansException {
// 从容器中获得类名
ApplicationContext applicationContext = obtainApplicationContext();
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
applicationContext.getBeanNamesForType(Object.class));
// Take any bean name that we can determine URLs for.
// 遍历 Bean ,逐个注册
for (String beanName : beanNames) {
// 根据类名获得url数组
String[] urls = determineUrlsForHandler(beanName);
if (!ObjectUtils.isEmpty(urls)) {
// 逐一祖册
// URL paths found: Let's consider it a handler.
registerHandler(urls, beanName);
}
}
if ((logger.isDebugEnabled() && !getHandlerMap().isEmpty()) || logger.isTraceEnabled()) {
logger.debug("Detected " + getHandlerMap().size() + " mappings in " + formatMappingName());
}
}
然而determineUrlsForHandler并没有实现,所以需要看他的子类
BeanNameUrlHandlerMapping
基于 Bean 的名字来自动探测的 HandlerMapping 实现类
// BeanNameUrlHandlerMapping.java
public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
/**
* Checks name and aliases of the given bean for URLs, starting with "/".
*/
@Override
protected String[] determineUrlsForHandler(String beanName) {
List<String> urls = new ArrayList<>();
// 如果是以 / 开头,添加到 urls
if (beanName.startsWith("/")) {
urls.add(beanName);
}
// 获得bean的别名假如以/开头则添加到urls中
String[] aliases = obtainApplicationContext().getAliases(beanName);
for (String alias : aliases) {
if (alias.startsWith("/")) {
urls.add(alias);
}
}
return StringUtils.toStringArray(urls);
}
}
这个…………我还没想明白是什么场景使用的