반응형
org.springframework.core.io.Resource
Resource 추상화란? springframework.core.io.Resource로 java.net.URL라는 클래스를 감싸고,
실제 low level에 있는 Resource에 접근하는 기능 을 추상화라고 한다.
특징
- java.net.URL 을 추상화 한 것
- 스프링 내부에서 많이 사용하는 인터페이스!
추상화 한 이유
- 클래스패스 기준으로 리소스 읽어오는 기능 부재
- ServletContext를 기준으로 상대 경로로 읽어오는 기능 부재
- 새로운 핸들러를 등록하여 특별한 URL 접미사를 만들어 사용할 수는 있지만 구현이 복잡하고 편의성 메소드가 부족하다.
우리가 ApplicationContext를 만들때,
var ctx = new ClassPathXmlApplicationContext("xxx.xml");
//xxx.xml 문자열이 resource로 변환이 된다.
인터페이스 둘러보기
- 상속 받은 인터페이스
- 주요 메소드
- getInputStream()
- exists() : 리소스가 존재하는지 true / false
- isOpen() : 리소스가 열려있는지
- getDescription(): 전체 경로 포함한 파일 이름 또는 실제 URL
구현체
- UrlResource : java.net.URL 참고, 기본으로 지원하는 프로토콜 http, https, ftp, file, jar.
- ClassPathResource : 지원하는 접두어 "classpath:~~"
- FileSystemResource
- ServletContextResource : 웹 애플리케이션 루트에서 상대 경로로 리소스 찾는다.
- ....
리소스 읽어오기
- Resource의 타입은 location 문자열과 ApplicationContext의 타입에 따라 결정 된다.
- ClassPathXmlApplicationContext -> ClassPathResource
- FileSystemXmlApplicationContext -> FileSystemResource
- WebApplicationContext -> ServletContextResource
- ApplicationContext의 타입에 상관없이 리소스 타입을 강제하려면 java.net.URL 접두어(+ classpath:)중 하나를 사용할 수 있다.
- classpath: me/whiteship/config.xml -> ClassPathResource : classpath경로 기준으로 문자열에 해당하는 리소스를 찾는다.
- file:// some/resource/path/config.xml -> FileSystemResource : filesystem 기준으로 리소스를 찾는다.
접두어를 사용해서 명시를 해주면 아주 좋다! 좋은 코드가 된다.
ex) classpath: ~~ -> 를 보고 "아~얘는 ClassPathResource로 오는구나~"
Resource 타입 찍어보기
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ApplicationContext resourceLoader;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println(resourceLoader.getClass()); //ApplicationContext의 리소스 타입을 찍어본다.
Resource resource = resourceLoader.getResource("classpath:test.txt");
System.out.println(resource.getClass());//classpath라는 prefix를 썼기때문에, classpathResource가 나온다.
System.out.println(resource.exists());
System.out.println(resource.getDescription());
System.out.println(Files.readString(Path.of(resource.getURI())));
}
}
class org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
class org.springframework.core.io.ClassPathResource
true
class path resource [test.txt]
Hello Spring
AnnotationConfigServletWebServerApplicationContext 를 따라가다 보면,..
public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry
public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext
public class GenericWebApplicationContext extends GenericApplicationContext implements ConfigurableWebApplicationContext, ThemeSource
public interface ConfigurableWebApplicationContext extends WebApplicationContext, ConfigurableApplicationContext
결국 끝에는 WebApplicationContext가 있다.
public interface WebApplicationContext extends ApplicationContext
원래는 ApplicationContext resourceLoader 이면 WebApplicationContext가 찍혀야하는데,
classpath: 라는 prefix 를 사용했기 때문에! ClassPathResource 가 찍힌것이다.
만약 classpath: 를 지운다면?
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ApplicationContext resourceLoader;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println(resourceLoader.getClass()); //ApplicationContext의 리소스 타입을 찍어본다.
Resource resource = resourceLoader.getResource("test.txt");//classpath: 생략
System.out.println(resource.getClass()); //기본적으로 WebApplicationContext가 찍히게 된다.
System.out.println(resource.exists()); // 찾을수 없으니 flase
System.out.println(resource.getDescription()); //Description은 띄울순 있어도..
System.out.println(Files.readString(Path.of(resource.getURI()))); //ㅕ
}
}
o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
ServletContextResource는 context path부터 찾게 되는데.. spring에 기본적으로 제공하는 내장되어있는 tomcat에는 context path가 지정되어 있지 않다. 그래서 Files.readString에서 오류가 나게 된다. (파일을 읽을수가 없기 때문)
접두어를 잘 써주자
반응형