티스토리 뷰

Spring

[Spring] Resource 추상화

나죽나강 2021. 6. 7. 09:09

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 (백기선 강의)

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/04   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
글 보관함