-
스프링 부트 7일차 - 스프링 부트 Jar, SpringApplication, 로거Portfolio/Spring Boot 2019. 4. 7. 00:25728x90
1. mvn package를 하면 하나의 JAR 파일이 생김
그 파일을 실행하려면 java -jar xxx.jar 로 실행하면됌
2. 1은 인텔리제이에서는 Maven의 Lifecycle에서 조작할 수 있음.
package를 만들면 그 파일은 프로젝트의 target에 생성됨.
3. Java 스펙에는 Jar에 내장된 또다른 Jar를 로딩하는 표준적인 방법은 없음.
스프링 부트에서는 내장 Jar를 구분해서 로딩시켜줌.
* uber Jar 라는 것도 있었으나 해당 Jar은 내장된 Jar에 대해서 구분해서 로딩시켜주는게 아니라 그냥 하나로 압축해버리는 것이었기 때문에 구분이 잘 되지 않았음.
참고 : https://docs.spring.io/spring-boot/docs/current/reference/html/executable-jar.html
4. 스프링 애플리케이션이 제공해주는 여러가지 옵션을 사용하기 위해서는 두 코드 중 아래 코드가 좀 더 낫다.
SpringApplication.run(Application.class, args);
SpringApplication app = new SpringApplication(Application.class);
app.run(args);5. 그냥 실행시키면 스프링 애플리케이션의 로그는 INFO 레벨의 로그만 출력된다.
이를 DEBUG 레벨까지 보고싶다면 다음과 같이 VM options에 -dDebug를 줘야한다.
6. 실행시 콘솔에 출력되는 배너를 바꿔주고 싶다면 resources 폴더에 banner.txt를 만들어주고 내용을 넣어주면 해당 내용이 배너로 출력된다.
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.4.RELEASE)
* https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-spring-application.html#boot-features-banner 를 들어가서 배너에 정해진 변수를 넣어주는 것도 가능하다.
7. SpringApplicationBuilder를 이용해 빌더 패턴으로도 사용 가능
package me.junhyung.webservershowcase;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(Application.class)
.run(args);
}
}8. 애플리케이션 컨텍스트가 만들어진 이후에 발생한 이벤트에 대한 리스너는 Bean으로 등록되어 관리 가능
애플리케이션 컨텍스트가 만들어지기 전에 발생한 이벤트에 대한 리스너는 관리가 안됌.
=> 애플리케이션 컨텍스트가 만들어지기 전에 발생한 이벤트에 대한 리스너는 다음과 같은 방법으로 직접 추가해줘야함.
Application.java
package me.junhyung.webservershowcase;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Application.class);
app.addListeners(new SampleListener());
app.run(args);
}
}SampleListener.java
package me.junhyung.webservershowcase;
import org.springframework.boot.context.event.ApplicationStartingEvent;
import org.springframework.context.ApplicationListener;
public class SampleListener implements ApplicationListener<ApplicationStartingEvent> {
@Override
public void onApplicationEvent(ApplicationStartingEvent applicationStartingEvent) {
System.out.println("=======================");
System.out.println("Application is starting");
System.out.println("=======================");
}
}* SampleListener은 어차피 애플리케이션 컨텍스트가 생기기 전에 등록되어야 하기 때문에 @Component 어노테이션을 줘서 Bean으로 등록시킬 필요가 없음. 그냥 app.addListeners로 등록하니까.
9. 어떤 Bean에 생성자가 1개이고, 그 생성자의 파라미터가 Bean일 경우 그 Bean을 스프링이 알아서 주입해줌.
package me.junhyung.webservershowcase;
import org.springframework.boot.ApplicationArguments;
import org.springframework.stereotype.Component;
@Component
public class SampleListener {
public SampleListener(ApplicationArguments arguments) {
System.out.println("foo: " + arguments.containsOption("foo"));
System.out.println("bar: " + arguments.containsOption("bar"));
}
}위 코드에서 SampleListener의 생성자 ApplicationArguments arguments 부분은 스프링에서 관리 중인 Bean을 주입해준다.
10. ApplicationAruguments는 VM option은 포함하지 않고 Program arguments만 포함함.
11. 애플리케이션을 실행한 뒤에 뭔가 실행하고 싶을 때
ApplicationRunner를 이용해 실행, CommandLineRunner를 통해서 좀 더 로우레벨로 실행 가능
12. @Order(x)를 통해 Runner의 실행 순서 결정 가능
숫자가 낮을 수록 먼저 실행 됨. @Order(1)이 @Order(3)보다 먼저 실행 됨.
13. 외부 환경 변수는 다음과 같은 우선순위를 가지고 높은 우선순위에 있는게 낮은 우선순위에 있는 것을 오버라이딩함.
- Devtools global settings properties on your home directory (
~/.spring-boot-devtools.properties
when devtools is active). @TestPropertySource
annotations on your tests.properties
attribute on your tests. Available on@SpringBootTest
and the test annotations for testing a particular slice of your application.- Command line arguments.
- Properties from
SPRING_APPLICATION_JSON
(inline JSON embedded in an environment variable or system property). ServletConfig
init parameters.ServletContext
init parameters.- JNDI attributes from
java:comp/env
. - Java System properties (
System.getProperties()
). - OS environment variables.
- A
RandomValuePropertySource
that has properties only inrandom.*
. - Profile-specific application properties outside of your packaged jar (
application-{profile}.properties
and YAML variants). - Profile-specific application properties packaged inside your jar (
application-{profile}.properties
and YAML variants). - Application properties outside of your packaged jar (
application.properties
and YAML variants). - Application properties packaged inside your jar (
application.properties
and YAML variants). @PropertySource
annotations on your@Configuration
classes.- Default properties (specified by setting
SpringApplication.setDefaultProperties
).
참고 : https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config
14. 테스트를 할때는 테스트용 application.properties가 소스의 application.properties를 덮어 쓴다. 그래서 필요한 properties는 테스트용에도 들어가야한다.
=> 근데 이러면 매번 테스트에도 필요한 프로퍼티를 추가해줘야함.
때문에 테스트 쪽에는 프로퍼티를 지우고 @TestPropertySource를 이용해서 프로퍼티를 테스트용에다 추가해주는 방식이 좋음.
package me.junhyung.webservershowcase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.env.Environment;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@TestPropertySource(properties = "junhyung.name=foo")
@SpringBootTest
public class SpringApplicationTests {
@Autowired
Environment environment;
@Test
public void contextLoads() {
assertThat(environment.getProperty("junhyung.name"))
.isEqualTo("foo");
}
}근데 또 이게 넘 많으면 관리하기가 어려움. 테스트 쪽에서는 application.properties 라는 이름 말고 다른 이름(ex. test.properties)으로 테스트용 프로퍼티들을 만들어두고, @TestPropertySource에서 locations로 지정해주면 됨.
package me.junhyung.webservershowcase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.env.Environment;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@TestPropertySource(locations = "classpath:/test.properties")
@SpringBootTest
public class SpringApplicationTests {
@Autowired
Environment environment;
@Test
public void contextLoads() {
assertThat(environment.getProperty("junhyung.name"))
.isEqualTo("foo");
}
}15. application.properties도 위치에 따라 우선순위가 있음
file:./config/
file:./
classpath:/config/
classpath:/
16. application.properties에 다음과 같이 properties가 등록되어 있는 상태에서 이 properties를 Bean으로 만들 수 있음.
junhyung.name=lee
junhyung.age=${random.int(100)}이 프로퍼티들은 junhyung 이라는 prefix가 있는데, 이것을 Bean형태로 만들어주기 위해서는 다음과 같은 코드가 작성되면 됨.
JunhyungProperties.java
package me.junhyung.webservershowcase;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties("junhyung")
public class JunhyungProperties {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}Bean으로서이 조건에 맞아야하기 때문에 getter, setter 메소드를 구현해주어야함.
이렇게 한 후 이 Bean을 주입받는 쪽에서는 @Autowired로 주입받으면 됨.
SampleListener.java
package me.junhyung.webservershowcase;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component
public class SampleListener implements ApplicationRunner {
@Autowired
JunhyungProperties junhyungProperties;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("==========");
System.out.println(junhyungProperties.getName());
System.out.println(junhyungProperties.getAge());
System.out.println("==========");
}
}17. @Validated 를 이용하면 프로퍼티에 대한 검증을 할 수 있음. (JSR-303)
* hibernate-validator에 포함되어 있음.
package me.junhyung.webservershowcase;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotEmpty;
@Component
@ConfigurationProperties("junhyung")
@Validated
public class JunhyungProperties {
@NotEmpty
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}이것은 name이 @NotEmpty하지 않도록 만들어준다. Empty라면 에러가 난다.
18. 어떤 프로파일을 사용할지 정하려면 application.properties에 옵션을 다음과 같이 주면 됨.
spring.profiles.active=production
이렇게 주면 @Profile("production")으로 되어있는 프로파일만 활성화 됨.
* 당연히 앞에 배운 내용을 응용하여 커맨드라인 args로 줘도 됨.
19. 스프링에서는 Commons Logging을(로깅 퍼사드) 사용하지만,
스프링부트에서는 SLF4j를 로킹 퍼사드로 사용, 최종적으로는 Logback으로 로깅이 되는것
'Portfolio > Spring Boot' 카테고리의 다른 글
스프링 부트 12일차 - CORS, 인메모리 DB(H2), DBCP, JdbcTemplate, Spring Data JPA (0) 2019.04.18 스프링 부트 11일차 - index 페이지, 파비콘, 템플릿 엔진, HtmlUnit, ExceptionHandler (0) 2019.04.16 스프링 부트 10일차 - ViewResolve, Static Resource, WebJar (0) 2019.04.15 스프링 부트 9일차 - Spring-Boot-Devtools, 스프링 웹 MVC (0) 2019.04.09 스프링 부트 8일차 - 스프링 부트 테스트 (0) 2019.04.08 스프링 부트 6일차 - 스프링 부트 내장 웹 서버, SSL/HTTP2 적용 (0) 2019.04.06 스프링 부트 5일차 - 스프링 부트 AutoConfigure (0) 2019.04.05 스프링 부트 4일차 - 스프링 부트 프로젝트 구조와 의존성 그리고 자동설정 (0) 2019.04.04 스프링 부트 3일차 - 인프런 강좌로 다시 시작 (0) 2019.04.03 스프링 부트 2일차 - junit으로 단위 테스트, FetchType (4) 2019.04.01 - Devtools global settings properties on your home directory (