Resource

  • java.net.URL을 추상화 한 것
  • org.springframework.core.io.Resource라는 클래스로 감싸 실제 low-level의 리소스에 접근하도록 리소스를 추상화한다.
  • 스프링 내부에서 많이 사용하는 인터페이스이다.

Resource를 추상화 한 이유

java.net.URL은 웹상에서 존재하는 자원(프로토콜, 호스트, 포트번호 등등)에 접근할 때 사용된다. 이 클래스는 classPath기준으로 리소스를 가져오는 방법이 없었다. 하지만 classpath, url을 통해 가져오는 두가지 방법 모두 resource를 가져오는 방법이기에, spring에서는 이것을 추상화하여 통일을 시켰다.

ClassPath란 ?
자바 가상머신이 프로그램을 실행할 때 class파일을 찾는데 기준이 되는 경로를 말한다. 빌드시 컴파일된 class파일의 위치 경로를 말하며, classPath를 지정하지 않을 경우, 자바 가상머신이 위치한 디렉토리에서만 클래스를 찾을 수 있다.

  • 클래스패스 기준으로 리소스를 읽어오는 기능 부재
  • ServletContext 기준으로 상대 경로를 읽어오는 기능 부재
  • 새로운 핸들러를 등록하여 특별한 URL 접미사를 만들어 사용할 수 있지만, 구현이 복잡하고 편의성 메소드 부족

Resource인터페이스의 주요 메소드

  • getInputStream()
  • exist() : 리소스가 존재하는지 확인
  • isOpen() : 리소스가 열린 상태인지 확인
  • getDescription() : 전체 경로를 포함한 파일 이름 또는 실제 URL

Resource 구현체

  • UrlResource : URL 기준으로 리소스를 읽음. http, https, ftp, file, jar등을 지원
  • ClassPathResource : classpath를 기준으로 리소스를 읽음
  • FileSystemResource : 파일 경로 기준으로 리소스를 읽음
  • ServletContextResource : 웹 어플리케이션 루트에서 상대 경로로 리소스를 찾는다.(가장 많이 쓰인다)

리소스를 읽어오는 방법

Resource타입은 location 문자열과 ApplicationContext의 타입에 따라 결정된다.

  • ClassPathXmlApplicationContext -> ClassPathResource
  • FileSystemXmlApplicationContext -> FileSystemResource
  • WebApplicationContext -> ServletContextResource
@Override
    public void run(ApplicationArguments args) throws Exception {
        var ctx = new ClassPathXmlApplicationContext("blabla.xml"); //xml이 리소스로 변환된다. 아래와 같이 변환된다고 볼 수 있다.
        Resource resource = resourceLoader.getResource("~~~"); // getResource 안의 파라미터 변수 안으로 들어간다.
      
    }

위와 같이 작성한 blabla.xml이라는 로케이션 문자열은 Resource로 변환이 된다. 이 때, ApplicationContext타입이 ClassPathXmlApplicationContext이기 때문에 ClassPathResource로 생성이 된다.

@Override
    public void run(ApplicationArguments args) throws Exception {
    
       var ctx = new FileSystemXmlApplicationContext("blabla.xml"); //xml이 리소스로 변환된다. 파일시스템 경로 기준으로 문자열에 해당되는 로케이션을 찾아서 변환한다.
       Resource resource = resourceLoader.getResource("~~~"); // getResource 안의 파라미터 변수 안으로 들어간다.
      
    }

위와 같이 작성하게 되면FileSystemXmlApplicationContext기준으로 생성하기 때문에 FileSystemResource로 리소스가 생성된다.

추가로, ApplicationContext인터페이스는 ResourcePatternResolver를 구현함으로써 ResourceLoader를 상속받는다. 따라서 ResourceLoader의 기능을 가지고 있다고 볼 수 있다. ApplicationContextClassPathXmlApplicationContext라면 classpath를 적어주지 않아도 ClassPathResource로 읽어온다.


ApplicationContext의 타입에 상관없이 리소스 타입을 강제하려면 java.net.URL접두어(+classpath:) 중 하나를 사용할 수 있다.

  • classpath:me/cjk/config.xml -> ClassPathResource
  • file:///some/resource/path/config.xml -> FileSystemResource

이 방법은 접두어를 사용하기 때문에 명시적으로 키워드를 적어주면, 코드만 보고 클래스경로로 오는지 파일경로로 오는지 직관적으로 알 수 있기에 이 방법을 더 추천한다.

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

위와 같이 getResource에서 접두어로 classpath를 명시해줌으로써 리소스의 클래스는 ClassPathResource로 출력되는 것을 확인할 수 있다. 여기서 접두어를 제거하고 실행하게 되면 아래와 같이 ServletContextResource로 출력된다.

class org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
class org.springframework.web.context.support.ServletContextResource
false

이는 스프링 부트가 생성하는 applicationContext가 기본으로 서블릿 기반 WebApplicationContext이기 때문에 ServletContextResource로 생성되었다고 볼 수 있다.


참고 자료

  1. 스프링 프레임워크 핵심 기술

oksusutea's blog

꾸준히 기록하려고 만든 블로그