티스토리 뷰
Resouce 추상화란?
스프링의 Resource 객체는 java.net.URL을 추상화 인터페이스이며, Resource 객체는 스프링 내부에서 가장 많이 사용하는 인터페이스이다. 추상화란 말이 어려운데 java.net.URL이 무엇인지 알아보면서 좀 더 자세히 정리해보자.
java.net.URL이란?
java.net.URL 클래스는 웹상에 존재하는 자원에 접근할 때 사용한다. 웹상의 존재하는 자원이라 하면 프로토콜, 호스트, 포트번호 등등이다.
그러면 java.net.URL을 추상화한 이유가 무엇일까?
- 기존의 java.net.URL 클래스의 classpath 기준으로 Resource를 가져오는 기능 부재
- ServletContext를 기준으로 상대 경로를 읽어오는 기능 부재
- 새로운 핸들러를 등록하여 특별한 URL 접미사를 만들어 사용할 수는 있지만 구현이 복잡하고 편의성 메소드 부족
- 한마디로 하나의 Resource 인터페이스로 여러 기능의 방법을 통일시킨 것이다.
Resource 인터페이스 내부
public interface Resource extends InputStreamSource {
boolean exists();
default boolean isReadable() {
return exists();
}
default boolean isOpen() {
return false;
}
default boolean isFile() {
return false;
}
URL getURL() throws IOException;
URI getURI() throws IOException;
File getFile() throws IOException;
default ReadableByteChannel readableChannel() throws IOException {
return Channels.newChannel(getInputStream());
}
long contentLength() throws IOException;
long lastModified() throws IOException;
Resource createRelative(String relativePath) throws IOException;
@Nullable
String getFilename();
String getDescription();
}
exists()를 통해서 존재하는지 여부를 알 수도 있고 URI, URL을 가져오는 등등의 다양한 메소드들이 존재한다.
Resource 인터페이스 구현체
- UrlResource : URL을 기준으로 리소스를 읽어들임, 프로토콜 http, https, ftp, file, jar를 지원
- ClassPathResource : classPath 기준으로 리소스를 읽어들임
- ServletContextResource : 웹 어플리케이션 루트에서 상대 경로로 리소스를 찾는다. (가장 많이 쓰임)
Resource 읽어오기
Resource의 타입은 location 문자열(ex : application.xml)과 ApplicaionContext의 타입에 따라 결정된다. 그리고 예전에 스프링에서 빈을 등록하는 과정에 대해 정리한 적이 있다.
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
위와 같이 작성하였을 때 application.xml 설정파일을 찾아오는데 이 때 application.xml이라는 문자열이 Resource로 변환이 된다. 이 때 application.xml을 찾을 때 ClassPathXmlApplicaionContext이기 때문에 ClassPath를 기준으로 찾는다.
ApplicationContext applicationContext = new FileSystemXmlApplicationContext("application.xml");
이번에는 FileSystemXmlApplicationContext이기 때문에 파일시스템의 경로 기준으로 location 문자열에 해당하는 리소스를 찾게 된다. 그리고 하나 더 봐야할 점은 ApplicationContext의 타입에 따라 Resource의 타입이 결정된다는 말이다.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ResourceLoader resourceLoader;
@Override
public void run(ApplicationArguments args) throws Exception {
Resource resource = resourceLoader.getResource("text.txt");
System.out.println(resource.exists());
}
}
ApplicationContext 인터페이스의 상위 인터페이스 중에 하나는 ResourceLoader이다. 따라서 ApplicationContext가 ResourceLoader의 기능을 가지고 있다. 그래서 ApplicationContext가 ClassPathXmlApplicaionContext라면 run 메소드의 첫 번째 줄에서 굳이 classpath를 적어주지 않아도 classPath로 읽어온다.
- ClassPathXmlApplicationContext -> ClassPathResource
- FileSystemXmlApplicationContext -> FileSystemResource
- WebApplicationContext -> ServletContextResource (제일 많이 쓰임)
왼쪽에 적어놓은 클래스들은 ResourceLoader 인터페이스를 구현하고 있는 클래스이다. 오른쪽에 적어놓은 클래스들은 Resource 인터페이스를 구현한 클래스이다. 만약 AppicationContext의 타입에 상관없이 리소스 타입을 강제하려면 java.net.URL 접두어중 하나를 사용할 수 있다. 이 방법은 접두어를 사용하기 때문에 classPath라는 이름을 명시적으로 적어주면 코드만 보고 클래스경로로 오는지 파일경로로 오는지 직관적으로 알 수 있기 때문에 이러한 방법을 더 추천한다.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ApplicationContext resourceLoader;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println(resourceLoader.getClass());
Resource resource = resourceLoader.getResource("classpath:text.txt");
System.out.println(resource.getClass());
System.out.println(resource.exists());
}
}
class org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
class org.springframework.core.io.ClassPathResource
true
위와 같이 결과가 나오게 되고, 알아두어야할 점은 ApplicationContext를 Autowire로 주입을 받으면 위에서 적은 것 처럼 WebServerApplicationContext의 타입으로 나온다. 그리고 이번에는 classpath를 붙힌 후에 resource.getClass()를 찍어보면 ClassPathResource가 출력되는 것도 확인할 수 있다.
Resource resource = resourceLoader.getResource("text.txt");
그리고 이번에는 classpath를 빼고 다시 실행해보자.
class org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
class org.springframework.web.context.support.ServletContextResource
false
그러면 위와 같이 결과가 나오게 되고 두 번째줄을 보면 ServletContextResource로 출력이 되는 것을 알 수 있다.
# 인프런 강의 (백기선 / 스프링 프레임워크 핵심 기술)
Resource 추상화 https://joinwithyou.tistory.com/34
Validation 추상화 https://joinwithyou.tistory.com/35
데이터 바인딩 추상화: PropertyEditor https://joinwithyou.tistory.com/36
데이터 바인딩 추상화: Converter와 Formatter https://joinwithyou.tistory.com/37
SpEL (스프링 Expression Language) https://joinwithyou.tistory.com/38
스프링 AOP: 개념 소개 https://joinwithyou.tistory.com/39
스프링 AOP: 프록시 기반 AOP https://joinwithyou.tistory.com/39
스프링 AOP: @AOP https://joinwithyou.tistory.com/39
Null-safety https://joinwithyou.tistory.com/39
# Reference
https://devlog-wjdrbs96.tistory.com/165?category=882236 (블로그)
https://www.inflearn.com/course/spring-framework_core (백기선 강의)
'Spring' 카테고리의 다른 글
| 데이터 바인딩 추상화: PropertyEditor (0) | 2021.06.07 |
|---|---|
| [Spring] Validation 추상화 (0) | 2021.06.07 |
| IoC 컨테이너 9부: ResourceLoader (0) | 2021.06.06 |
| IoC 컨테이너 8부: ApplicationEventPublisher (0) | 2021.06.06 |
| IoC 컨테이너 7부: MessageSource (0) | 2021.06.06 |
