자바의 표준 java.net.URL인터페이스와 다양한 URL접두사를 위한 표준 핸들러는 불행하게도 하위-레벨 자원에 모두 접근하기 위해 충분하지 않다. classpath나 ServletContext에 상대적인 위치로부터 얻을 필요가 있는 자원에 접근하기 위해 사용될 표준 URL 구현물이 없다. 특정 URL접두사(http:와 같은 접두사를 위해 존재하는 핸들러와 유사한)를 위한 새로운 핸들러를 등록하는 것이 가능한 반면에, 이것은 대개 처리하기 어렵다. 그리고 URL 인터페이스는 여전히 가리키는 자원의 존재여부를 체크하기 위한 메소드와 같은 몇몇 필요한 함수가 빈약하다.
Spring의 Resource인터페이스는 하위-레벨 자원에 대한 추상 접근을 위해 좀더 많은 능력을 가지는 인터페이스이다.
public interface Resource extends InputStreamSource {
boolean exists();
boolean isOpen();
URL getURL() throws IOException;
File getFile() throws IOException;
Resource createRelative(String relativePath) throws IOException;
String getFilename();
String getDescription();
}public interface InputStreamSource {
InputStream getInputStream() throws IOException;
}Resource 인터페이스로부터 몇몇 매우 중요한 메소드는:
getInputStream(): 자원의 위치를 정하고 연다. 자원을 읽기 위한 InputStream을 반환한다. 각각의 호출은 refresh된 InputStream을 반환한다. 스트림을 닫기 위한 호출자의 책임을 가진다.
exists(): 자원이 물리적인 형태로 존재하는지에 대한 boolean값 표시를 반환.
isOpen(): 이 자원이 열린 스트림을 다루는지에 대한 boolean값 표시를 반환. 만약 true라면, InputStream은 여러번 읽을수 없고 반드시 한번 읽고 난후 자원 부족을 피하기 위해 닫아야만 한다. InputStreamResource예외를 가지고 평소의 자원 구현을 위해서는 false로 두라.
getDescription(): 자원을 이용해서 작업할때 에러출력을 위해 사용되기 위한 상세설명을 반환한다. 이것은 종종 전체경로의 파일명이나 실질적인 URL이다.
밑바닥에 깔린 구현물은 호환가능하고 이 함수들을 지원한다면 다른 메소드는 당신에게 자원을 표현하는 실질적인 자원의 URL이나 File객체를 얻도록 허용한다.
Resource 추상화는 자원이 필요할때 많은 메소드 시그너처내 인자타입처럼 Spring자체에 광범위하게 사용된다. 몇몇 Spring API(다양한 ApplicationContext구현물의 생성자처럼)내 다른 메소드들은 context구현물에 적절한 Resource를 생성하기 위해 사용되는 있는 그대로이거나(unadorned) 간단한 형태인 String을 가지거나 String경로의 특별한 접두사를 통해, 생성되고 사용되어야하는 특별한 Resource구현물을 명시하기 위한 호출자를 허용한다.
당신의 코드가 Spring의 다른 부분에 대해 알지않고 다루지 않을때 조차도 자원에 접근하기 위해, Resource는 많은 Spring과 사용되고 Spring에 의해 사용된다. 이것은 당신 자신의 코드에 의해 일반적인 유틸리티 클래스처럼 사용되기 위해 매우 유용하다. 이것이 당신의 코드를 Spring에 커플링하는 동안, 이것은 URL을 위해 더 많은 능력을 가진 대체물처럼 제공되고 이 목적을 위해 당신이 사용할 다른 라이브러리에 동일할수 있는 유틸리티 클래스의 작은 세트를 위해 이것을 커플링한다.
이것은 Resource가 기능을 대체하지 않는다는 것을 아는것이 중요하다. 예를들어, UrlResource는 URL을 포장하고 포장된 URL을 사용한다.
Spring외부에서 일관적으로 제공되는 많은 수의 내장된 Resource구현물이 있다.
UrlResource는 java.net.URL을 포장한다. 그리고 파일, HTTP target, FTP target 등등과 같은 URL을 통해 대개 접근가능한 객체에 접근하기 위해 사용된다. 모든 URL은 적절히 표준화된 접두사가 하나의 URL타입대 다른것을 표시하기 위해 사용되는것처럼 표준적인 String표현을 가진다. 이것은 파일시스템 경로에 접근하기 위한 file:, HTTP프로토콜을 통해 자원에 접근하기 위한 http:, FTP를 통해 자원에 접근하기 위한 ftp: 등을 포함한다.
UrlResource는 UrlResource생성자를 사용하여 명시적으로 자바코드에 의해 생성된다. 하지만 당신이 경로를 표현하는 String인자를 가지는 API메소드를 호출할수 있을때 무조건적으로 생성될것이다. 후자의 경우를 위해, JavaBeans PropertyEditor는 생성하기 위한 Resource의 타입을 결정할것이다. 만약 경로 문자열이 classpath:와 같이 잘 알려진 접두사를 몇개 포함한다면, 그 접두사를 위한 적절히 특별한 Resource를 생성할것이다. 어쨌든, 접두사를 인지하지 않는다면, 이것은 표준 URL문자이고 UrlResource를 생성할것이라는 것을 가정할것이다.
클래스는 classpath로부터 얻어지는 자원을 표현한다. 이것은 주어진 클래스로더이거나 로딩하는 자원을 위해 주어진 클래스인 쓰레드 컨텍스트 클래스 로더를 사용한다.
이 Resource의 구현물은 클래스 경로 자원이 파일시스템내 위치하지만, jar내 위치하는 classpath자원을 위한 것이 아니고 파일시스템에 확장(서블릿 엔진에 의하거나 환경이 무엇이듯)되지 않았다면 java.io.File처럼 분석을 지원한다. 이것은 java.net.URL처럼 언제나 분석을 지원한다.
ClassPathResource는 ClassPathResource 생성자를 사용하여 명시적으로 자바 코드에 의해 생성된다. 하지만 경로를 표현하는 String 인자를 가지는 API메소드를 호출할때 무조건적으로 생성될것이다. 후자의 경우를 위해, JavaBeans PropertyEditor는 문자열 경로에서 특별한 접두사 classpath:를 인지하고 이 경우 ClassPathResource를 생성한다.
이것은 웹 애플리케이션 root디렉토리내 상대적인 경로를 해석하는 ServletContext자원을 위한 Resource 구현물이다.
이것은 스트림접근과 URL접근을 언제나 지원하지만, 웹 애플리케이션 저장고(archive)가 확장되고 자원이 파일시스템에서 물리적일때만 java.io.File접근을 허용한다. 이것처럼 확장되거나 파일시스템에 있는지, JAR나 DB와 같은 것으로부터 직접 접근되는지는 Servlet컨테이너에 실질적으로 의존한다.
주어진 InputStream을 위한 Resource 구현물이다.이것은 적용가능한 특정 Resource구현물이 없을 경우에만 사용된다. 특별히, 가능하다면 ByteArrayResource나 파일-기반 Resource구현물을 선호하라.
다른 Resource구현물과 반대로, 이것은 이미 열려있는 자원을 위한 설명자(descriptor)이다. 그러므로 isOpen()에서 "true"를 반환한다. 만약 당신이 어딘가에 자원 설명자를 유지할 필요가 있거나 스트림을 여러번 읽을 필요가 있다면 이것을 사용하지 말라.
ResourceLoader 인터페이스는 Resources를 반환할수 있는 객체에 의해 구현된다.
public interface ResourceLoader {
Resource getResource(String location);
}모든 애플리케이션 컨텍스트는 ResourceLoader를 구현한다. 그러므로 모든 애플리케이션 컨텍스트는 Resource를 얻기 위해 사용된다.
당신이 특정 애플리케이션 컨텍스트에서 getResource()를 호출하고 명시된 위치경로가 특정 접두사를 가지지 않을때, 당신은 특정 애플리케이션 컨텍스트에 적절한 Resource타입으로 돌아갈것이다. 이를테면 당신이 ClassPathXmlApplicationContext를 요청할때가 그렇다.
Resource template = ctx.getResource("some/resource/path/myTemplate.txt");반환되는 것은 ClassPathResource가 될것이다. 만약 같은 메소드가 FileSystemXmlApplicationContext인스턴스에 대해 수행된다면, 당신은 FileSystemResource를 돌려받게 될것이다. WebApplicationContext를 위해서, 당신은 ServletContextResource를 돌려받게 될것이다.
그 자체로, 당신은 특정 애플리케이션 컨텍스트를 위해 적절한 형태로 자원을 로드할수있다.
반면에, 당신은 애플리케이션 컨텍스트 타입에 상관없이 특별한 classpath: 접두사를 명시하여 ClassPathResource를 사용하도록 강요할지도 모른다.
Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");표준 java.net.URL 접두사를 명시하여 UrlResource를 사용하도록 강요한다.
Resource template = ctx.getResource("file:/some/resource/path/myTemplate.txt");Resource template = ctx.getResource("http://myhost.com/resource/path/myTemplate.txt");다음의 테이블은 String을 Resource로 변환하기 위한 전략을 보여준다.
Table 4.1. 자원(Resource) 문자열
| 접두사 | 예제 | 상세설명 |
|---|---|---|
classpath: | classpath:com/myapp/config.xml | classpath로 부터 로드 |
file: | file:/data/config.xml | 파일시스템으로부터 URL처럼 로드 [a] |
http: | http://myserver/logo.png | URL처럼 로드 |
(none) | /data/config.xml | 기초적인 ApplicationContext에 달려있다. |
[a] 다음(Section 4.6.3, “FileSystemResource 경고(caveats)”)에서 다른 사항을 보라. | ||
ResourceLoaderAware 인터페이스는 ResourceLoader와 제공되는 객체를 위한 특별한 표시자 인터페이스이다.
public interface ResourceLoaderAware {
void setResourceLoader(ResourceLoader resourceLoader);
}클래스가 ResourceLoaderAware를 구현하고 애플리케이션 컨텍스트로 배치했을때(Spring관리 bean처럼), 이것은 애플리케이션 컨텍스트에 의해 ResourceLoaderAware로 인지된다. 애플리케이션 컨텍스트는 인자로 자체를 제공하는 setResourceLoader(ResourceLoader)를 호출할것이다(기억하라. Spring내 모든 애플리케이션 컨텍스트는 ResourceLoader 인터페이스를 구현한다는 것을 기억하라).
물론, ApplicationContext이 ResourceLoader이기 때문에, bean은 ApplicationContextAware를 구현할수 있고 자원을 로드하기 위해 직접 컨텍스트로 제공된것을 사용한다. 하지만 대개, 그 모든것이 필요하다면 특별한 ResourceLoader인터페이스를 사용하는게 더 좋다. 코드는 전체 Spring ApplicationContext가 아니고 유틸리티 인터페이스일수 있는 자원로딩 인터페이스에 커플링될것이다.
bean자체가 동적인 처리의 몇가지 정렬을 통해 자원 경로를 판단하고 제공할려고 한다면, 이것은 아마도 bean이 자원을 로드하기 위한 ResourceLoader인터페이스를 사용하도록 하는것이다. 몇몇 정렬된 템플릿을 로드하는 예제처럼, 어느 특정 템플릿이 사용자의 역활에 의존할 필요가 있다고 생각해보자. 만약 자원이 정적이라면, 이것은 ResourceLoader인터페이스의 사용을 완전히 제거하고 필요한 Resource프라퍼티를 드러낸다. 그리고 그것들은 삽입될것이다.
이러한 프라퍼티로 삽입하기 위해 해야하는 것은 모든 애플리케이션 컨텍스트를 등록하고 String경로를 Resource객체로 변환할수 있는 특별한 JavaBeans PropertyEditor를 사용하는 것이다. 그래서 myBean이 Resource타입의 템플릿 프라퍼티를 가진다면, 이것은 다음처럼 자원을 위한 간단한 문자열로 설정될수 있다.
bean id="myBean" class="..."> <property name="template" value="some/resource/path/myTemplate.txt" /> </bean>
자원 경로가 어떤 접두사를 가지지 않아서, 애플리케이션 컨텍스트 자체는 ResourceLoader처럼 사용될것이고, 자원 자체는 컨텍스트 타입에 적절히 의존하는 ClassPathResource, FileSystemResource, ServletContextResource등등을 통해 로드된다.
만약 특별한 Resource타입이 사용되도록 해야할 필요가 있다면, 접두사가 사용될것이다. 다음의 두가지 예제는 ClassPathResurce와 UrlResource(파일시스템 파일에 접근하기 위해 사용된다.)가 사용되도록 하는 방법을 보여준다.
<property name="template" value="classpath:some/resource/path/myTemplate.txt"/>
<property name="template" value="file:/some/resource/path/myTemplate.txt"/>
애플리케이션 컨텍스트 생성자(특별한 애플리케이션 컨텍스트 타입을 위한)는 대개 컨텍스트의 정의를 만드는 XML파일과 같은 자원의 위치 경로로 문자열이나 문자열의 배열을 가진다.
그러한 위치가 접두사를 가지지 않을때, 특정 Resource타입은 그 경로로부터 빌드되고 관계되고 특정 애플리케이션 컨텍스트에 적절한 정의를 로드하기 위해 사용된다. 예를 들어, 당신이 다음처럼 ClassPathXmlApplicationContext를 생성한다면:
ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");ClassPathResource가 사용될것처럼 정의는 classpath로부터 로드될것이다. 하지만 당신이 다음처럼 FilleSystemXmlApplicationContext를 생성한다면:
ApplicationContext ctx =
new FileSystemClassPathXmlApplicationContext("conf/appContext.xml");현재 작업중인 디렉토리에 상대적으로 파일시스템 위치로부터 정의가 로드될것이다.
위치 경로에서 특정 classpath접두사나 표준 URL접두사를 사용하는 것은 정의를 로드하기 위해 생성된 Resource 디폴트 타입을 오버라이드할것이다. 그래서 이 FileSystemXmlApplicationContext는
ApplicationContext ctx =
new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");는 classpath로부터 실질적으로 정의를 로드할것이다. 어쨌든, 이것은 여전히 FileSystemXmlApplicationContext이다. 만약 ResourceLoader처럼 순차적으로 사용했다면, 접두사를 가지지 않는 경로는 파일시스템 경로처럼 처리될것이다.
XML기반 애플리케이션 컨텍스트를 생성할때, 위치 문자열은 특별한 classpath*: 접두사를 사용할것이다.
ApplicationContext ctx =
new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");이 특별한 접두사는 주어진 이름으로 얻는 모든 classpath자원을 명시(내부적으로, 이것은 ClassLoader.getResources(...)호출을 통해 본질적으로 발생한다.)하고 마지막 애플리케이션 컨텍스트 정의를 형성하기 위해 병합된다.
이 기법을 위한 사용은 컴포넌트-스타일의 애플리케이션을 조립할때이다. 모든 컴포넌트는 컨텍스트 정의 조각을 잘 알려진 위치 경로로 '발표(publish)' 할수 있다. 그리고 마지막 애플리케이션 컨텍스트가 classpath*를 통해 같은 경로 접두사를 사용하여 생성될때, 모든 컴포넌트 조각은 자동으로 수거된다(pick up).
이 특별한 접두사는 애플리케이션 컨텍스트에 특성화되고 생성시 분석된다. Resource타입 자체로 하는 것은 없다. 실질적으로 하나의 자원을 가리키는 자원처럼 Resource를 생성하기 위한 classpath*: 접두사를 사용하는 것은 가능하지 않다.
FileSystemApplicationContext에 첨부되지 않는 FileSystemResource는 절대경로와 상대경로를 처리할것이다(FileSystemApplicationContext는 실제로 ResourceLoader가 아니다.). 상대경로는 현재 작업중인 디렉토리에 대해 상대적이다. 반면에 절대경로는 파일시스템의 root에 대해 상대적이다.
이전버전과의 호환성의 이유로, 어쨌든 이것은 FileSystemApplicationContext이 ResourceLoader일때 변경한다. FileSystemApplicationContext는 슬래쉬(/)로 시작하거나 슬래쉬(/)를 사용하지 않거나 상대적인 모든 위치 경로를 처리하기 위해 모든 첨부된 FileSystemResources 간단히 고집한다. 실제로 이것은 다음과 동일하다.
ApplicationContext ctx =
new FileSystemClassPathXmlApplicationContext("conf/context.xml");ApplicationContext ctx =
new FileSystemClassPathXmlApplicationContext("/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접두사를 사용하여 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 it's definition via a UrlResource
ApplicationContext ctx =
new FileSystemXmlApplicationContext("file:/conf/context.xml");