继续之前《Spring源码学习-容器初始化之FileSystemXmlApplicationContext(二)路径格式及解析方式(上)》的问题:
先测试分析包含通配符(?)的。
- /**
- * 测试包含通配符:*,?的路径
- * <p>D:\\workspace-home\\spring-custom\\src\\main\\resources\\spring\\ap?-context.xml</p>
- * 通过读取配置文件失败的情况,因为此时Spring不支持\\路径的通配符解析
- *
- * @author lihzh
- * @date 2012-5-5 上午10:53:53
- */
- @Test
- public void testAntStylePathFail() {
- String pathOne = "D:\\workspace-home\\spring-custom\\src\\main\\resources\\spring\\ap?-context.xml";
- ApplicationContext appContext = new FileSystemXmlApplicationContext(pathOne);
- assertNotNull(appContext);
- VeryCommonBean bean = null;
- try {
- bean = appContext.getBean(VeryCommonBean.class);
- fail("Should not find the [VeryCommonBean].");
- } catch (NoSuchBeanDefinitionException e) {
- }
- assertNull(bean);
- }
正如测试用例所写,实际是找不到该Bean的。这又是为什么?Spring不是支持通配符吗?FileSystemXmlApplicationContext的注释里也提到了通配符的情况:
- * <p>The config location defaults can be overridden via {@link #getConfigLocations},
- * Config locations can either denote concrete files like "/myfiles/context.xml"
- * or Ant-style patterns like "/myfiles/*-context.xml" (see the
- * {@link org.springframework.util.AntPathMatcher} javadoc for pattern details).
从代码中寻找答案。回到上回的else分支中,因为包含通配符,所以进入第一个子分支。
- /**
- * Find all resources that match the given location pattern via the
- * Ant-style PathMatcher. Supports resources in jar files and zip files
- * and in the file system.
- * @param locationPattern the location pattern to match
- * @return the result as Resource array
- * @throws IOException in case of I/O errors
- * @see #doFindPathMatchingJarResources
- * @see #doFindPathMatchingFileResources
- * @see org.springframework.util.PathMatcher
- */
- protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
- String rootDirPath = determineRootDir(locationPattern);
- String subPattern = locationPattern.substring(rootDirPath.length());
- Resource[] rootDirResources = getResources(rootDirPath);
- Set<Resource> result = new LinkedHashSet<Resource>(16);
- for (Resource rootDirResource : rootDirResources) {
- rootDirResource = resolveRootDirResource(rootDirResource);
- if (isJarResource(rootDirResource)) {
- result.addAll(doFindPathMatchingJarResources(rootDirResource, subPattern));
- }
- else if (rootDirResource.getURL().getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
- result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirResource, subPattern, getPathMatcher()));
- }
- else {
- result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
- }
- }
- if (logger.isDebugEnabled()) {
- logger.debug("Resolved location pattern [" + locationPattern + "] to resources " + result);
- }
- return result.toArray(new Resource[result.size()]);
- }
此方法传入的完整的没有处理的路径,从第一行开始,就开始分步处理解析传入的路径,首先是决定“根”路径: determineRootDir(locationPattern)
- /**
- * Determine the root directory for the given location.
- * <p>Used for determining the starting point for file matching,
- * resolving the root directory location to a <code>java.io.File</code>
- * and passing it into <code>retrieveMatchingFiles</code>, with the
- * remainder of the location as pattern.
- * <p>Will return "/WEB-INF/" for the pattern "/WEB-INF/*.xml",
- * for example.
- * @param location the location to check
- * @return the part of the location that denotes the root directory
- * @see #retrieveMatchingFiles
- */
- protected String determineRootDir(String location) {
- int prefixEnd = location.indexOf(":") + 1;
- int rootDirEnd = location.length();
- while (rootDirEnd > prefixEnd && getPathMatcher().isPattern(location.substring(prefixEnd, rootDirEnd))) {
- rootDirEnd = location.lastIndexOf('/', rootDirEnd - 2) + 1;
- }
- if (rootDirEnd == 0) {
- rootDirEnd = prefixEnd;
- }
- return location.substring(0, rootDirEnd);
- }
这个“根”,就是不包含通配符的最长的部分,以我们的路径为例,这个“根”本来应该是: D:\\workspace-home\\spring-custom\\src\\main\\resources\\spring\\ ,但是实际上,从determineRootDir的实现可以看出:
- /**
- * Find all resources in the file system that match the given location pattern
- * via the Ant-style PathMatcher.
- * @param rootDirResource the root directory as Resource
- * @param subPattern the sub pattern to match (below the root directory)
- * @return the Set of matching Resource instances
- * @throws IOException in case of I/O errors
- * @see #retrieveMatchingFiles
- * @see org.springframework.util.PathMatcher
- */
- protected Set<Resource> doFindPathMatchingFileResources(Resource rootDirResource, String subPattern)
- throws IOException {
- File rootDir;
- try {
- rootDir = rootDirResource.getFile().getAbsoluteFile();
- }
- catch (IOException ex) {
- if (logger.isWarnEnabled()) {
- logger.warn("Cannot search for matching files underneath " + rootDirResource +
- " because it does not correspond to a directory in the file system", ex);
- }
- return Collections.emptySet();
- }
- return doFindMatchingFileSystemResources(rootDir, subPattern);
- }
- /**
- * Retrieve files that match the given path pattern,
- * checking the given directory and its subdirectories.
- * @param rootDir the directory to start from
- * @param pattern the pattern to match against,
- * relative to the root directory
- * @return the Set of matching File instances
- * @throws IOException if directory contents could not be retrieved
- */
- protected Set<File> retrieveMatchingFiles(File rootDir, String pattern) throws IOException {
- if (!rootDir.exists()) {
- // Silently skip non-existing directories.
- if (logger.isDebugEnabled()) {
- logger.debug("Skipping [" + rootDir.getAbsolutePath() + "] because it does not exist");
- }
- return Collections.emptySet();
- }
- if (!rootDir.isDirectory()) {
- // Complain louder if it exists but is no directory.
- if (logger.isWarnEnabled()) {
- logger.warn("Skipping [" + rootDir.getAbsolutePath() + "] because it does not denote a directory");
- }
- return Collections.emptySet();
- }
- if (!rootDir.canRead()) {
- if (logger.isWarnEnabled()) {
- logger.warn("Cannot search for matching files underneath directory [" + rootDir.getAbsolutePath() +
- "] because the application is not allowed to read the directory");
- }
- return Collections.emptySet();
- }
- String fullPattern = StringUtils.replace(rootDir.getAbsolutePath(), File.separator, "/");
- if (!pattern.startsWith("/")) {
- fullPattern += "/";
- }
- fullPattern = fullPattern + StringUtils.replace(pattern, File.separator, "/");
- Set<File> result = new LinkedHashSet<File>(8);
- doRetrieveMatchingFiles(fullPattern, rootDir, result);
- return result;
- }
- /**
- * Recursively retrieve files that match the given pattern,
- * adding them to the given result list.
- * @param fullPattern the pattern to match against,
- * with prepended root directory path
- * @param dir the current directory
- * @param result the Set of matching File instances to add to
- * @throws IOException if directory contents could not be retrieved
- */
- protected void doRetrieveMatchingFiles(String fullPattern, File dir, Set<File> result) throws IOException {
- if (logger.isDebugEnabled()) {
- logger.debug("Searching directory [" + dir.getAbsolutePath() +
- "] for files matching pattern [" + fullPattern + "]");
- }
- File[] dirContents = dir.listFiles();
- if (dirContents == null) {
- if (logger.isWarnEnabled()) {
- logger.warn("Could not retrieve contents of directory [" + dir.getAbsolutePath() + "]");
- }
- return;
- }
- for (File content : dirContents) {
- String currPath = StringUtils.replace(content.getAbsolutePath(), File.separator, "/");
- if (content.isDirectory() && getPathMatcher().matchStart(fullPattern, currPath + "/")) {
- if (!content.canRead()) {
- if (logger.isDebugEnabled()) {
- logger.debug("Skipping subdirectory [" + dir.getAbsolutePath() +
- "] because the application is not allowed to read the directory");
- }
- }
- else {
- doRetrieveMatchingFiles(fullPattern, content, result);
- }
- }
- if (getPathMatcher().match(fullPattern, currPath)) {
- result.add(content);
- }
- }
- }
- /**
- * Actually match the given <code>path</code> against the given <code>pattern</code>.
- * @param pattern the pattern to match against
- * @param path the path String to test
- * @param fullMatch whether a full pattern match is required (else a pattern match
- * as far as the given base path goes is sufficient)
- * @return <code>true</code> if the supplied <code>path</code> matched, <code>false</code> if it didn't
- */
- protected boolean doMatch(String pattern, String path, boolean fullMatch,
- Map<String, String> uriTemplateVariables) {
- if (path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) {
- return false;
- }
- String[] pattDirs = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator);
- String[] pathDirs = StringUtils.tokenizeToStringArray(path, this.pathSeparator);
- int pattIdxStart = 0;
- int pattIdxEnd = pattDirs.length - 1;
- int pathIdxStart = 0;
- int pathIdxEnd = pathDirs.length - 1;
- // Match all elements up to the first **
- while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
- String patDir = pattDirs[pattIdxStart];
- if ("**".equals(patDir)) {
- break;
- }
- if (!matchStrings(patDir, pathDirs[pathIdxStart], uriTemplateVariables)) {
- return false;
- }
- pattIdxStart++;
- pathIdxStart++;
- }
- if (pathIdxStart > pathIdxEnd) {
- // Path is exhausted, only match if rest of pattern is * or **'s
- if (pattIdxStart > pattIdxEnd) {
- return (pattern.endsWith(this.pathSeparator) ? path.endsWith(this.pathSeparator) :
- !path.endsWith(this.pathSeparator));
- }
- if (!fullMatch) {
- return true;
- }
- if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") && path.endsWith(this.pathSeparator)) {
- return true;
- }
- for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
- if (!pattDirs[i].equals("**")) {
- return false;
- }
- }
- return true;
- }
- else if (pattIdxStart > pattIdxEnd) {
- // String not exhausted, but pattern is. Failure.
- return false;
- }
- else if (!fullMatch && "**".equals(pattDirs[pattIdxStart])) {
- // Path start definitely matches due to "**" part in pattern.
- return true;
- }
- // up to last '**'
- while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
- String patDir = pattDirs[pattIdxEnd];
- if (patDir.equals("**")) {
- break;
- }
- if (!matchStrings(patDir, pathDirs[pathIdxEnd], uriTemplateVariables)) {
- return false;
- }
- pattIdxEnd--;
- pathIdxEnd--;
- }
- if (pathIdxStart > pathIdxEnd) {
- // String is exhausted
- for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
- if (!pattDirs[i].equals("**")) {
- return false;
- }
- }
- return true;
- }
- while (pattIdxStart != pattIdxEnd && pathIdxStart <= pathIdxEnd) {
- int patIdxTmp = -1;
- for (int i = pattIdxStart + 1; i <= pattIdxEnd; i++) {
- if (pattDirs[i].equals("**")) {
- patIdxTmp = i;
- break;
- }
- }
- if (patIdxTmp == pattIdxStart + 1) {
- // '**/**' situation, so skip one
- pattIdxStart++;
- continue;
- }
- // Find the pattern between padIdxStart & padIdxTmp in str between
- // strIdxStart & strIdxEnd
- int patLength = (patIdxTmp - pattIdxStart - 1);
- int strLength = (pathIdxEnd - pathIdxStart + 1);
- int foundIdx = -1;
- strLoop:
- for (int i = 0; i <= strLength - patLength; i++) {
- for (int j = 0; j < patLength; j++) {
- String subPat = pattDirs[pattIdxStart + j + 1];
- String subStr = pathDirs[pathIdxStart + i + j];
- if (!matchStrings(subPat, subStr, uriTemplateVariables)) {
- continue strLoop;
- }
- }
- foundIdx = pathIdxStart + i;
- break;
- }
- if (foundIdx == -1) {
- return false;
- }
- pattIdxStart = patIdxTmp;
- pathIdxStart = foundIdx + patLength;
- }
- for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
- if (!pattDirs[i].equals("**")) {
- return false;
- }
- }
- return true;
- }
- private boolean matchStrings(String pattern, String str, Map<String, String> uriTemplateVariables) {
- AntPathStringMatcher matcher = new AntPathStringMatcher(pattern, str, uriTemplateVariables);
- return matcher.matchStrings();
- }
在构造AntPathStringMatcher实例的时候,spring果然也创建了正则:
- AntPathStringMatcher(String pattern, String str, Map<String, String> uriTemplateVariables) {
- this.str = str;
- this.uriTemplateVariables = uriTemplateVariables;
- this.pattern = createPattern(pattern);
- }
- private Pattern createPattern(String pattern) {
- StringBuilder patternBuilder = new StringBuilder();
- Matcher m = GLOB_PATTERN.matcher(pattern);
- int end = 0;
- while (m.find()) {
- patternBuilder.append(quote(pattern, end, m.start()));
- String match = m.group();
- if ("?".equals(match)) {
- patternBuilder.append('.');
- }
- else if ("*".equals(match)) {
- patternBuilder.append(".*");
- }
- else if (match.startsWith("{") && match.endsWith("}")) {
- int colonIdx = match.indexOf(':');
- if (colonIdx == -1) {
- patternBuilder.append(DEFAULT_VARIABLE_PATTERN);
- variableNames.add(m.group(1));
- }
- else {
- String variablePattern = match.substring(colonIdx + 1, match.length() - 1);
- patternBuilder.append('(');
- patternBuilder.append(variablePattern);
- patternBuilder.append(')');
- String variableName = match.substring(1, colonIdx);
- variableNames.add(variableName);
- }
- }
- end = m.end();
- }
- patternBuilder.append(quote(pattern, end, pattern.length()));
- return Pattern.compile(patternBuilder.toString());
- }
简单说,就是spring先用正则:
- private static final Pattern GLOB_PATTERN = Pattern.compile("\\?|\\*|\\{((?:\\{[^/]+?\\}|[^/{}]|\\\\[{}])+?)\\}");
博主推荐:
Java Coder技术交流高级群:91513074
推荐文章:
本文出自 “苦逼coder” 博客,谢绝转载!