스프링부트 애플리케이션에서 생성하는 파일 등의 생성물은...

절대경로보다는 상대경로로
애플리케이션 실행위치를 기준으로

생성하는 것이 여러모로 편리하다.

[스프링부트] Executable JAR 내에서 ResourceUtil.getFile() 사용시 FileNotFoundException 발생

발생문제

IDE에서 실행하거나 bootrun 명령으로 실행시켰을 경우에는 별다른 이상없이 동작하던

File defaultProfileImage = ResourceUtil.getFile("static/images/default-profile.png");

가 실행가능한 jar(Excutable jar) 형태로 만들어 실행시에는 java.io.FileNotFoundException 을 일으킨다. 그 접근경로를 살펴보면,

...Exception: java.io.FileNotFoundException: class path resource [static/images/default-profile.png] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/home/ihoneymon/.../build/libs/innoquartz-server.war!/WEB-INF/classes!/static/images/default-profile.png
    at io.honeymon.test.member.ApplicantServiceImpl.setDefaultProfileImage(ApplicantServiceImpl.java:59) ~[classes!/:na]
    at io.honeymon.test.member.ApplicantServiceImpl.register(ApplicantServiceImpl.java:39) ~[classes!/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_25]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_25]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_25]
    at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0_25]
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302) ~[spring-aop-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) ~[spring-tx-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281) ~[spring-tx-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) ~[spring-aop-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at com.sun.proxy.$Proxy172.register(Unknown Source) ~[na:na]
    at io.honeymon.test.member.SignupController.signUp(SignupController.java:53) ~[classes!/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_25]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_25]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_25]
    at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0_25]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:222) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110) [spring-webmvc-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:814) [spring-webmvc-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:737) [spring-webmvc-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) [spring-webmvc-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959) [spring-webmvc-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893) [spring-webmvc-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:969) [spring-webmvc-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:871) [spring-webmvc-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:707) [javax.servlet-api-3.1.0.jar!/:3.1.0]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:845) [spring-webmvc-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) [javax.servlet-api-3.1.0.jar!/:3.1.0]
    at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:812) [jetty-servlet-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1669) [jetty-servlet-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:224) [websocket-server-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652) [jetty-servlet-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration$ApplicationContextHeaderFilter.doFilterInternal(EndpointWebMvcAutoConfiguration.java:243) [spring-boot-actuator-1.3.1.RELEASE.jar!/:1.3.1.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652) [jetty-servlet-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652) [jetty-servlet-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:111) [spring-boot-actuator-1.3.1.RELEASE.jar!/:1.3.1.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652) [jetty-servlet-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:316) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:126) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:90) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:122) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:169) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:48) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:158) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:205) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:120) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652) [jetty-servlet-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652) [jetty-servlet-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652) [jetty-servlet-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:103) [spring-boot-actuator-1.3.1.RELEASE.jar!/:1.3.1.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652) [jetty-servlet-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:585) [jetty-servlet-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:577) [jetty-security-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:223) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1127) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:515) [jetty-servlet-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1061) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.server.Server.handle(Server.java:499) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:311) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:257) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:544) [jetty-io-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635) [jetty-util-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555) [jetty-util-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_25]

로그를 살펴보면 cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/home/ihoneymon/.../build/libs/innoquartz-server.war!/WEB-INF/classes!/static/images/default-profile.png 항목이 보일 것이다. 이에 대해서는,

해결책

war 파일이나 IDE로 application run as로 실행하였다면 실제 resource 파일인 file:// 프로토콜을 쓰기 때문에 File객체를 생성해 줄수 있지만, executable jar로 실행 했다면 FileNotFoundException이 발생 하게 됩니다.

에서 설명하고 있다. jar 파일로 실행시에는 VfsResourceDelegate 를 사용하면서 실제 파일시스템에서 지원하는 프로토콜로 처리가 되기에 의도했던 것과는 다르게 동작하게 된다. 이를 해결하기 위한 방법도 위의 포스팅에서 설명되어 있다.

해결적용

ClassPathResource classPathResource = new ClassPathResource("static/images/default-profile.png");
// classPathResource.getInputStream(); 를 사용하도록 처리 

문제 해결!

참고


현재 스프링부트 1.3.1 버전으로 해서

spring-webmvc-4.2.4.RELEASE

버전을 사용중인데,

윈도우와 리눅스에서 결과가 미묘하게 다르다?

윈도우에서는 컨트롤러에서 발생한 NullPointException을 @ControllerAdvice 컴포넌트서 잡질 못하고 그대로 NullPointException을 뱉었고

리눅스에서는 컨트롤러에서 발생한 NullPointException을 @ControllerAdvice 컴포넌트에서 잡아서 변환처리를 해줬다.


스프링부트 애플리케이션 개발가이드

…​ 쓴다면... 목차는 이렇게 잡을 듯 싶다. 쓸지는 모르겠지만...


Table of Contents

1. 개발환경 설정

1.1. JDK 설치

1.2. STS 설치

1.3. 그레이들Gradle 설치


2. 스프링부트 살펴보기

스프링부트 살펴보기

2.1. Hello, SpringBoot

별도의 설정없이 spring-boot-starter-web 만을 이용한 웹 애플리케이션 만들기

2.2. 스프링부트 특징소개

2.3. 스프링부트 시동절차 설명

2.4. 스프링부트 구조 설명

2.5. 실행가능한 내장형 jar


3. 스프링부트 환경 소개

3.1. 시스템요구사항

3.2. 빌드 시스템

3.2.1. 빌드툴 선택: 메이븐Maven or 그레이들 Gradle

3.2.2. Starter POM

3.2.3. 의존성관리

3.3. 패키지 형태 선택: jar or war

배포할 목적에 따라 선택

3.3.1. jar

3.3.2. war

3.4. 프로젝트 구조

3.4.1. 기본패키지 default 사용

3.4.2. 메인 클래스 위치

3.5. 애플리케이션 환경구성

3.5.1. 구성 클래스 설정

3.5.2. XML 임포트

3.6. 자동구성

debug=true 을 이용해서 조건에 부합하여 활성화된 자동구성과 그렇지 않은 자동구성 확인

3.6.1. 자동구성 대체하기

3.6.2. 자동구성 비활성화하기

3.6.3. 설정파일을 이용한 활성화 선택

3.7. 스프링 빈과 의존성 주입

3.8. 애플리케이션 실행

3.8.1. IDE

3.8.2. packaged application

3.8.3. Gradle 플러그인 사용

3.9. 개발자도구 dev-tools

3.10. 애플리케이션 압축포장

실행가능한 jar


4. 스프링부트 기능

4.1. 스프링 애플리케이션SpringApplication

4.2. 구성 확장하기

4.2.1. Properties 대신 YAML 사용

4.2.2. 타입-세이프 구성 프로퍼티즈

4.3. 프로필 Profil 사용

4.4. 로깅logging

4.5. 웹 애플리케이션 개발

4.5.1. 스프링 웹 MVC 프레임워크

4.5.2. 내장 서블릿 컨테이너 지원

4.6. 데이터베이스 동작

4.6.1. H2

4.6.2. JPA

4.7. 테스트

4.7.1. TDD로 한다?

4.8. 자동구성 만들기

넣을까 말까.

4.8.1. 자동구성된 빈에 대한 이해

4.8.2. 음..

거창하다.


5. 스프링부트 액츄에이터: 출시준비 기능

5.1. 출시준비 기능 활성화

5.2. 엔드포인트

5.3. 모니터링과 관리

5.3.1. HTTP

5.3.2. JMX

5.3.3. Remote shell

5.4. 측정

5.5. 프로세스 모니터링


6. 빌드

6.1. 의존성 버전 변경

6.2. 실행가능한 jar 만들기


7. 스프링부트 배포

7.1. 전통적인 배포가능한 war 배포

7.2. 스프링부트 설치

7.2.1. 유닉스/리눅스 서비스 등록

7.3. 클라우드 배포

7.3.1. Heroku

7.3.2. AWS

7.4. 도커Docker 배포


8. 부록

8.1. 개발을 위해 기능확인 방법

  • 스프링부트 레퍼런스 문서 참고

  • 관련 자동구성 클래스 확인

  • debug=true 를 이용한 활성화된 자동설정과 비활성화된 자동설정 확인

8.2. 실행가능한 jar 구조


기본구성되어 있는 스프링부트가 구동되면 JmxAutoConfiguration 구성이 구동된다. 이 JMX 구성 덕분에 로컬에서는 jconsole을 이용해서 애플리케이션의 상태를 모니터링할 수 있다.

오우!!

스프링부트 애플리케이션을 구동하고

구동될 때 구성을 살펴볼 수 있도록 application.propertiesdebug=true 속성을 부여하면 나오는 항목 중에 보면

JmxAutoConfiguration matched
     - @ConditionalOnClass classes found: org.springframework.jmx.export.MBeanExporter (OnClassCondition)
     - matched (OnPropertyCondition)
 
  JmxAutoConfiguration#mbeanExporter matched
     - @ConditionalOnMissingBean (types: org.springframework.jmx.export.MBeanExporter; SearchStrategy: current) found no beans (OnBeanCondition)
 
  JmxAutoConfiguration#mbeanServer matched
     - @ConditionalOnMissingBean (types: javax.management.MBeanServer; SearchStrategy: all) found no beans (OnBeanCondition)
 
  JmxAutoConfiguration#objectNamingStrategy matched
     - @ConditionalOnMissingBean (types: org.springframework.jmx.export.naming.ObjectNamingStrategy; SearchStrategy: current) found no beans (OnBeanCondition)

들을 볼 수 있다.

스프링부트 애플리케이션 구동이 완료되고

로컬에 떠있는 프로세스들 중에서 살펴보려는 스프링부트 애플리케이션을 선택하고



연결이 완료되면 다음의 항목들을 볼 수 있다.








@_@) 지금까지는 성능이나 모니터링에 대해서 관심이 높지 않았는데 이에 대해서도 관심을 가져야겠다 싶어졌다.

흠... 아마, 운영에 대한 경험이 없기 때문이 무관심했던 것이 아닐까?

위에서 보여지는 그래프에 대한 정보는, 스프링부트에 액츄에이터actuator 기능을 추가하면 REST API로 로 /beans, /metrics 등의 정보로 제공되기도 한다.

프론트엔드쪽도 잘하면... 이를 이용해서 간단한 대시보드를 만들어볼텐데... @_@);;

+ Recent posts