자바의 표준 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.7.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처럼 순차적으로 사용했다면, 접두사를 가지지 않는 경로는 파일시스템 경로처럼 처리될것이다.
ClassPathXmlApplicationContext 는 편리한 초기화를 가능하게 해주는 많은 수의 생성자를 보여준다. 기본적인 생각은 XML파일 자체(경로 정보를 가질 필요없이)의 파일명을 포함하는 문자열 배열을 제공하고 물론 Class를 제공한다. ClassPathXmlApplicationContext는 제공되는 클래스로부터 경로 정보를 얻어낸다.
예제는 이것을 명백하게 만들어준다. 디렉토리 레이아웃을 생각해보자.
com/ foo/ services.xml daos.xml MessengerService.class
'services.xml' 과 'daos.xml' 내 정의된 bean을 구성하는 ClassPathXmlApplicationContext 인스턴스는 다음처럼 인스턴스화될것이다.
ApplicationContext ctx = new ClassPathXmlApplicationContext( new String[] {"services.xml", "daos.xml"}, MessengerService.class);
Please do consult the Javadocs for the ClassPathXmlApplicationContext class for details of the various constructors.
애플리케이션 컨텍스트 생성자내 자원경로값은 대상 자원에 대한 일대일 맵핑이 되는 간단한 경로이거나 대안으로 특별한 "classpath*:" 접두사나/또는 내부 Ant스타일의 정규표현식(Spring의 PathMatcher 유틸리티를 사용하여 일치하는)를 포함할것이다. 후자모두 효과적인 와일드카드이다.
이 기법을 사용하는 것은 컴포넌트 스타일의 애플리케이션을 조합할때이다. 모든 컴포넌트는 잘 알려진 위치경로에 컨텍스트 정의 조각을 '발행(publish)' 할수 있고 마지막 애플리케이션 컨텍스트가 classpath*:를 통해 접두사로 붙여진 같은 경로를 사용하여 생성될때, 모든 컴포넌트 조각은 자동적으로 올려질것이다.
와일드카드를 붙이는 것은 특별히 애플리케이션 컨텍스트 생성자내 자원경로를 사용하고(또는 PathMatcher 유틸리티 클래스를 직접 구조적으로 사용하는 때이다) 생성시 해석된다. Resource 타입 자체를 사용하는 것은 아무 상관이 없다. 실제 Resource를 하나의 자원에 대한 자원점으로 생성하기 위해 classpath*: 접두사를 사용하는 것은 불가능하다.
경로 위치가 Ant스타일의 패턴을 포함할때, 이를테면
/WEB-INF/*-context.xml com/mycompany/**/applicationContext.xml file:C:/some/path/*-context.xml classpath:com/mycompany/**/applicationContext.xml
... 해석자는 좀더 복잡하지만 와일드카드를 해석하고자 절차(procedure)를 정의한다. 와일드카드가 아닌 부분을 경로로 지정하고 URL을 얻어서 Resource를 만든다. 이 URL이 "jar:" URL이나 컨테이너 특유의 변수(이를테면 웹로직에서는 "zip:", 웹스피어에서는 "wsjar" 등등)가 아니라면, java.io.File이 얻어지고 파일시스템을 찾아서 와일드카드를 해석하기 위해 사용된다. jar URL의 경우, 해석자는 java.net.JarURLConnection을 얻거나 수동으로 jar URL을 파싱하고 와일드카드를 해석하기 위해 jar파일의 내용을 조사한다.
명시된 경로가 file URL(기본 ResourceLoader가 파일시스템이기 때문에 명시적이거나 절대적으로)이라면, 와일드카드는 완벽하게 이식가능한 형태로 작업하는 것을 보장한다.
명시된 경로가 클래스패스 경로라면, 해석자는 Classloader.getResource() 호출을 통해 마지막 와일드카드가 아닌 경로 조각 URL을 얻어야만 한다. 이것은 경로 노드(끝의 파일명이 아닌)이기 때문에 정렬된 URL이 반환되는 것을 실제로 정의(ClassLoader javadoc에서)하지 않았다. 사실, 이것은 언제나 디렉토리를 표현하는 java.io.File이다. 여전히 여기에는 이 작업에 대한 이식성이 고려된다.
jar URL이 마지막의 와일드카드를 가지지 않는 구문을 얻는다면, 해석자는 이것으로부터 java.net.JarURLConnection를 얻을수 있거나 jar에 내용을 알수 있도록 jar URL을 수동으로 파싱하고 와일드카드를 해석할수 있어야만 한다. 이것은 대부분의 환경에서 작동할것이지만 실패할수도 있다. 우리는 특정 환경에서 jar로 부터 자원의 와일드카드를 해석할수 있는지 테스트하길 강력히 권한다.
XML기반 애플리케이션 컨텍스트를 생성할때, 위치 문자열은 특별한 classpath*: 접두사를 사용할것이다.
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");
이 특별한 접두사는 주어진 이름으로 얻는 모든 classpath자원을 명시(내부적으로, 이것은 ClassLoader.getResources(...)호출을 통해 본질적으로 발생한다.)하고 마지막 애플리케이션 컨텍스트 정의를 형성하기 위해 병합된다.
![]() | Classpath*: 이식성 |
---|---|
와일드카드 클래스패스는 참조하는 클래스로더의 getResources() 메소드에 의존한다. 대부분의 애플리케이션 서버는 자체적인 클래스로더 구현물을 제공한다. 행위는 jar파일을 다룰때 특히 다를것이다. classpath* 가 작동하는지를 테스트 하기 위한 간단한 테스트는 클래스패스내 jar파일로부터 파일을 로드하기 위해 클래스로더를 사용(getClass().getClassLoader().getResources("<someFileInsideTheJar>"))하는 것이다. 같은 이름을 가지지만 다른 위치에 있는 파일로 이것을 테스트해보라. 적절하지 않은 경우에 결과는 반환된다. 클래스로더 행위에 영향을 끼치는 셋팅을 위해 애플리케이션 서버 문서를 체크하라. |
"classpath*:" 접두사는 예를 들어 "classpath*:META-INF/*-beans.xml"와 같이 위치경로의 나머지에서 PathMatcher 패턴으로 조합될수 있다. 이 경우, 해석전략은 지극히 단순하다. ClassLoader.getResources()호출이 클래스 로더 구조에서 자원을 일치시키는 모든것을 얻기 위해 마지막 와일드카드가 아닌 경로조각에서 사용된다.
Ant스타일로 조합되었을때 실제 대상 파일이 파일시스템에 존재하지 않더라도 "classpath*:"가 패턴이 시작되기 전 적어도 하나의 root디렉토리로 확실하게 작동할것이다. 이것은 "classpath*:*.xml"과 같은 패턴이 jar파일의 가장 상위에서가 아닌 확장된 디렉토리의 가장 상위에서 파일을 가져올 것을 의미한다. JDK ClassLoader.getResources() 메소드내 제한은 오직 전달된 공백 문자열(검색을 위한 잠재적인 가장 상위를 표시하는)을 위한 파일시스템 위치를 반환한다.
"classpath:"자원을 가진 Ant스타일의 패턴은 검색을 위한 가장 상위 패키지가 여러개의 클래스패스 위치에서 사용가능하다면 일치하는 자원을 찾기 위해 보장되지 않는다. 이것은 자원이 다음과 같기 때문이다.
com/mycompany/package1/service-context.xml
하나의 위치가 될것이다. 하지만 경로가 다음과 같을때
classpath:com/mycompany/**/service-context.xml
는 이것을 해석하도록 시도한다. 해석자는 getResource("com/mycompany")에 의해 반환되는 (첫번째) URL을 없앨것이다. 이 기본 패키지 노드가 여러개의 클래스로더 위치에 존재한다면, 실제 마지막 자원은 아래에 놓이게 되지 않을것이다. 그러므로, 가장 상위 패키지에 포함된 모든 클래스 패스 경로를 검색할 이런 경우 같은 Ant스타일 패턴을 가지고 "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");