2.7 어플리케이션 컨텍스트와 리소스 경로

이 섹션은 어떻게 리소스를 가진 어플리케이션을 생성하는지와 XML로 작업하는 빠른 방법을 포함하고 어떻게 와일드카드를 사용하는지, 기타 등등을 설명한다.

2.7.1 어플리케이션 컨텍스트 생성

어플리케이션 생성자(구체적 어플리케이션 컨텍스트 타입)는 컨텍스트를 정의하는 XML 파일같이 리소스의 위치 경로로서 일반적으로 문자열과 문자 배열을 취한다.

위치 경로는 prefix를 가지지 않을 때, 경로에서 만들어지고 의존성있는 빈 정의를 로드하는 구체적인 Resource 타입은 특정 어플리케이션 컨텍스트에 적절하다. 예를 들어서 아래 예제는 ClassPathXmlApplicationContext를 생성한 것이다.

ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");

ClassPathResource가 사용되기 때문에 빈 정의는 클래스패스에서 로드된다. 하지만 FileSystemXmlApplicationContext를 생성하는 예제를 고려해보자.

ApplicationContext ctx =
    new FileSystemXmlApplicationContext("conf/appContext.xml");

이제 빈 정의는 파일 시스템 위치에서 로드된다.(이 경우에 현재 작업 디렉토리와 관련이 있다.)

특정 클래스패스 prefix나 정의를 로드하기 위해 생성된 Resource의 기본 타입을 오버라이드하는 위치 경로에서 표준 URL prefix를 사용한다.

ApplicationContext ctx =
    new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");

FileSystemXmlApplicationContext는 클래스패스에서 빈 정의를 로드한다. 하지만 여전히 FileSystemXmlApplicationContext이다. 만약 그 후에 ResourceLoader를 사용한다면 prefix가 없는 경로는 여전히 파일시스템 경로로서 다뤄진다.

ClassPathXmlApplicationContext 인스턴스 생성하는 지름길

ClassPathXmlApplicationContext는 편리한 인스턴스화를 가능하게 하기 위해 생성자들을 노출한다. 기본 아이디어는 드물게 (리드하는 경로 정보 없이) 그것들 스스로 XML 파일의 파일명만을 포함하는 문자 배열을 적용할 수 있는 것이고 Class에 적용된다. ClassPathXmlApplicationContext는 그리고나서 적용된 클래스에서 경로 정보를 가져온다.

classpath의 기준은 src/main/java , src/main/resource 의 하위 자원이래.

아래 디렉토리 레이아웃을 보자.

com/
  foo/
    services.xml
    daos.xml
    MessengerService.class

아래는 service.xml과 daos.xml(클래스패스에 있는) 라는 이름의 파일에 정의된 빈들로 구성된 ClassPathXmlApplicationContext인스턴스가 인스턴스화될 수 있는지를 보여준다.

ApplicationContext ctx = new ClassPathXmlApplicationContext(
    new String[] {"services.xml", "daos.xml"}, MessengerService.class);

ClassPathXmlApplicationContext javadoc를 보자.

2.7.2 어플리케이션 컨텍스트 생성자 리소스 경로에 와일드카드

어플리케이션 컨텍스트 생성자 값에서 리소스 경로는 간단한 경로이고(앞에서 보여준것처럼), 1:1 매핑되는 타겟 Resource를 가지는 각각은 대안적으로 "classpath*:" prefix나 내부 Ant-style 정규표현식(스프링의 PathMatcher 유틸리티를 사용하여 매치하는) 을 포함한다.둘다 효과적으로 와일드카드이다.

이 매카니즘의 한가지 사용은 컴포넌트 스타일의 어플리케이션조립하는게 필요할때이다. 모든 컴포넌트들은 잘 알려진 위치 경로와 마지막 어플리케이션이 classpath*:로 prefix된 같은 경로를 사용하여 생성될 때 컨텍스트 정의 일부분을 '퍼블리시'할 수 있고, 모든 컴포넌트 부분은 자동적으로 추출된다.

/* 를 쓰면 / 아래 모든걸 자동으로 가져온다는 의미.

이 와일드 카드는 응용 프로그램 컨텍스트 생성자 (또는 PathMatcher 유틸리티 클래스 계층 구조를 직접 사용하는 경우)에서 리소스 경로를 사용하는 경우에만 해당되며 작성시 해결된다. 자원 유형 자체와는 아무런 관련이 없다. classpath * : prefix를 사용하여 실제 리소스를 구성 할 수 없다. 자원은 한 번에 하나의 자원만을 가리킨다.

Ant-style Patterns

경로 위치는 Ant-style 패턴을 사용할 수 있다.

/WEB-INF/*-context.xml
com/mycompany/**/applicationContext.xml
file:C:/some/path/*-context.xml
classpath:com/mycompany/**/applicationContext.xml

경로 위치에 Ant-style 패턴이 포함되어 있으면 리졸버는 보다 복잡한 절차를 따라 와일드 카드를 해결하려고 한다. 마지막 비 와일드 카드 세그먼트까지의 경로에 대한 자원을 생성하고 URL을 확보합니다. 이 URL이 jar : URL 또는 컨테이너 특정 변형 (예 : WebLogic의 zip : WebSphere의 wsjar 등)이 아닌 경우 java.io.File을 가져 와서 이를 사용하여 와일드 카드를 해결하는 데 사용된다. 파일 시스템. jar URL의 경우, 해석자는 java.net.JarURLConnection을 가져 오거나 수동으로 jar URL을 구문 분석 한 다음 jar 파일의 내용을 탐색하여 와일드 카드를 해결합니다

이식성에 대한 시사점

만약 구체적 경로가 이미 파일 URL(시사적으로 기본 ResourceLoader가 파일시스템 하나이거나 명시적이기 때문에)이라면 와일드카드는 완전하게 이식하여 동작하는 것을 보장한다.

만약 구체적 경로가 클래스패스 위치라면 리졸버는 마지막 non-wildcard 경로 URL을 Classloader.getResource()를 불러서 얻어야만한다. 이는 단지 경로(끝에 파일이 아니라)의 한 노드이기 때문에 사실 정의되지않은(ClassLoader javadoc에서) 정확하게 URL의 종류가 이 경우에 반환된 것이다. 예행으로, java.io.File은 항상 디렉토리(클래스패스 리소스는 파일시스템 위치에 리졸브되는)를 보여주거나 (jar 위치를 표현하는 클래스패스 리소스인) jar URL 를 보여준다. 여전히 이 동작에 이식성문제가 있다.

파일url과는 다르게 클래스패스는 파일 자체가 아니라 결국 경로 정보일 뿐이라서 오류가 있을 수 있대.

만약 jar URL이 마지막 non-wildcard 세그먼트를 가진다면, 리졸버는 와일드카드를 해결하고 jar의 내용을 탐색하기 위해 수동으로 jar URL을 파싱한 것에서 java.net.JarURLConnection을 얻을 수 있다. 이는 대부분 환경에서 동작하지만 다른것에서는 실패하기 때문에 쓰기 전에 특정 환경에 테스트를 할 것을 추천한다.

The classpath*: Prefix

XML 기반의 어플리케이션 컨텍스트를 생성할 때 위치 문자열은 특정한 classpath*: prefix를 사용할지도 모른다.

ApplicationContext ctx =
    new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");

이 특별한 prefix는 모든 주어진 이름과 매치한 클래스패스 리소스가 얻어져야만 하고(내부적으로 이는 필수적으로 ClassLoader.getResource()를 불러서 발생한다) 그리고나서 마지막 어플리케이션 정의를 머지하는 형태로 구체화한다.

와일드카드 클래스패스는 classloader 에서 getResource() 에 의존한다. 대부분 어플리케이션 서버는 요즘 자신의 클래스로더 확장체를 적용하고, 특히 jar 파일을 다룰때 행위는 다를지도 모른다. 만약 classpath* 가 클래스패스: getClass().getClassLoader().getResource("")에 jar한으로부터 파일을 부르기 위해 클래스로더를 사용하는 것이라면 간단한 테스트로 체크해봐라. 같은 이름을 가졌지만 두개의 다른 위치에 있는 파일들을 테스트해봐라. 적절하지 않은 결과가 반환된 경우에, 클래스로더 행위에 영향을 미칠 수 있는 어플리케이션 서버 세팅 문서를 체크해보자.

classpath* : 접두어를 나머지 위치 경로 (예 : classpath : META-INF / * - beans.xml)의 PathMatcher 패턴과 결합 할 수도 있다. 이 경우 해결 전략은 매우 간단하다. ClassLoader.getResources () 호출은 마지막 비 와일드 카드 경로 세그먼트에서 클래스 로더 계층의 모든 일치하는 리소스를 가져온 다음 각 리소스에서 이전에 설명한 동일한 PathMatcher 해결 전략이 와일드 카드 하위 경로에 사용된다.

pathMathcer : 경로가 일치하는지 확인하는 메소드. 매칭되는 부분을 추출하는 기능이 있다.

참조 : https://javacan.tistory.com/entry/Tip-Using-Spring-AntPathMatcher

와일드카드와 관련있는 다른 것들

Ant 스타일의 패턴과 결합 된 classpath*:는 실제 대상 파일이 파일 시스템에 있지 않는 한 패턴이 시작되기 전에 적어도 하나의 루트 디렉토리에서만 안정적으로 작동합니다. 즉, classpath : *. xml과 같은 패턴은 jar 파일의 루트에서 파일을 검색하지 않고 확장 된 디렉토리의 루트에서만 파일을 검색 할 수 있음을 의미합니다.

클래스패스 엔트리를 추출하기 위한 스프링의 기능은 빈 문자열(검색하기 위한 잠재적 루트를 가리키는) 에 파일 시스템 위치만을 반환하는 JDK의 ClassLoader.getResource() 메소드에서 시작한다. 스프링은 URLClassLoader를 런타임에, jar파일에 있는 java.class.path manifest 도 평가하지만 이는 잠재적 행위를 보장하진 않는다.

클래스 경로 패키지를 검색하려면 클래스 경로에 해당 디렉토리 항목이 있어야한다. JAR들을 Ant로 빌드할 때 JAR 태스크의 스위치에 오직 파일로 실행하지 않는다. 또한 클래스패스 디렉토리는 몇몇 환경의 시큐리티 정책에 기본하여 노출되지않을수도 있다 - 예를 들어서, JDK 1.7.0_45에 어플리캐이션 단독 사용과 더 높은 버전에서.('Trusted-Library'를 manifest에서 세팅하는 것을 요구하는 를http://stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources). 보자.) JDK 9의 모듈 패스(Jigsaw) 에서 스프링의 스캐닝하는 클래스패스는 일반적으로 기대한 바로 동작한다. 자원을 전용 디렉토리에 두는 것은 jar 파일 루트 레벨을 검색 할 때 앞서 언급 한 이식성 문제를 피하면서 여기에서도 권장 할 만하다.

classpath: 의 Ant-style 패턴은 : 리소스는 만약 탐색하기 위해 루트 패키지가 여러 클래스 경로 위치에 사용할 수 있다면 매칭하는 리소스를 찾는 것을 보장하지 않는다. 아래 리소스 위치 예제를 보자.

com/mycompany/package1/service-context.xml

이제 Ant-style 경로를 고려해보자

classpath:com/mycompany/**/service-context.xml

그런 리소스는 오직 한 위치에서만 되지만, 예측한 예제같은 경로가 해결하려고 사용될 때 리졸버는 getResource("com/mycompany"); 에서 반환된 (첫번째) URL의 동작을 끈다. 만약 기본 패키지 노드가 여러 클래스로더 위치에 존재한다면 실제 마지막 리소스는 거기에 없을수도 있다. 그러므로 그런 상황에서 같은 Ant-style 패턴을 가지고 루트 패키지를 포함하는 클래스 경로 위치를 탐색하는 classpath*: 를 사용하는것을 선호한다.

2.7.3 FileSystemResource 주의사항

FileSystemApplicationContext에 첨부되지 않은 FileSystemResource (즉, FileSystemApplicationContext가 실제 ResourceLoader가 아닌 경우)는 예상대로 절대 경로와 상대 경로를 처리한다. 상대경로는 절대경로가 파일시스템의 루트에 관련있는 반면 현재 작업 디렉토리와 관련있다.

하지만 이전 버전과의 호환성(역사적) 이유를 위해 FilesystemApplicationContext는 ResourceLoader일 때 변화했다. FileSystemApplicationContext는 모든 첨부된 FileSystemResource 인스턴스를 모든 상대적 경로를 다루기 위해 앞에 '/'로 시작하거나 아니거나로 강화했다. 임의로 이는 아래 예제가 동일하다.

ApplicationContext ctx =
    new FileSystemXmlApplicationContext("conf/context.xml");
ApplicationContext ctx =
    new FileSystemXmlApplicationContext("/conf/context.xml");

다음 예제는 동등하다 (상대적인 경우와 다른 절대적인 경우가 서로 다르기 때문에 의미가 있다.).

FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("some/resource/path/myTemplate.txt");
FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("/some/resource/path/myTemplate.txt");

임의로, 만약 진짜 절대 파일시스템 경로가 필요하다면 FileSystemResource나 FileSystemXmlApplicationContext를 사용하는것을 피해야만 하고 file: URL prefix를 사용해서 UrlResource의 사용을 강화해야 한다. 아래는 어떻게 하는지를 보여준다.

// actual context type doesn't matter, the Resource will always be UrlResource
ctx.getResource("file:///some/resource/path/myTemplate.txt");
// force this FileSystemXmlApplicationContext to load its definition via a UrlResource
ApplicationContext ctx =
    new FileSystemXmlApplicationContext("file:///conf/context.xml");

Last updated