Spring 소스 코드 학습 - 마스크 경로 분석 포함 (상)
경로 에 어댑터 (?, *, * *) 가 포함 되 어 있 으 면 spring 은 어떻게 처리 합 니까?classpath * 로 시작 하면 어 떨 까요?
우선 어댑터 (?) 를 포함 하 는 것 을 테스트 분석 합 니 다.
- /**
- * :*,?
- * <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);
- }
테스트 사례 에서 보 듯 이 이 빈 을 찾 을 수 없다.이 건 또 왜?Spring 은 어댑터 를 지원 하지 않 습 니까?FileSystemXmlApplication Context 의 주석 에서 도 마스크 의 상황 이 언급 되 었 습 니 다.
- * <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 의 실현 을 보면 다음 과 같다.
우선, 짝 퉁: 색인 위치, prefixEnd 에 할당 。
그 다음 에 콜론 부터 마지막 문자열 까지 마스크 가 포함 되 어 있 는 지 순환 적 으로 판단 합 니 다. 포함 되 어 있 으 면 마지막 으로 '/' 로 분 단 된 부분 을 차단 합 니 다. 예 를 들 어 우리 경로 에서 마지막 ap 입 니까? -context. xml 이 단락 입 니 다.나머지 경로 에 마스크 가 포함 되 지 않 을 때 까지 나머지 부분 을 반복 해서 판단 합 니 다.
검색 이 완료 되면 rootDirEnd = 0 이 되면 이전에 할당 한 prefixEnd 의 값 을 rootDirEnd, 즉 ":" 가 있 는 색인 위치 에 부여 합 니 다.
마지막 으로 루트 DirEnd 를 처음부터 문자열 을 끊 습 니 다.
우리 의 문 제 는 중요 한 두 번 째 단계 입 니 다. Spring 은 문자열 에서 만 "/" 를 찾 습 니 다. "\ \ \" 와 같은 경로 분할 방식 을 지원 하지 않 기 때문에 "\ \", rootDirEnd = - 1 + 1 = 0 을 찾 을 수 없습니다.그래서 순환 한 후에 단계 에서 나 오 는 경 로 는 D 입 니 다.
,자연 Spring 에서 설정 파일 을 찾 을 수 없습니다. 용 기 를 초기 화 할 수 없습니다.
상기 분석 을 바탕 으로 우 리 는 경 로 를 D: / workspace - home / spring - custom / src / main / resources / spring / ap? -context.xml,
테스트 통과.
방금 분 석 했 을 뿐 입 니 다. 우리 가 이전에 경 로 를 분석 한 문제 가 있 습 니 다. 그리고 제 생각 에 도 여러분 의 관심 이 있 는 것 같 습 니 다. 바로 마스크 가 어떻게 일치 하 는 지 하 는 것 입 니 다.그러면 우 리 는 소스 코드 를 계속 분석 하고 findPathMatchingResources 로 돌아 갑 니 다. 방법
경 로 를 어댑터 와 포함 되 지 않 는 두 부분 으로 나 누 면 Spring 은 루트 경 로 를 하나의 리 소스 로 만 들 고 getResources 방법 을 사용 합 니 다.그리고 루트 경로 의 종 류 를 확인 합 니 다. Jar 경로 입 니까?VFS 경로 입 니까?우리 의 이런 일반적인 경로 에 대해 서 는 자 연 스 럽 게 마지막 지점 까지 간다.
- /**
- * 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);
- }
- }
- }
주요 일치 하 는 작업 은 doRetrieveMatchingFiles 에서 방법 은 시작 했다.앞의 것 은 모두 간단 한 패 키 징 과도 입 니 다. retrieveMatchingFiles 에서 다음 경로 가 존재 하 는 지, 폴 더 인지, 읽 을 수 있 는 지 여 부 를 판단 합 니 다.그렇지 않 으 면 모두 빈 집합 으로 돌아간다.모두 만족 한 후에 야 들 어 왔 습 니 다. 방법이 방법 에서
우선 이 폴 더 의 모든 파일 을 보 여 줍 니 다.
그리고 모든 파일 을 옮 겨 다 니 며 폴 더 라면 doRetrieveMatchingFiles 방법 을 재 귀적 으로 호출 합 니 다.그렇지 않 으 면 getPathMatcher (). match (fullPattern, currPath) 를 호출 하여 파일 이름 의 마지막 매 칭 을 하고 조건 을 만족 시 켜 결과 집합 에 넣 습 니 다.
이 match 방법 은 실제 AntPathMatcher 의 doMatch 방법 을 호출 하 였 습 니 다.
- /**
- * 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;
- }
비교 방법 은 다음 과 같다.
우선, 입력 경로 와 비교 할 경 로 를 각각 파일 구분자 에 따라 문자열 배열 로 나 눕 니 다.(예: ("D:", "Workspace - home", "spring - custom"... 곶)
그리고 시작 과 끝 자 리 를 설정 한 후 이 두 배열 에 대해 while 순환 (코드 의 첫 번 째 while 순환) 을 진행 합 니 다. 한 단락 이 만족 하지 않 으 면 fasle 로 돌아 갑 니 다.
현재 테스트 경로 에 * * 부분 이 포함 되 어 있 지 않 기 때문에 주요 판단 은 기본적으로 첫 번 째 while 에서 이 루어 집 니 다. 이 부분 작업 은 당연히 match Strings 에서 이 루어 집 니 다.
생각해 보 세 요: 마스크 경로 에 맞 는 기능 을 완성 시 키 면 어떻게 하 시 겠 습 니까? 정규 가 자 연 스 럽 게 연상 되 었 습 니까? 좋 은 선택 인 것 같 습 니 다. spring 이 어떻게 하 는 지 보 세 요.
- 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("\\?|\\*|\\{((?:\\{[^/]+?\\}|[^/{}]|\\\\[{}])+?)\\}");
경로 에 있 는 "?" 와 "*" 마스크 를 찾 은 다음 자바 정규 임의의 문자 "." 와 ". *" 로 변환 합 니 다. 찾 은 파일 의 경로 와 일치 하 는 다른 정규 표현 식 을 생 성 합 니 다. 일치 하면 true 로 돌아 갑 니 다.
이로써 경로 에 포 함 된? 와 * 의 상황 해석 spring 의 해석 방식 에 대해 우 리 는 이미 기본적으로 알 고 있 습 니 다. * * 의 상황 을 함께 소개 하려 고 했 지만 고려 한 편폭 이 너무 길 어서 다음 에 다시 함께 연구 합 시다.
마지막 으로 모든 연 구 는 필자 가 일 한 여가 시간 에 소일 하고 잘못된 지적 은 각종 형식 과 내용 에 대한 연 구 를 환영한다 고 지적 했다.
블 로 거들 추천:
Java Coder 기술 교류 고급 군: 91513074
추천 글:
가장 일반적인 IT 남 - 코 더 잡담
본 고 는 '코 더 를 핍박 하 는' 블 로그 에서 나 온 것 으로 전 재 를 사절 합 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Java Adapter 어댑터 모드(클래스 어댑터, 객체 어댑터)의 장단점 비교어댑터 모드는 하나의 클래스의 인터페이스를 클라이언트가 기대하는 다른 인터페이스로 바꾸어 원래의 인터페이스가 일치하지 않아 함께 작업할 수 없는 두 클래스가 함께 작업할 수 있도록 하는 것이다.기능적으로 말하자면, ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.