이 장에서는 Spring이 리소스를 처리하는 방법과 Spring에서 리소스를 사용하여 작업하는 방법을 설명합니다. 여기에는 다음 주제가 포함됩니다.
Resource 인터페이스Resource 구현ResourceLoader 인터페이스ResourcePatternResolver 인터페이스ResourceLoaderAware 인터페이스불행히도 Java의 표준 java.net.URL 클래스와 다양한 URL 접두어(prefixes)에 대한 표준 핸들러는 하위 수준 리소스에 대한 모든 액세스에 충분하지 않습니다. 예를 들어, classpath나 ServletContext에 상대적인 리소스에 액세스하는 데 사용할 수 있는 표준화된 URL 구현이 없습니다. 특수한 URL 접두어에 대한 새 핸들러를 등록하는 것이 가능하지만(http:와 같은 접두어에 대한 기존 핸들러와 유사) 이는 일반적으로 매우 복잡하며 URL 인터페이스에는 가리키는 리소스의 존재를 확인하는 방법과 같은 일부 바람직한 기능이 여전히 부족합니다.
Resource Interfaceorg.springframework.core.io 패키지에 위치한 Spring의 Resource 인터페이스는 하위 수준(low-level) 리소스에 대한 액세스를 추상화하기 위한 보다 유능한(more capable) 인터페이스입니다. 다음 목록은 Resource 인터페이스의 개요를 제공합니다. 자세한 내용은 Resource javadoc을 참조하세요.
public interface Resource extends InputStreamSource {
boolean exists();
boolean isReadable();
boolean isOpen();
boolean isFile();
URL getURL() throws IOException;
URI getURI() throws IOException;
File getFile() throws IOException;
ReadableByteChannel readableChannel() throws IOException;
long contentLength() throws IOException;
long lastModified() throws IOException;
Resource createRelative(String relativePath) throws IOException;
String getFilename();
String getDescription();
}
Resource 인터페이스의 정의에서 볼 수 있듯이 이는 InputStreamSource 인터페이스를 확장합니다. 다음 목록은 InputStreamSource 인터페이스의 정의를 보여줍니다.
public interface InputStreamSource {
InputStream getInputStream() throws IOException;
}
Resource 인터페이스의 가장 중요한 메소드 중 일부는 다음과 같습니다.
getInputStream(): 리소스를 찾아서 열고, 리소스에서 읽기 위한 InputStream을 반환합니다. 각 호출은 새로운 InputStream을 반환할 것으로 예상됩니다. 스트림을 닫는 것은 caller의 책임입니다.
exists(): 이 리소스가 실제로 물리적 형태로 존재하는지 여부를 나타내는 boolean을 반환합니다.
isOpen(): 이 리소스가 개방형 스트림이 있는 핸들을 나타내는지 여부를 나타내는 boolean을 반환합니다. true인 경우, InputStream은 여러 번 읽을 수 없으며 리소스 누수를 방지하기 위해 한 번만 읽은 후 닫아야 합니다. InputStreamResource를 제외하고(with the exception of) 모든 일반적인 리소스 구현에 대해 false를 반환합니다.
getDescription(): 리소스 작업 시 오류 출력에 사용되는 이 리소스에 대한 설명을 반환합니다. 이는 정규화된 파일 이름이거나 리소스의 실제 URL인 경우가 많습니다.
다른 방법을 사용하면 리소스를 나타내는 실제 URL이나 File 객체를 얻을 수 있습니다(기본 구현이 호환 가능하고 해당 기능을 지원하는 경우).
Resource 인터페이스의 일부 구현은 쓰기를 지원하는 리소스에 대한 확장된 WritableResource 인터페이스도 구현합니다.
Spring 자체는 리소스가 필요할 때 많은 메소드 시그니처의 인수 유형으로 Resource 추상화를 광범위(extensively)하게 사용합니다. 일부 Spring API(다양한 ApplicationContext 구현에 대한 생성자와 같은)의 다른 메소드는, 해당 컨텍스트 구현에 적합한 Resource를 생성하는 데 사용되는 장식되지 않거나 간단한 형식으로 String을 사용하거나, String 경로(path)의 특수 접두사(prefix)를 통해 호출자가, 구현이 생성되고 사용되어져야만 하는 특정Resource를 지정하게 합니다.
Resource 인터페이스는 Spring과 함께 및 Spring에 의해서 많이 사용되지만, 당신의 코드가 스프링의 다른 부분을 모르거나 신경쓰지 않더라도, 리소스에 액세스하기 위해 당신의 코드에서 자체적으로 일반 유틸리티 클래스로 사용하는 것이 실제로 매우 편리합니다. 이는 코드를 Spring에 결합(couple)하지만, 실제로는 URL을 보다 효과적으로 대체할 수 있는 작은 유틸리티 클래스 세트에만 결합(couple)하며, 이 목적으로 사용할 다른 라이브러리와 동등한 것으로 간주될 수 있습니다.
[Note]
Resource추상화는 기능을 대체하지 않습니다. 가능하면 래핑(wrap)합니다. 예를 들어UrlResource는 URL을 래핑하고 래핑된URL을 사용하여 작업을 수행합니다.
Resource ImplementationsSpring에는 여러 가지 내장 Resource 구현이 포함되어 있습니다.
UrlResource
ClassPathResource
FileSystemResource
PathResource
ServletContextResource
InputStreamResource
ByteArrayResource
Spring에서 사용할 수 있는 Resource 구현의 전체 목록을 보려면 Resource javadoc의 "모든 알려진 구현 클래스" 섹션을 참조하세요.
UrlResourceUrlResource는 java.net.URL을 래핑하고 파일, HTTPS 대상(target), FTP 대상(target) 등과 같이 일반적으로 URL로 액세스할 수 있는 모든 객체에 액세스하는 데 사용할 수 있습니다. 모든 URL에는 표준화된 String 표현이 있으므로 적절한 표준화된 접두사(prefix)가 다른 URL 유형을 나타내는 데 사용됩니다. 여기에는 파일 시스템 경로에 액세스하기 위한 file:, HTTPS 프로토콜을 통해 리소스에 액세스하기 위한 https:, FTP를 통해 리소스에 액세스하기 위한 ftp: 등이 포함됩니다.
UrlResource는 UrlResource 생성자를 명시적으로 사용하여 Java 코드로 생성되지만, 경로(path)를 나타내기 위한 String 인수를 사용하는 API 메서드를 호출할 때 암시적으로 생성되는 경우가 많습니다. 후자의 경우 JavaBeans PropertyEditor는 궁극적으로 생성할 Resource 타입을 결정합니다. 경로(path) 문자열에 잘 알려진(속성 편집기(property editor)에 대한) 접두사(예: classpath:)가 포함된 경우 해당 접두사에 대해 적절하고 특수화된 Resource를 생성합니다. 그러나 접두사를 인식하지 못하는 경우 해당 문자열을 표준 URL 문자열로 가정하고 UrlResource를 생성합니다.
ClassPathResource이 클래스는 classpath에서 얻어야 하는 리소스를 나타냅니다. 이는 스레드 컨텍스트 클래스 로더, 지정된 클래스 로더 또는 리소스 로드를 위해 지정된 클래스를 사용합니다.
이 Resource 구현은 classpath 리소스가 파일 시스템에 있지만, jar에 있고 (서블릿 엔진이나 환경에 따라) 파일시스템에 확장되지 않은 classpath 리소스가 아닌 경우, java.io.File로 resolution을 지원합니다. 이 문제를 해결(address)하기 위해 다양한 Resource 구현은 항상 java.net.URL로 resolution을 지원합니다.
ClassPathResource는 ClassPathResource 생성자를 명시적으로 사용하여 Java 코드로 생성되지만, 경로(path)를 나타내기 위한 String 인수를 사용하는 API 메소드를 호출할 때 암시적으로 생성되는 경우가 많습니다. 후자의 경우 JavaBeans PropertyEditor는 문자열 경로에서 특수 접두사인 classpath:를 인식하고 이 경우 ClassPathResource를 생성합니다.
FileSystemResource이는 java.io.File 핸들에 대한 Resource 구현입니다. 또한 Spring의 표준 문자열 기반 경로 변환(path transformation)을 적용하는 java.nio.file.Path 핸들을 지원하지만 java.nio.file.Files API를 통해 모든 작업을 수행하는 합니다. 순수 java.nio.path.Path 기반 지원을 위해서는 대신 PathResource를 사용하십시오. FileSystemResource는 File 및 URL로 resolution을 지원합니다.
PathResource이는 Path API를 통해 모든 작업과 변환을 수행하는 java.nio.file.Path 핸들에 대한 Resource 구현입니다. File 및 URL로 resolution을 지원하고 확장된 WritableResource 인터페이스도 구현합니다. PathResource는 거의 FileSystemResource에 대한 순수한 java.nio.path.Path 기반 대안이며 그와 다른 createRelative 동작을 사용합니다.
ServletContextResource이는 관련 웹 애플리케이션의 루트 디렉터리 내 상대 경로를 해석하는 ServletContext 리소스에 대한 Resource 구현입니다.
항상 스트림 액세스와 URL 액세스를 지원하지만, 웹 애플리케이션 아카이브가 확장되고 리소스가 물리적으로 파일 시스템에 있는 경우에만 java.io.File 액세스를 허용합니다. 파일 시스템에서 확장되거나 JAR 또는 데이터베이스(상상 가능한 일입니다)와 같은 다른 곳에서 직접 액세스되는지 여부는 실제로 서블릿 컨테이너에 따라 다릅니다.
InputStreamResourceInputStreamResource는 주어진 InputStream에 대한 Resource 구현입니다. 특정 Resource 구현을 적용할 수 없는 경우에만 사용해야 합니다. 특히 가능한 경우 ByteArrayResource 또는 파일 기반 Resource 구현을 선호합니다.
다른 Resource 구현과 달리 이는 이미 열려 있는(already-opened) 리소스에 대한 설명자입니다. 따라서 isOpen()에서 true를 반환합니다. 리소스 설명자를 어딘가에 보관해야 하거나 스트림을 여러 번 읽어야 하는 경우에는 사용하지 마세요.
ByteArrayResource이것은 주어진 바이트 배열에 대한 Resource 구현입니다. 주어진 바이트 배열에 대해 ByteArrayInputStream을 생성합니다. 일회용(single-use) InputStreamResource를 사용하지 않고도 지정된 바이트 배열에서 콘텐츠를 로드하는 데 유용합니다.
ResourceLoader InterfaceResourceLoader 인터페이스는 Resource 인스턴스를 반환(즉, 로드)할 수 있는 객체로 구현됩니다. 다음 목록은 ResourceLoader 인터페이스 정의를 보여줍니다.
public interface ResourceLoader {
Resource getResource(String location);
ClassLoader getClassLoader();
}
모든 애플리케이션 컨텍스트는 ResourceLoader 인터페이스를 구현합니다. 따라서 모든 애플리케이션 컨텍스트를 사용하여 Resource 인스턴스를 얻을 수 있습니다.
특정 애플리케이션 컨텍스트에서 getResource()를 호출하고, 지정된 위치 경로에 특정 접두사가 없으면, 해당 특정 애플리케이션 컨텍스트에 적합한 Resource 타입을 다시 가져옵니다. 예를 들어, ClassPathXmlApplicationContext 인스턴스에 대해 다음 코드 조각이 실행되었다고 가정합니다.
Resource template = ctx.getResource("some/resource/path/myTemplate.txt");
ClassPathXmlApplicationContext에 대해 해당 코드는 ClassPathResource를 반환합니다. FileSystemXmlApplicationContext 인스턴스에 대해 동일한 메서드를 실행하면 FileSystemResource가 반환됩니다. WebApplicationContext의 경우 ServletContextResource를 반환합니다. 마찬가지로 각 컨텍스트에 적절한 객체를 반환합니다.
결과적으로 특정 애플리케이션 컨텍스트에 적합한 방식으로 리소스를 로드할 수 있습니다.
반면에 다음 예제와 같이 특수 classpath: 접두사를 지정하여 애플리케이션 컨텍스트 타입에 관계없이 ClassPathResource를 강제로 사용할 수도 있습니다.
Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");
마찬가지로 표준 java.net.URL 접두어를 지정하여 UrlResource를 강제로 사용할 수 있습니다. 다음 예에서는 file 및 https 접두사를 사용합니다.
Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt");
Resource template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt");
다음 표에는 String 객체를 Resource 객체로 변환하는 전략이 요약되어 있습니다.
| 접두사 | 예시 | 설명 |
|---|---|---|
classpath: |
|
클래스패스에서 로드됩니다. |
file: |
|
파일 시스템에서 |
https: |
|
|
(없음) |
|
기본적으로 |
ResourcePatternResolver InterfaceResourcePatternResolver 인터페이스는 위치 패턴(예: Ant 스타일 경로 패턴)을 Resource 객체로 해석하기 위한 전략을 정의하는 ResourceLoader 인터페이스의 확장입니다.
public interface ResourcePatternResolver extends ResourceLoader {
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
Resource[] getResources(String locationPattern) throws IOException;
}
위에서 볼 수 있듯이 이 인터페이스는 클래스 경로에서 일치하는 모든 리소스에 대해 특수 classpath*: 리소스 접두사도 정의합니다. 이 경우 리소스 위치는 자리 표시자가 없는 경로(예: classpath*:/config/beans.xml)여야 합니다. JAR 파일 또는 classpath의 다른 디렉토리에는 동일한 경로와 동일한 이름을 가진 여러 파일이 포함될 수 있습니다. classpath*: 리소스 접두사를 사용한 와일드카드 지원에 대한 자세한 내용은 애플리케이션 컨텍스트 생성자 리소스 경로의 와일드카드 및 해당 하위 섹션을 참조하세요.
전달된 ResourceLoader(예: ResourceLoaderAware semantics를 통해 제공되는)가 이 확장 인터페이스도 구현하는지 여부를 확인할 수 있습니다.
PathMatchingResourcePatternResolver는 ApplicationContext 외부에서 사용할 수 있는 독립 실행형 구현이며 Resource[] Bean 속성(property)을 채우기 위해 ResourceArrayPropertyEditor에서도 사용됩니다. PathMatchingResourcePatternResolver는 지정된 리소스 위치 경로를 하나 이상의 일치하는 Resource 객체로 resolve할 수 있습니다. 소스 경로는 대상 Resource에 대한 일대일 매핑을 갖는 간단한 경로일 수도 있고, 대안으로 특별한 classpath*: 접두사 또는 내부 Ant 스타일 정규 표현식(Spring의 org.springframework.util.AntPathMatcher 유틸리티를 사용하여 매칭)를 포함할 수 있습니다. 후자는 둘 다 사실상 와일드카드입니다.
[Note]
표준ApplicationContext의 기본ResourceLoader는 실제로ResourcePatternResolver인터페이스를 구현하는PathMatchingResourcePatternResolver의 인스턴스입니다.ResourcePatternResolver인터페이스를 구현하고 기본PathMatchingResourcePatternResolver에 위임하는ApplicationContext인스턴스 자체의 경우에도 마찬가지입니다.
ResourceLoaderAware InterfaceResourceLoaderAware 인터페이스는 ResourceLoader 참조가 제공될 것으로 예상되는 구성 요소를 식별하는 특수 콜백 인터페이스입니다. 다음 목록은 ResourceLoaderAware 인터페이스의 정의를 보여줍니다.
public interface ResourceLoaderAware {
void setResourceLoader(ResourceLoader resourceLoader);
}
클래스가 ResourceLoaderAware를 구현하고 애플리케이션 컨텍스트(Spring 관리 빈으로)에 배포되면 애플리케이션 컨텍스트에 의해 ResourceLoaderAware로 인식됩니다. 그런 다음 애플리케이션 컨텍스트는 setResourceLoader(ResourceLoader)를 호출(invoke)하여 자신을 인수로 제공합니다(Spring의 모든 애플리케이션 컨텍스트는 ResourceLoader 인터페이스를 구현한다는 것을 기억하십시오).
ApplicationContext는 ResourceLoader이므로 Bean은 ApplicationContextAware 인터페이스를 구현하고 제공된 애플리케이션 컨텍스트를 직접 사용하여 리소스를 로드할 수도 있습니다. 그러나 일반적으로 그것이 필요한 전부라면 특수 ResourceLoader 인터페이스를 사용하는 것이 더 좋습니다. 코드는 전체 Spring ApplicationContext 인터페이스가 아닌 리소스 로딩 인터페이스(유틸리티 인터페이스로 간주될 수 있음)에만 연결됩니다.
애플리케이션 구성 요소에서는 ResourceLoaderAware 인터페이스를 구현하는 대신 ResourceLoader의 자동 연결(autowiring)을 사용할 수도 있습니다. 전통적인 constructor 및 byType autowiring 모드(Autowiring Collaborators에 설명된 대로)는 각각 생성자 인수 또는 setter 메서드 매개 변수에 대해 ResourceLoader를 제공할 수 있습니다. 유연성을 높이려면(필드 autowiring 기능 및 다중 매개변수 메서드 포함) 어노테이션 기반 autowiring 기능을 사용하는 것이 좋습니다. 이 경우 ResourceLoader는 해당 필드, 생성자 또는 메서드가 @Autowired 어노테이션을 전달하는 한 ResourceLoader 타입을 예상하는 필드, 생성자 인수 또는 메서드 매개 변수에 자동으로 연결됩니다. 자세한 내용은 @Autowired 사용을 참조하세요.
[Note]
와일드카드를 포함하거나 특수classpath*:리소스 접두사(prefix)를 사용하는 리소스 경로에 대해 하나 이상의Resource개체를 로드하려면ResourceLoader대신 애플리케이션 컴포넌트에 자동 연결되는ResourcePatternResolver인스턴스를 사용하는 것을 고려하세요.
빈 자체가 일종의 동적 프로세스를 통해 리소스 경로를 결정하고 제공하려는 경우, 빈이 리소스를 로드하기 위해 ResourceLoader 또는 ResourcePatternResolver 인터페이스를 사용하는 것이 합리적일 수 있습니다. 예를 들어, 필요한 특정 리소스가 사용자의 역할에 따라 달라지는 일종의 템플릿 로드를 생각해 보세요. 리소스가 정적인 경우 ResourceLoader 인터페이스(또는 ResourcePatternResolver 인터페이스)의 사용을 완전히 제거하고 Bean이 필요한 Resource 속성(property)을 노출하도록 하고 해당 속성이 여기에 주입되도록 하는 것이 합리적입니다.
이러한 속성을 삽입하는 것이 쉬운 이유는 모든 애플리케이션 컨텍스트가 String 경로를 Resource 객체로 변환할 수 있는 특수 JavaBeans PropertyEditor를 등록하고 사용한다는 것입니다. 예를 들어, 다음 MyBean 클래스에는 Resource 유형의 template 속성이 있습니다.
public class MyBean {
private Resource template;
public setTemplate(Resource template) {
this.template = template;
}
// ...
}
XML 구성 파일에서 template 속성은 다음 예제와 같이 해당 리소스에 대한 간단한 문자열로 구성될 수 있습니다.
<bean id="myBean" class="example.MyBean">
<property name="template" value="some/resource/path/myTemplate.txt"/>
</bean>
리소스 경로에는 접두사(prefix)가 없습니다. 결과적으로 애플리케이션 컨텍스트 자체가 ResourceLoader로 사용되기 때문에 리소스는 애플리케이션 컨텍스트의 정확한 타입에 따라 ClassPathResource, FileSystemResource 또는 ServletContextResource를 통해 로드됩니다.
특정 Resource 타입을 강제로 사용해야 하는 경우 접두사를 사용할 수 있습니다. 다음 두 가지 예는 ClassPathResource 및 UrlResource(후자는 파일 시스템의 파일에 액세스하는 데 사용됨)를 강제하는 방법을 보여줍니다.
<property name="template" value="classpath:some/resource/path/myTemplate.txt">
<property name="template" value="file:///some/resource/path/myTemplate.txt"/>
MyBean 클래스가 어노테이션 기반 구성(configuration)과 함께 사용하기 위해 리팩터링된 경우 myTemplate.txt에 대한 경로는 template.path 라는 키 아래에 저장될 수 있습니다. 예를 들어 Spring Environment에서 사용할 수 있는 속성 파일에 저장됩니다(환경 추상화 참조). 그런 다음 속성 자리 표시자를 사용하여 @Value 어노테이션을 통해 템플릿 경로를 참조할 수 있습니다(@Value 사용 참조). Spring은 템플릿 경로의 값을 문자열로 검색하고 특수 PropertyEditor는 문자열을 MyBean 생성자에 주입될 Resource 개체로 변환합니다. 다음 예에서는 이를 달성하는 방법을 보여줍니다.
@Component
public class MyBean {
private final Resource template;
public MyBean(@Value("${template.path}") Resource template) {
this.template = template;
}
// ...
}
classpath의 여러 위치에 있는 동일한 경로에서 발견된 여러 템플릿을 지원하려는 경우(예: classpath의 여러 jar에서) 특수 classpath*: 접두사와 와일드카드를 사용하여 template.path 키를 classpath*:/config/template/*.txt로 정의할 수 있습니다. MyBean 클래스를 다음과 같이 재정의하면 Spring은 템플릿 경로 패턴을 MyBean 생성자에 주입할 수 있는 Resource 객체의 배열로 변환합니다.
@Component
public class MyBean {
private final Resource[] templates;
public MyBean(@Value("${templates.path}") Resource[] templates) {
this.templates = templates;
}
// ...
}
이 섹션에서는 XML로 작동하는 바로 가기(shortcut), 와일드카드 사용 방법 및 기타 세부 정보를 포함하여 리소스로 애플리케이션 컨텍스트를 생성하는 방법을 다룹니다.
특정 애플리케이션 컨텍스트 타입에 대한 애플리케이션 컨텍스트 생성자는 일반적으로 문자열 또는 문자열 배열을, 컨텍스트 정의를 구성하는 XML 파일과 같은 리소스의 위치 경로로 사용합니다.
이러한 위치 경로에 접두사가 없으면 해당 경로에서 빌드되고 Bean 정의를 로드하는 데 사용되는 특정 Resource 타입은 특정 애플리케이션 컨텍스트에 따라 달라지며 적합합니다. 예를 들어, ClassPathXmlApplicationContext를 생성하는 다음 예제를 고려해보세요.
ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");
ClassPathResource가 사용되기 때문에 Bean 정의는 classpath에서 로드됩니다. 그러나 FileSystemXmlApplicationContext를 생성하는 다음 예제를 고려하십시오.
ApplicationContext ctx =
new FileSystemXmlApplicationContext("conf/appContext.xml");
이제 빈 정의는 파일 시스템 위치(이 경우 현재 작업 디렉토리를 기준으로 함)에서 로드됩니다.
위치 경로에 특수 classpath 접두어나 표준 URL 접두어를 사용하면 빈 정의를 로드하기 위해 생성된 기본 Resource 타입이 재정의(override)된다는 점에 유의하세요. 다음 예를 고려하십시오.
ApplicationContext ctx =
new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");
FileSystemXmlApplicationContext를 사용하면 classpath에서 빈 정의가 로드됩니다. 그러나 여전히 FileSystemXmlApplicationContext입니다. 이후에 ResourceLoader로서 사용되는 경우 접두사가 없는 경로는 여전히 파일 시스템 경로로 처리됩니다.
ClassPathXmlApplicationContext Instances — ShortcutsClassPathXmlApplicationContext는 편리한 인스턴스화를 가능하게 하기 위해 여러 생성자를 노출합니다. 기본 개념은 XML 파일 자체의 파일 이름만 포함하는(선행 경로 정보 없이) 문자열 배열만 제공하고 Class도 제공할 수 있다는 것입니다. 그런 다음 ClassPathXmlApplicationContext는 제공된 클래스에서 경로 정보를 파생합니다.
다음 디렉터리 레이아웃을 고려하세요.
com/
example/
services.xml
repositories.xml
MessengerService.class
다음 예제에서는 services.xml 및 repositories.xml(classpath에 있음)이라는 파일에 정의된 Bean으로 구성된 ClassPathXmlApplicationContext 인스턴스를 인스턴스화할 수 있는 방법을 보여줍니다.
ApplicationContext ctx = new ClassPathXmlApplicationContext(
new String[] {"services.xml", "repositories.xml"}, MessengerService.class);
다양한 생성자에 대한 자세한 내용은 ClassPathXmlApplicationContext javadoc를 참조하세요.
애플리케이션 컨텍스트 생성자 값의 리소스 경로는 간단한 경로(앞서 설명한 것처럼)일 수 있으며, 각 경로는 대상 Resource에 대한 일대일 매핑을 갖거나 특수 classpath*: 접두사 또는 내부 Ant 스타일을 포함할 수 있습니다. 패턴(Spring의 PathMatcher 유틸리티를 사용하여 매칭). 후자는 둘 다 사실상 와일드카드입니다.
이 메커니즘의 한 가지 용도는 컴포넌트 스타일 애플리케이션 어셈블리를 수행해야 할 때입니다. 모든 컴포넌트는 컨텍스트 정의 조각을 잘 알려진 위치 경로에 게시(publish)할 수 있으며, classpath*: 접두사가 붙은 동일한 경로를 사용하여 최종 애플리케이션 컨텍스트가 생성되면 모든 구성 요소 조각이 자동으로 선택됩니다.
이 와일드카드는 애플리케이션 컨텍스트 생성자에서(또는 PathMatcher 유틸리티 클래스 계층을 직접 사용하는 경우) 리소스 경로를 사용하는 경우에만 적용되며 생성 시(at construction time) resolve됩니다. Resource 타입 자체와는 아무 관련이 없습니다. 리소스는 한 번에 하나의 리소스만 가리키므로 classpath*: 접두사를 사용하여 실제 Resource를 구성할 수 없습니다.
다음 예제와 같이 경로 위치에는 Ant 스타일 패턴이 포함될 수 있습니다.
/WEB-INF/*-context.xml
com/mycompany/**/applicationContext.xml
file:C:/some/path/*-context.xml
classpath:com/mycompany/**/applicationContext.xml
경로 위치에 Ant 스타일 패턴이 포함된 경우, resolver는 더 복잡한 절차에 따라 와일드카드를 해석하려고 시도합니다. 와일드카드가 아닌 마지막 세그먼트까지의 경로에 대한 Resource를 생성하고 여기에서 URL을 얻습니다. 이 URL이 jar: URL 또는 컨테이너 특정 변형(예: WebLogic의 zip:, WebSphere의 wsjar 등)이 아닌 경우 여기에서 java.io.File을 얻고 파일 시스템을 통과하여 와일드카드를 resolve하는 데 사용됩니다. jar URL의 경우 resolver는 java.net.JarURLConnection을 가져오거나 jar URL을 수동으로 구문 분석한 다음 jar 파일의 콘텐츠를 탐색하여 와일드카드를 확인합니다.
Implications on Portability
지정된 경로가 이미 file URL인 경우(기본 ResourceLoader가 파일 시스템이기 때문에 암시적으로 또는 명시적으로) 와일드카드는 완전히 이식 가능한 방식으로 작동하도록 보장됩니다.
지정된 경로가 classpath 위치인 경우, resolver는 Classloader.getResource() 호출을 수행하여 와일드카드가 아닌 마지막 경로 세그먼트 URL을 얻어야 합니다. 이것은 단지 경로의 노드이기 때문에(끝에 있는 파일이 아님) 실제로 이 경우에 어떤 종류의 URL이 반환되는지는 실제로 정의되지 않습니다(ClassLoader javadoc에서). 실제로 이는 항상 디렉토리(classpath 리소스가 파일 시스템 위치로 해석되는 위치) 또는 일종의 jar URL(classspath 리소스가 jar 위치로 해석되는 위치)을 나타내는 java.io.File입니다. 하지만 이 작업에는 이식성 문제가 있습니다.
와일드카드가 아닌 마지막 세그먼트에 대해 jar URL을 얻은 경우, resolver는 해당 세그먼트에서 java.net.JarURLConnection을 가져오거나 jar URL을 수동으로 구문 분석하여, jar의 내용을 탐색하고 와일드카드를 resolve할 수 있어야 합니다. 이는 대부분의 환경에서 작동하지만 다른 환경에서는 실패합니다. 따라서 jar에서 나오는 리소스의 와일드카드 resolution을 사용하기 전에 특정 환경에서 철저하게 테스트하는 것이 좋습니다.
classpath*: PrefixXML 기반 애플리케이션 컨텍스트를 구성할 때 위치 문자열은 다음 예제와 같이 특수 classpath*: 접두사를 사용할 수 있습니다.
ApplicationContext ctx =
new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");
이 특수 접두사는 지정된 이름과 일치하는 모든 클래스 경로 리소스를 획득한 다음(내부적으로 이는 기본적으로 ClassLoader.getResources(…) 호출을 통해 발생) 병합하여 final 애플리케이션 컨텍스트 정의를 형성해야 함을 지정합니다.
[Note]
와일드카드 classpath는 기본ClassLoader의getResources()메서드에 의존합니다. 현재 대부분의 애플리케이션 서버는 자체ClassLoader구현을 제공하므로, 특히 jar 파일을 처리할 때 동작이 다를 수 있습니다.classpath*가 작동하는지 확인하는 간단한 테스트는ClassLoader를 사용하여 classpath의 jar 내에서 파일을 로드하는 것입니다:getClass().getClassLoader().getResources("<someFileInsideTheJar>"). 이름은 같지만 서로 다른 두 위치에 있는 파일에 대해 이 테스트를 시도해 보세요. 예를 들어 이름과 경로가 같지만 classpath의 다른 jar에 있는 파일이 있습니다. 부적절한 결과가 반환되는 경우,ClassLoader동작에 영향을 미칠 수 있는 설정에 대해 애플리케이션 서버 설명서를 확인하십시오.
또한 classpath*: 접두어를 나머지 위치 경로의 PathMatcher 패턴과 결합할 수도 있습니다(예: classpath*:META-INF/*-beans.xml). 이 경우 resolution 전략은 매우 간단합니다. 와일드카드가 아닌 마지막 경로 세그먼트에서 ClassLoader.getResources() 호출이 사용되어 클래스 로더 계층 구조에서 일치하는 모든 리소스를 가져온 다음, 각 리소스에서 동일한 PathMatcher resolution 전략을 가져옵니다. 앞서 설명한 전략은 와일드카드 하위 경로에 사용됩니다.
Ant 스타일 패턴과 결합된 classpath*:는 실제 대상 파일이 파일 시스템에 상주하지 않는 한, 패턴이 시작되기 전에 최소한 하나의 루트 디렉토리에서만 안정적으로 작동합니다. 이는 classpath*:*.xml과 같은 패턴이 jar 파일의 루트에서 파일을 검색하지 않고 확장된 디렉터리의 루트에서만 파일을 검색할 수 있음을 의미합니다.
classpath 항목을 검색하는 Spring의 기능은 빈 문자열(검색할 잠재적 루트를 나타냄)에 대한 파일 시스템 위치만 반환하는 JDK의 ClassLoader.getResources() 메서드에서 비롯됩니다. Spring은 URLClassLoader 런타임 구성(configuration)과 jar 파일의 java.class.path 매니페스트도 평가(evaluate)하지만 이것이 이식 가능한 동작으로 이어진다는 보장은 없습니다.
[Note]
classpath 패키지를 스캐닝하려면 classpath에 해당 디렉토리 항목(entries)이 있어야 합니다. Ant로 JAR을 빌드할 때, JAR 작업의files-only스위치를 활성화하지 마십시오. 또한 classpath 디렉터리는 일부 환경(예: JDK 1.7.0_45 이상의 독립 실행형 애플리케이션)의 보안 정책에 따라 노출되지 않을 수 있습니다(매니페스트에 '신뢰할 수 있는 라이브러리'를 설정해야 함). stackoverflow.com을 참조하세요. /questions/19394570/java-jre-7u45-breaks-classloader-getresources).JDK 9의 모듈 경로(Jigsaw)에서 Spring의 classpath 검색은 일반적으로 예상대로 작동합니다. 여기서도 리소스를 전용 디렉터리에 두는 것이 좋습니다. 이는 앞서 언급한 jar 파일 루트 수준 검색 시 발생하는 이식성 문제를 방지하는 것입니다.
classpath: 리소스의 Ant 스타일의 패턴은 검색할 루트 패키지가 여러 classpath 위치에서 사용 가능한 경우, 리소스가 일치하는 리소스를 찾는다는 보장이 없습니다. 리소스 위치에 대한 다음 예를 고려하세요.
com/mycompany/package1/service-context.xml
이제 누군가가 해당 파일을 찾기 위해 사용할 수 있는 Ant 스타일 경로를 고려해 보십시오.
classpath:com/mycompany/**/service-context.xml
이러한 리소스는 classpath의 한 위치에만 존재할 수 있지만, 앞의 예와 같은 경로를 사용하여 이를 resolve하려고 하면 resolver는 getResource("com/mycompany")에서 반환된 (첫 번째) URL에서 작동합니다. 이 기본 패키지 노드가 여러 ClassLoader 위치에 존재하는 경우 원하는 리소스가 발견된 첫 번째 위치에 존재하지 않을 수 있습니다. 따라서 이러한 경우에는 com.mycompany 기본 패키지가 포함된 모든 classpath 위치를 검색하는 동일한 Ant 스타일 패턴으로 classpath*:를 사용하는 것이 좋습니다. (classpath*:com/mycompany/**/service-context.xml)
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");
실제로 true 절대 파일 시스템 경로가 필요한 경우, 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 its definition via a UrlResource
ApplicationContext ctx =
new FileSystemXmlApplicationContext("file:///conf/context.xml");