ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 스프링 부트 7일차 - 스프링 부트 Jar, SpringApplication, 로거
    Portfolio/Spring Boot 2019. 4. 7. 00:25
    728x90

    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. 외부 환경 변수는 다음과 같은 우선순위를 가지고 높은 우선순위에 있는게 낮은 우선순위에 있는 것을 오버라이딩함.

    1. Devtools global settings properties on your home directory (~/.spring-boot-devtools.properties when devtools is active).
    2. @TestPropertySource annotations on your tests.
    3. properties attribute on your tests. Available on @SpringBootTest and the test annotations for testing a particular slice of your application.
    4. Command line arguments.
    5. Properties from SPRING_APPLICATION_JSON (inline JSON embedded in an environment variable or system property).
    6. ServletConfig init parameters.
    7. ServletContext init parameters.
    8. JNDI attributes from java:comp/env.
    9. Java System properties (System.getProperties()).
    10. OS environment variables.
    11. RandomValuePropertySource that has properties only in random.*.
    12. Profile-specific application properties outside of your packaged jar (application-{profile}.properties and YAML variants).
    13. Profile-specific application properties packaged inside your jar (application-{profile}.properties and YAML variants).
    14. Application properties outside of your packaged jar (application.properties and YAML variants).
    15. Application properties packaged inside your jar (application.properties and YAML variants).
    16. @PropertySource annotations on your @Configuration classes.
    17. 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도 위치에 따라 우선순위가 있음

    1. file:./config/
    2. file:./
    3. classpath:/config/
    4. 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으로 로깅이 되는것



Designed by Tistory.