Pyohwan / english-study

0 stars 0 forks source link

Spring Boot Features #25

Open Pyohwan opened 4 years ago

Pyohwan commented 4 years ago

12. Caching

@Component public class MathService {

@Cacheable("piDecimals")
public int computePiDecimal(int i) {
    // ...
}

}

* `i` argument 와 일치하는 `piDecimals` 캐시 엔트리가 있다면 메소드를 호출하지 않고 즉시 리턴함
  * 메소드가 호출되면 value 를 응답하기 전에 cache 에 업데이트 함

> `@CacheResult` 처럼 standard JSR-107 (JCache) 를 쓸 수 있다. 하지만 Spring Cache 와 JCache 를 혼합하여 쓰지 않는것을 강력히 추천한다

* 특정 cache 라이브러리를 추가 하지 않으면, 메모리내 concurrent maps 기반의 Simple provider 를 제공함
## 12.1. Supported Cache Providers
* cache abstraction 는 실제 store 를 제공하는게 아니라, `org.springframework.cache.Cache` and `org.springframework.cache.CacheManager` 인터페이스의 추상화를 제공
* CacheManager or a CacheResolver bean 이 정의되어 있지 않으면, 다음의 순서로 디텍팅 시도
  1. Generic
  1. JCache (JSR-107) (EhCache 3, Hazelcast, Infinispan, and others)
  1. EhCache 2.x
  1. Hazelcast
  1. Infinispan
  1. Couchbase
  1. Redis
  1. Caffeine
  1. Simple

> `spring.cache.type` property 로 강제로 cache provider 설정 가능

* auto-configured by Spring Boot 으로 `CacheManager` 할 경우, `CacheManagerCustomizer` 인터페이스 구현을 통해 커스터마이징 가능 함

@Bean public CacheManagerCustomizer cacheManagerCustomizer() { return new CacheManagerCustomizer() { @Override public void customize(ConcurrentMapCacheManager cacheManager) { cacheManager.setAllowNullValues(false); } }; }


> 위에선 auto-configured `ConcurrentMapCacheManager` 가 필요하다. 그렇지 않으면 전혀 호출 안됨

### 12.1.1. Generic
* 하나 이상의 `org.springframework.cache.Cache` bean 이 정의되어 있다면 Generic caching 이 사용된다. 
  * 래핑된 `CacheManager` bean 이 생성된다

### 12.1.2. JCache (JSR-107)
* JCache 는 `javax.cache.spi.CachingProvider` 가 classpath (JSR-107) 있다면 부트스트랩되고, `JCacheCacheManager` 는 `spring-boot-starter-cache` 가 제공한다.
* 둘 이상의 provider 가 있다면, 명시적으로 provider 지정 필요

Only necessary if more than one provider is present

spring.cache.jcache.provider=com.acme.MyCachingProvider spring.cache.jcache.config=classpath:acme.xml

* `javax.cache.cacheManager` 를 커스터마이징 하는 두가지 방법
  * `spring.cache.cache-names` property 설정 함. 커스텀 `javax.cache.configuration.Configuration` 빈이 정의되어 있다면 이걸 사용 함
  * `org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer` 빈으로 `CacheManager` 호출

> 표준 `javax.cache.CacheManager` 빈이 정의되어 있다면, 자동으로 `org.springframework.cache.CacheManager` 가 랩핑되고 커스타마이징은 지원 안함

### 12.1.3. EhCache 2.x
* `ehcache.xml` 가 root classpath 있다면, 사용됨. 
  * `EhCacheCacheManager` 는 `spring-boot-starter-cache` 의 캐시매니저로 부트스트랩 됨
* 다른 설정으로 하고 싶다면

spring.cache.ehcache.config=classpath:config/another-config.xml

### 12.1.4. Hazelcast
* Spring Boot 는 [general support for Hazelcast](https://docs.spring.io/spring-boot/docs/2.2.2.RELEASE/reference/html/spring-boot-features.html#boot-features-hazelcast) 가 있음
* `HazelcastInstance` 빈이 자동구성 되어있다면, 자동으로 `CacheManager` 가 래핑 됨
### 12.1.5. Infinispan
* 기본 구성 파일 위치가 없으므로, 따로 적어줘야 함

spring.cache.infinispan.config=infinispan.xml

* `spring.cache.cache-names` property 로 설정 가능

> Spring Boot 에서 Infinispan 지원은 제한적이다. 더 많은 옵션을 사용하기 위해선, Spring Boot starter 가 아니라 별도로 만들어야 한다.

### 12.1.6. Couchbase
* Couchbase 는 자바 클라이언트와 `couchbase-spring-cache` 구현을 사용할 수 있음
* `CouchbaseCacheManager` 자동 구성 됨
* `spring.cache.cache-names` property 로 설정 가능
* `Bucket` 설정 가능. 
  * cache1, cache2 은 "main" Bucket, cache3 는 “another” Bucket

spring.cache.cache-names=cache1,cache2

* cache3 를 갖고 있는 extra Bucket 구성 가능

@Configuration(proxyBeanMethods = false) public class CouchbaseCacheConfiguration {

private final Cluster cluster;

public CouchbaseCacheConfiguration(Cluster cluster) {
    this.cluster = cluster;
}

@Bean
public Bucket anotherBucket() {
    return this.cluster.openBucket("another", "secret");
}

@Bean
public CacheManagerCustomizer<CouchbaseCacheManager> cacheManagerCustomizer() {
    return c -> {
        c.prepareCache("cache3", CacheBuilder.newInstance(anotherBucket())
                .withExpiration(2));
    };
}

}

### 12.1.7. Redis
* Redis 가 구성되어 있다면, `RedisCacheManager` 가 자동구성 됨
* `spring.cache.cache-names` property 로 설정 가능
  * `spring.cache.redis.*` property 로 기본값 구성
* 다음은 live 시간이 10 분인 cache1, cache2 구성 예제

spring.cache.cache-names=cache1,cache2 spring.cache.redis.time-to-live=600000


> RedisCacheConfiguration Bean 으로 커스터마이징 가능

### 12.1.8. Caffeine
* Guava’s cache 를 대체하기 위해 Java 8 로 새로 만들어짐
* Caffeine 가 있다면, CaffeineCacheManager 가 자동구성 됨
* `spring.cache.cache-names` property 로 설정 가능하고, 커스터마이징은 다음의 순서로 할 수 있음
  1. cache spec 은 `spring.cache.caffeine.spec` 로 정의
  1. `com.github.benmanes.caffeine.cache.CaffeineSpec` 빈 정의
  1. `com.github.benmanes.caffeine.cache.Caffeine` 빈 정의 
* maximum size of 500 and a time to live of 10 minutes 한 cache1, cache2 설정

spring.cache.cache-names=cache1,cache2 spring.cache.caffeine.spec=maximumSize=500,expireAfterAccess=600s

* com.github.benmanes.caffeine.cache.CacheLoader 빈이 정의되어 있다면, `CaffeineCacheManager` 에 자동으로 연결 됨.
### 12.1.9. Simple
* 다른 제공자가 없다면, `ConcurrentHashMap` 로 구현된 cache store 가 구성 됨
* 캐싱 라이브러리가 없다면 기본적으로 사용 됨.
* `cache-names ` property 로 이용할 캐시 목록으로만 제한 가능

spring.cache.cache-names=cache1,cache2

* 캐시 목록을 지정하지 않고, 캐시 설정을 하면, startup 시 에러 난다
### 12.1.10. None
* `@EnableCaching` 되어 있다면, 캐시 사용이 예상되는데, 안쓰고 싶다면 다음과 같이 하라

spring.cache.type=none

Pyohwan commented 4 years ago

https://docs.spring.io/spring-boot/docs/2.2.2.RELEASE/reference/html/spring-boot-features.html#boot-features-spring-application

1. SpringApplication

2019-04-31 13:09:54.117 INFO 56603 --- [ main] o.s.b.s.app.SampleApplication : Starting SampleApplication v0.1.0 on mycomputer with PID 56603 (/apps/myapp.jar started by pwebb) 2019-04-31 13:09:54.166 INFO 56603 --- [ main] ationConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6e5a8246: startup date [Wed Jul 31 00:08:16 PDT 2013]; root of context hierarchy 2019-04-01 13:09:56.912 INFO 41370 --- [ main] .t.TomcatServletWebServerFactory : Server initialized with port: 8080 2019-04-01 13:09:57.501 INFO 41370 --- [ main] o.s.b.s.app.SampleApplication : Started SampleApplication in 2.992 seconds (JVM running for 3.658)

* 기본적으로 INFO 로깅 메시지
* `spring.main.log-startup-info` 로 startup 정보 로깅을 끌 수 있음
> SpringApplication 에서 `logStartupInfo(boolean)` 로도 설정 가능

## 1.1. Startup Failure
* FailureAnalyzers 에 등록된 오류 메시지와 해결을 위한 조치를 제공 함
* 8080 포트가 이미 사용중이라면?

APPLICATION FAILED TO START


Description:

Embedded servlet container failed to start. Port 8080 was already in use.

Action:

Identify and stop the process that's listening on port 8080 or configure this application to listen on another port.

> Spring Boot 는 수많은 `FailureAnalyzer` 구현체가 있고, 직접 추가도 가능 함

* failure analyzers 가 처리 하지 못하는 exception 이 있을 수 있는데, DEBUG 로깅으로 바꿔서 상세하게 봐라
* `java -jar` 할때 DEBUG 로깅을 하고 싶다면

$ java -jar myproject-0.0.1-SNAPSHOT.jar --debug

## 1.2. Lazy Initialization
* lazy initialization 를 활성화 하면, Bean 이 필요할때 생성된다. (애플리케이션 startup 이 아니라)
* 애플리케이션 시작 시간을 단축 시킬 수 있다
* HTTP 요청 수신전까지 웹 관련 Bean 이 초기화 되지 않는다
* 하지만 단점은, 만약 잘못된 Bean 이 있을 경우 startup 시 발견 되지 않는다
* 또한 startup 시에 충분한 메모리 공간을 알수가 없다

spring.main.lazy-initialization=true

> 특정 Bean 만 Lazy 를 사용하지 않으려면 `@Lazy(false)` 를 붙혀라

## 1.3. Customizing the Banner
* classpath 에 `banner.txt` 파일을 추가하거나, `spring.banner.location` 로 file 위치 설정 가능
  * 파일이 UTF-8 로 인코딩 되어 있다면, `spring.banner.charset` 로 설정 가능
* banner.gif, banner.jpg, or banner.png  등의 이미지 파일로 추가 가능
  * `spring.banner.image.location` 로 파일 위치 설정 가능
  * 이미지는 ASCII 로 표현 됨
* `banner.txt` 에서 사용할 수 있는 placeholders. [이 링크](https://docs.spring.io/spring-boot/docs/2.2.2.RELEASE/reference/html/spring-boot-features.html#boot-features-banner)의 표 참고
> 프로그래밍 적으로는 `SpringApplication.setBanner(…​)` method 로 설정 가능
* `spring.main.banner-mode` 로 `System.out (console)` 로 찍을지, 로그로 찍을지 설정 가능 함
* 프린트된 배너는 `springBootBanner` Bean 으로 등록 됨

## 1.4. Customizing SpringApplication
* `SpringApplication` 기본값이 마음에 안들면, 로컬 인스턴스를 작성하고 사용자 정의 가능하다.
  * 예를 들어 밴어를 끄려면

public static void main(String[] args) { SpringApplication app = new SpringApplication(MySpringConfiguration.class); app.setBannerMode(Banner.Mode.OFF); app.run(args); }

> SpringApplication 로 전달되는 생성자 아규먼트는 Spring Beans 의 구성 소스임. 대부분은 @Configuration 클래스에 대한 참조이지만, XML 구성에 대한 참조일 수도 있다
* application.properties 로도 SpringApplication 구성할수 있는데, Externalized Configuration 장에서 봐라

## 1.5. Fluent Builder API
* ApplicationContext 를 여러개 계층적으로 만들어야 하거나, “fluent” API 를 선호하면 SpringApplicationBuilder 를 사용해라
  *  SpringApplicationBuilder를 사용하면 여러 메소드 호출을 체인할 수 있고, 계층(child) 구조 작성 가능하다

new SpringApplicationBuilder() .sources(Parent.class) .child(Application.class) .bannerMode(Banner.Mode.OFF) .run(args);

> ApplicationContext 를 계층적으로 만들때에는 제약사항이 있다. 웹 컴포넌트는 child context 에 포함되어야 하고, 상하위의 컨텍스트 모두 동일한 환경이어야 한다. 

## 1.6. Application Events and Listeners
* `ContextRefreshedEvent` 와 같은 일반적인 이벤트 외에도, 추가적인 이벤트를 보낸다
> 특정 이벤트는 실제로 `ApplicationContext` 가 생성되기 전에 트리거되개 때문에, `@Bean` 으로 등록할 수 없다. `SpringApplication.addListeners(…​)` 메소드로 등록해라
> 리스너 자동 등록을 하고 싶다면 `META-INF/spring.factories` 파일에 리스너를 추가해라

org.springframework.context.ApplicationListener=com.example.project.MyListener


* 애플리케이션 이벤트는 다움의 순서로 전송 된다
   1. `ApplicationStartingEvent` 는 run 시작시 에 발송 (리스너 및 초기화 등록 제외하고)
   1. `ApplicationEnvironmentPreparedEvent` 는 컨텍스트 생선전에 사용될때 `Environment` 전송
   1. `ApplicationContextInitializedEvent` 는 `ApplicationContext` 가 준비되고, `ApplicationContextInitializers` 가 호출되고 Bean 이 생성전에 전송
   1. `ApplicationPreparedEvent` 는 refresh 시작 전, bean 정의가 로드된 후 전송
   1. `ApplicationStartedEvent` 는 컨텍스트가 refresh 되었고, command-line runners 호출 전에 전송
   1. `ApplicationReadyEvent` 는 애플리케이션 및 command-line runners 가 호출 된 후 전송. 애플리케이션이 요청을 처리할 준비가 되었다는 의미
   1. `An ApplicationFailedEven` 는 startup 시 exception 있을때 전송
* 이외에 ApplicationStartedEvent 와 ApplicationPreparedEvent 사이에 발생하는 이벤트
   1. `ContextRefreshedEvent` 는 ApplicationContext 가 refresh 되고 나서 전송
   1. `WebServerInitializedEvent` 는 웹서버가 준비된 후 전송

## 1.7. Web Environment
* SpringApplication 는 사용자를 대신하여 올바르게 ApplicationContext 를 만들려고 함
  * Spring MVC 가 있다면 AnnotationConfigServletWebServerApplicationContext 적용
  * Spring MVC 가 없고, Spring WebFlux 가 있다면 AnnotationConfigReactiveWebServerApplicationContext 적용
  * 모두 그렇지 않다면 AnnotationConfigApplicationContext 적용
* setWebApplicationType(WebApplicationType) 로 설정 가능
> JUnit test 할때에는 `setWebApplicationType(WebApplicationType.NONE)` 로 써라

## 1.8. Accessing Application Arguments
* `SpringApplication.run(…​)` 에서 아규먼트를 접근해야 하는 경우 `org.springframework.boot.ApplicationArguments` bean 을 삽입 가능

import org.springframework.boot.; import org.springframework.beans.factory.annotation.; import org.springframework.stereotype.*;

@Component public class MyBean {

@Autowired
public MyBean(ApplicationArguments args) {
    boolean debug = args.containsOption("debug");
    List<String> files = args.getNonOptionArgs();
    // if run with "--debug logfile.txt" debug=true, files=["logfile.txt"]
}

}


## 1.9. Using the ApplicationRunner or CommandLineRunner
* SpringApplication 가 시작되고 나서, 특정 코드를 한번 실행해야 한다면, ApplicationRunner or CommandLineRunner 인터페이스를 구현해라

import org.springframework.boot.; import org.springframework.stereotype.;

@Component public class MyBean implements CommandLineRunner {

public void run(String... args) {
    // Do something...
}

}

* 순서대로 호출해야 하면 `org.springframework.core.Ordered` interface or `org.springframework.core.annotation.Order` 애너테이션을 써라

## 1.10. Application Exit
* 각 SpringApplication 종료시, ApplicationContext 가 close 되도록 JVM에 종료 후크를 등록 한다.
  * DisposableBean 인터페이스 or `@PreDestroy` 를 사용 하라
* 또한 Bean 이 SpringApplication.exit() 가 호출되고, 특정 종료 코드를 응답하고자 한다면 org.springframework.boot.ExitCodeGenerator 인터페이스를 구현하라

@SpringBootApplication public class ExitCodeApplication {

@Bean
public ExitCodeGenerator exitCodeGenerator() {
    return () -> 42;
}

public static void main(String[] args) {
    System.exit(SpringApplication.exit(SpringApplication.run(ExitCodeApplication.class, args)));
}

}



## 1.11. Admin Features
* spring.application.admin.enabled 로 관리자 기능 사용 가능
  * 플랫폼 MBeanServer 에 SpringApplicationAdminMXBean 를 노출 시킨다
  * 이를 통해 Spring Boot application 을 원격으로 관리할 수 있다
Pyohwan commented 4 years ago

https://docs.spring.io/spring-boot/docs/2.2.4.RELEASE/reference/html/spring-boot-features.html#boot-features-external-config

2. Externalized Configuration

@Component public class MyBean {

@Value("${name}")
private String name;

// ...

}

* 애플리케이션 classpath 에 `application.properties` 파일이 있을 수 있고, jar 외부에서 `name` 을  overrides 하여 제공 가능하다.
* 일회성 테스트로 한번만 커맨드라인 수행 가능
  * `java -jar app.jar --name="Spring"`

> `SPRING_APPLICATION_JSON` properties 는 커맨드라인에서 환경변수와 함께 제공 가능하다.
> `$ SPRING_APPLICATION_JSON='{"acme":{"name":"test"}}' java -jar myapp.jar
> System property 로도 제공 가능
> `$ java -Dspring.application.json='{"name":"test"}' -jar myapp.jar`
> 커맨드라인 아규먼트로도 가능
> `$ java -jar myapp.jar --spring.application.json='{"name":"test"}'`
> JNDI 변수로도 가능 `java:comp/env/spring.application.json`

## 2.1. Configuring Random Values
* `RandomValuePropertySource` 는 랜덤값을 주입하는데 유용함 (for example, into secrets or test cases)
* integers, longs, uuids, or strings 생산 가능

my.secret=${random.value} my.number=${random.int} my.bignumber=${random.long} my.uuid=${random.uuid} my.number.less.than.ten=${random.int(10)} my.number.in.range=${random.int[1024,65536]}

* `random.int*` 문법은 `OPEN value (,max) CLOSE` 인데 `OPEN,CLOSE` 는 모든 캐릭터이고 `value,max` 는 integers 다
* `max` 가 있다면 `value` 는 최소값이고, `max` 는 최대값이다 (exclusive)

## 2.2. Accessing Command Line Properties
* 기본적으로 `SpringApplication` 는 커맨드라인 옵션 아규먼트(that is, arguments starting with --, such as --server.port=9000)를 `property` 로 변환하고, Spring `Environment` 에 추가 함
* 앞에서 봤듯이 커먼드라인 properties 는 다른 property sources 보다 우선임
* 커맨드라인 properties 를 `Environment` 에 추가를 원하지 않으면, `SpringApplication.setAddCommandLineProperties(false)` 게 해라

## 2.3. Application Property Files
* `SpringApplication` 아래에 있는 위치에서 `application.properties` 파일을 읽어들여 Spring `Environment` 에 추가한다
  1. 현재 디렉터리의 `/config` 서브 디렉터리
  1. 현재 디렉터리
  1. 클래스패스 `/config/ 패키지
  1. 클래스패스 root
* 위 목록은 우선순위 별로 나타낸것임
* 만일 `application.properties` 란 파일 이름을 원하지 않는다면, `spring.config.name` 환경 property 로 수정 가능하다.
* `spring.config.location` 환경 property (콤마로 구분된) 로 명시적인 로케이션을 참조토록 할 수 있다.
  * 예제임

$ java -jar myproject.jar --spring.config.name=myproject

* 두가지 로케이션을 지정하는 예제

$ java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties

> spring.config.name and spring.config.location 은 로드할 파일을 결정하기 위해 일찍 사용 된다. 환경 property 로만 정의 해야 한다 (OS 환경 변수, 시스템 프로퍼티, 커맨드라인 아규먼트)
* `spring.config.location` 가 디렉터리를 포함하고 있다면, `/` 로 끝나야 한다. (profile-specific 파일들과 함께 로드 되기 전에, 런타임에 `spring.config.name` 가 생성되어야 한다)
* `spring.config.location` 에 지정된 파일은 그대로 사용, profile-specific 변형은 지원되지 않고,  profile-specific properties 로 재정의 된다
* Config location 은 역순으로 검색된다.
  * 기본적으로 classpath:/,classpath:/config/,file:./,file:./config/
  1. file:./config/
  1. file:./
  1. classpath:/config/
  1. classpath:/
* `spring.config.location` 로 커스텀 config location 을 사용하면, default location 이 바뀐다. 예를 들어, `spring.config.location` 이 `classpath:/custom-config/,file:./custom-config/` 로 되어 있다면, 다음 순으로 될것이다.
  1. file:./custom-config/
  1. classpath:custom-config/
* 또는, `spring.config.additional-location` 으로 커스텀 config location 을 사용하면, default locations 이 외에 추가 된다. 
  * 예를 들어,  `classpath:/custom-config/,file:./custom-config/` 로 되어 있다면, 다음 순으로 될것이다.
  1. file:./custom-config/
  1. classpath:custom-config/
  1. file:./config/
  1. file:./
  1. classpath:/config/
  1. classpath:/
* 이 검색 순서를 이용하면 하나의 configuration 파일에서 기본값과 과 선택적 값을 재정의 할 수 있다. 
  * `application.properties` 에서 기본값 제공 가능
  * 런타임 시에 다른 file location 주입 가능
> 시스템 프로퍼티 대신 환경 변수을 사용하는 경우, period-separated (마침표) 키 네임을 허용 하지 않지만, 대신 밑줄 사용 가능하다. (spring.config.name 대신에 SPRING_CONFIG_NAME)
> 컨테이너에서 애플리케이션을 실행중일때 JNDI 프로퍼티 나 서블릿 초기화 파라미터를 환경 변수 또는 시스템 프로퍼티 대신에 사용할 수 있다.

## 2.4. Profile-specific Properties
* `application-{profile}.properties` 처럼 profile-specific properties 정의 가능함
*  `Environment` 에 명시적으로 profile 을 활성화 하지 않으면, 기본 프로파일을 `[default]` 을 사용함
  * `application-default.properties` 가 사용 됨
* Profile-specific properties 은 `application.properties` 과 동일한 location 에서 읽는다. 
  * packaged jar 안이든 밖이든 profile-specific 파일이 있다면, profile-specific 파일이 non-specific 을 항상 오버로딩 한다.
* 여러개의 profiles 이 주어지면, last-wins strategy 이 적용됨
  * 예를 들어 `spring.profiles.active` property 로 추가된 profile 은 `SpringApplication` API 로 구성된거 보다 뒤에 추가되므로, 뒤에 추가된것이 적용 됨
> `spring.config.location` 이 지정되어 있다면, profile-specific 변형이 고려 되지 않음

## 2.5. Placeholders in Properties
* `application.properties` 의 값은 `Environment` 에서 필터링 되고, 이전에 정의한 값들을 다시 사용 가능 함

app.name=MyApp app.description=${app.name} is a Spring Boot application

## 2.6. Encrypting Properties
* 기본적으로 property 암호화는 안되는데, `Environment` 의 값을 수정하는 후크 포인트를 제공함
* Spring Cloud Vault 참고

## 2.7. Using YAML Instead of Properties
* SnakeYAML 라이브러리가 있어서 YAML 지원 함

### 2.7.1. Loading YAML
* YAML 문서를 로드하는 두가지 방법이 있음
  * `YamlPropertiesFactoryBean` 은 YAML 을 `Properties` 로 읽음
  * `YamlMapFactoryBean` 은 YAML 을 `Map` 로  읽음

environments: dev: url: https://dev.example.com name: Developer Setup prod: url: https://another.example.com name: My Cool App

* 위 YAML 은 다음의 properties 로 변환 됨

environments.dev.url=https://dev.example.com environments.dev.name=Developer Setup environments.prod.url=https://another.example.com environments.prod.name=My Cool App

* YAML 목록은 `[index]` dereferencers 를 가진 property key 로 표현 됨

my: servers:

2.7.2. Exposing YAML as Properties in the Spring Environment

2.7.3. Multi-profile YAML Documents

2.7.4. YAML Shortcomings

2.8. Type-safe Configuration Properties

2.8.1. JavaBean properties binding

import java.net.InetAddress; import java.util.ArrayList; import java.util.Collections; import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("acme") public class AcmeProperties {

private boolean enabled;

private InetAddress remoteAddress;

private final Security security = new Security();

public boolean isEnabled() { ... }

public void setEnabled(boolean enabled) { ... }

public InetAddress getRemoteAddress() { ... }

public void setRemoteAddress(InetAddress remoteAddress) { ... }

public Security getSecurity() { ... }

public static class Security {

    private String username;

    private String password;

    private List<String> roles = new ArrayList<>(Collections.singleton("USER"));

    public String getUsername() { ... }

    public void setUsername(String username) { ... }

    public String getPassword() { ... }

    public void setPassword(String password) { ... }

    public List<String> getRoles() { ... }

    public void setRoles(List<String> roles) { ... }

}

}

* 위 POJO 는 다음의 properties 를 정의한다
  * acme.enabled, with a value of false by default.
  * `acme.remote-address` 는 `String` 으로 강제
  * `acme.security.username` 은 중첩된 "security" object 의 property 이고, 리턴 타입이 없다(?)
  *  `acme.security.roles` 은 기본적으로 `USER` 스프링 배열로 제공
> `@ConfigurationProperties` 는 properties files, YAML files, environment variables etc. 등 으로 구성되는 public API 이지만, 클래스의 accessors (getters/setters) 는 직접적으로 사용 되진 않음. 다음의 경우 세터는 생략 가능

> 배열은 empty 생성자에 의존하고, 일반적인 Java Bean 과 마찬가지로, 게터 세터 로 바인딩하므로 필수다
> * 초기화된 Map 은 게터는 필요하지만 세터는 바인더에 의해 변경 가능하므로 필요 없다
> * 콜렉션과 배열은 index (typically with YAML) or single comma-separated value (properties) 를 통해 접근 가능하다. 후자는 세터가 필수다. 하지만 그냥 세터 추가하는걸 추천한다. 초기화한 콜렉션이라면 not immutable 인지 확인 해라
> * 초기화된 중첩 POJO properties(like the Security field) 라면 세터는 필수아님. 하지만 기본 생성자로 객체를 만들면 세터 필요함
> 누구는 Lombok 으로 게터 세터를 자동화 하는데, 컨테이너에서 객체를 초기화 해주는데, 이런타입에 대해 Lombok 은 부분적인 생성자를 생성 하지 않는다
> * 표준 자바 빈 프로퍼티만 고려하기 때문에 static properties 를 지원 안함

### 2.8.2. Constructor binding
* 이전 세션에서 한것을 immutable 방식으로 재작성 가능
```java
package com.example;

import java.net.InetAddress;
import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.DefaultValue;

@ConstructorBinding
@ConfigurationProperties("acme")
public class AcmeProperties {

    private final boolean enabled;

    private final InetAddress remoteAddress;

    private final Security security;

    public AcmeProperties(boolean enabled, InetAddress remoteAddress, Security security) {
        this.enabled = enabled;
        this.remoteAddress = remoteAddress;
        this.security = security;
    }

    public boolean isEnabled() { ... }

    public InetAddress getRemoteAddress() { ... }

    public Security getSecurity() { ... }

    public static class Security {

        private final String username;

        private final String password;

        private final List<String> roles;

        public Security(String username, String password,
                @DefaultValue("USER") List<String> roles) {
            this.username = username;
            this.password = password;
            this.roles = roles;
        }

        public String getUsername() { ... }

        public String getPassword() { ... }

        public List<String> getRoles() { ... }

    }

}

생성자 바인딩을 사용하려면 @EnableConfigurationProperties 나 configuration property scanning 를 사용하여 클래스를 활성화 해야 한다. 일반적인 스프링 메카니즘(e.g. @Component beans, beans created via @Bean methods or beans loaded using @Import) 에서는 안된다

하나 이상의 생성자가 있다면 @ConstructorBinding 로 직접 생성자를 바인딩해라

2.8.3. Enabling @ConfigurationProperties-annotated types

2.8.4. Using @ConfigurationProperties-annotated types

acme: remote-address: 192.168.1.1 security: username: admin roles:

additional configuration as required

* `@ConfigurationProperties` 빈을 사용하려면, 다음 예제처럼 다른 빈 주입 하듯이 사용 함
```java
@Service
public class MyService {

    private final AcmeProperties properties;

    @Autowired
    public MyService(AcmeProperties properties) {
        this.properties = properties;
    }

    //...

    @PostConstruct
    public void openConnection() {
        Server server = new Server(this.properties.getRemoteAddress());
        // ...
    }

}

2.8.5. Third-party Configuration

}

* 위 코드는 다음과 같은 properties names 사용 가능

#### Table 2. relaxed binding

Property | Note
-- | --
acme.my-project.person.first-name | Kebab case, which is recommended for use in .properties and .yml files.
acme.myProject.person.firstName | Standard camel case syntax.
acme.my_project.person.first_name | Underscore notation, which is an alternative format for use in .properties and .yml files.
ACME_MYPROJECT_PERSON_FIRSTNAME | Upper case format, which is recommended when using system environment variables.

> `prefix` 값은 무조건 kebab 케이스

#### Table 3. relaxed binding rules per property source

Property Source | Simple | List
-- | -- | --
Properties Files | Camel case, kebab case, or underscore notation | Standard list syntax using [ ] or comma-separated values
YAML Files | Camel case, kebab case, or underscore notation | Standard YAML list syntax or comma-separated values
Environment Variables | Upper case format with underscore as the delimiter. _ should not be used within a property name | Numeric values surrounded by underscores, such as MY_ACME_1_OTHER = my.acme[1].other
System properties | Camel case, kebab case, or underscore notation | Standard list syntax using [ ] or comma-separated values

> 가능하면 properties 는 케밥 포맷 추천

* `Map` 바인딩
```yaml
acme:
  map:
    "[/key1]": value1
    "[/key2]": value2
    /key3: value3

괄호는 파씽하기 위해선 " 를 붙혀라

2.8.7. Merging Complex Types

}

* configuration
```yaml
acme:
  list:
    - name: my name
      description: my description
---
spring:
  profiles: dev
acme:
  list:
    - name: my another name

}

*configuration

acme: map: key1: name: my name 1 description: my description 1 ---yaml spring: profiles: dev acme: map: key1: name: dev name 1 key2: name: dev name 2 description: dev description 2

* `dev` profile 이 활성화 안되면 `AcmeProperties.map` 는 `key1` 한개 포함한다
* `dev` profile 이 활성화 되면, 2개의 key 를 포함한다

> 머지 룰은 YAML 뿐만 아니라 모든 property 소스에 적용 된다

### 2.8.8. Properties Conversion
* `@ConfigurationProperties` 빈 바인딩 할때 올바른 유형으로 강제하길 시도한다
* 커스텀 타입 conversion 이 필요하면 `ConversionService` `CustomEditorConfigurer` `@ConfigurationPropertiesBinding` 참고
> 애플리케이션 lifecycle 중에 매우 일찍 요청하므로 `ConversionService` 사용에 제약이 있다. 일반적으로 필요한 디펜던시가 아직 생성전일 수 있다. `@ConfigurationPropertiesBinding` 로 커스텀 `ConversionService` 리네임 가능

#### Converting durations
* `java.time.Duration` 프로퍼티 사용
  * 표준 `long` 표현 (`@DurationUnit` 를 지정 안하면 기본 milliseconds 사용)준
  * The standard ISO-8601 format used by java.time.Duration
  * value 와 unit 보다 읽기 쉬운 포맷 (e.g. 10s means 10 seconds)
```java
@ConfigurationProperties("app.system")
public class AppSystemProperties {

    @DurationUnit(ChronoUnit.SECONDS)
    private Duration sessionTimeout = Duration.ofSeconds(30);

    private Duration readTimeout = Duration.ofMillis(1000);

    public Duration getSessionTimeout() {
        return this.sessionTimeout;
    }

    public void setSessionTimeout(Duration sessionTimeout) {
        this.sessionTimeout = sessionTimeout;
    }

    public Duration getReadTimeout() {
        return this.readTimeout;
    }

    public void setReadTimeout(Duration readTimeout) {
        this.readTimeout = readTimeout;
    }

}

Converting Data Sizes

}

* 버퍼사이즈를 10메가로 하려면 10 and 10MB 해라
* threshold 256 bytes 는 256 or 256B 해라
* 다음은 units 지원
  * B for bytes
  * KB for kilobytes
  * MB for megabytes
  * GB for gigabytes
  * TB for terabytes

### 2.8.9. @ConfigurationProperties Validation
* `@ConfigurationProperties` 클래스에 `@Validated` 붙히면 유효성 검사 함
* JSR-303 javax.validation constraint annotation 도 사용 가능하다
```java
@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {

    @NotNull
    private InetAddress remoteAddress;

    // ... getters and setters

}

@Bean 메소드에도 @Validated 달아 유효성 트리거 가능

}


* `configurationPropertiesValidator` 를 정의하면 커스텀 `Validator` 추가 가능
  * ` @Bean` 메소드는 `static` 이어야 한다
  * configuration properties validator 는 애플리케이션 lifecycle 에서 일찍 생성되기 때문에, @Bean method as static 해야 생성 가능

### 2.8.10. @ConfigurationProperties vs. @Value
* `@Value` 는 코어 컨테이너 피쳐인데, type-safe configuration 는 제공 안함

Feature | @ConfigurationProperties | @Value
-- | -- | --
Relaxed binding | Yes | No
Meta-data support | Yes | No
SpEL evaluation | No | Yes

* 자체 콤퍼넌트의 configuration keys 를 정의할때에는 @ConfigurationProperties 와 POJO 가 좋음
* `@Value` 는 relaxed binding 을 지원안해서, environment variables 통해 제공하는 것은 적합하지 않다
* `@Value` 는 `SpEL` 사용이 가능하긴 한데 application property files 에서는 처리 안됨
Pyohwan commented 4 years ago

https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-profiles

3. Profiles

}

* `application.properties` 에서 `spring.profiles.active` 로 설정 가능

spring.profiles.active=dev,hsqldb

* 커맨드 라인에서 `--spring.profiles.active=dev,hsqldb` 로 지정 가능

## 3.1. Adding Active Profiles
* `PropertySource` 의 적용 우선순위가 있음
* `spring.profiles.include` 로 무조건 active 가능 함
  * `--spring.profiles.active=prod` 하면 proddb and prodmq active 됨

my.property: fromyamlfile

spring.profiles: prod spring.profiles.include:

3.2. Programmatically Setting Profiles

3.3. Profile-specific Configuration Files

Pyohwan commented 4 years ago

4. Logging

4.1. Log Format

2019-03-05 10:57:51.112  INFO 45469 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/7.0.52
2019-03-05 10:57:51.253  INFO 45469 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2019-03-05 10:57:51.253  INFO 45469 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1358 ms
2019-03-05 10:57:51.698  INFO 45469 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean        : Mapping servlet: 'dispatcherServlet' to [/]
2019-03-05 10:57:51.702  INFO 45469 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]

4.2. Console Output

4.2.1. Color-coded Output

Level Color
FATAL Red
ERROR Red
WARN Yellow
INFO Green
DEBUG Green
TRACE Green

4.3. File Output

logging.file.name logging.file.path Example Description
(none) (none)   Console only logging.
Specific file (none) my.log Writes to the specified log file. Names can be an exact location or relative to the current directory.
(none) Specific directory /var/log Writes spring.log to the specified directory. Names can be an exact location or relative to the current directory.

로깅 설정은 실제 로깅 infrastructure 와 독립적이다. 예를 들어 logback.configurationFile 은 스프링부트가 관리 안함

4.4. Log Levels

4.5. Log Groups

Name Loggers
web org.springframework.core.codec, org.springframework.http, org.springframework.web, org.springframework.boot.actuate.endpoint.web, org.springframework.boot.web.servlet.ServletContextInitializerBeans
sql org.springframework.jdbc.core, org.hibernate.SQL, org.jooq.tools.LoggerListener

4.6. Custom Log Configuration

Logging System Customization
Logback logback-spring.xml, logback-spring.groovy, logback.xml, or logback.groovy
Log4j2 log4j2-spring.xml or log4j2.xml
JDK (Java Util Logging) logging.properties

-spring 형식으로 만들어 달라. (logback.xml 보단 logback-spring.xml)

"executable jar" 에서 Java Util Logging 가 문제 발생하니, 이 상황에선 쓰지 마라

Spring Environment System Property Comments
logging.exception-conversion-word LOG_EXCEPTION_CONVERSION_WORD The conversion word used when logging exceptions.
logging.file.clean-history-on-start LOG_FILE_CLEAN_HISTORY_ON_START Whether to clean the archive log files on startup (if LOG_FILE enabled). (Only supported with the default Logback setup.)
logging.file.name LOG_FILE If defined, it is used in the default log configuration.
logging.file.max-size LOG_FILE_MAX_SIZE Maximum log file size (if LOG_FILE enabled). (Only supported with the default Logback setup.)
logging.file.max-history LOG_FILE_MAX_HISTORY Maximum number of archive log files to keep (if LOG_FILE enabled). (Only supported with the default Logback setup.)
logging.file.path LOG_PATH If defined, it is used in the default log configuration.
logging.file.total-size-cap LOG_FILE_TOTAL_SIZE_CAP Total size of log backups to be kept (if LOG_FILE enabled). (Only supported with the default Logback setup.)
logging.pattern.console CONSOLE_LOG_PATTERN The log pattern to use on the console (stdout). (Only supported with the default Logback setup.)
logging.pattern.dateformat LOG_DATEFORMAT_PATTERN Appender pattern for log date format. (Only supported with the default Logback setup.)
logging.pattern.file FILE_LOG_PATTERN The log pattern to use in a file (if LOG_FILE is enabled). (Only supported with the default Logback setup.)
logging.pattern.level LOG_LEVEL_PATTERN The format to use when rendering the log level (default %5p). (Only supported with the default Logback setup.)
logging.pattern.rolling-file-name ROLLING_FILE_NAME_PATTERN Pattern for rolled-over log file names (default ${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz). (Only supported with the default Logback setup.)
PID PID The current process ID (discovered if possible and when not already defined as an OS environment variable).

4.7. Logback Extensions

4.7.1. Profile-specific Configuration

### 4.7.2. Environment Properties
* `<springProperty>` 로 Spring `Environment` 에서 properties 가져올 수 있음
```xml
<springProperty scope="context" name="fluentHost" source="myapp.fluentd.host"
        defaultValue="localhost"/>
<appender name="FLUENT" class="ch.qos.logback.more.appenders.DataFluentAppender">
    <remoteHost>${fluentHost}</remoteHost>
    ...
</appender>

source 는 케밥 케이스 여야 함. (my.property-name)

Pyohwan commented 4 years ago

5. Internationalization

6. JSON

6.1. Jackson

Pyohwan commented 4 years ago

https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-developing-web-applications

7. Developing Web Applications

7.1. The “Spring Web MVC Framework”

}

### 7.1.1. Spring MVC Auto-configuration
* 자동구성으로 설정되는 기본 기능들
  * Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
  * WebJars 포함 장적 리소스 서빙
  * Automatic registration of Converter, GenericConverter, and Formatter beans.
  * Support for HttpMessageConverters
  * Automatic registration of MessageCodesResolver
  * Static index.html support.
  * Custom Favicon support
  * Automatic use of a ConfigurableWebBindingInitializer bean
* 커스텀할 수 있는데 `@EnableWebMvc` 대신 `WebMvcConfigurer` 클래스 넣고 작업 함
### 7.1.2. HttpMessageConverters
* 기본적으로 `HttpMessageConverter` 로 HTTP 요청 응답을 변환 함
  * JSON 은 Jackson lib 로, XML 은 Jackson XML extension 으로
  * 기본적으로 스트링은 `UTF-8` 로 인코딩
* 커스텀 컨버터를 추가하고 싶다면, `HttpMessageConverters` 를 사용해라
```java
mport org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.*;
import org.springframework.http.converter.*;

@Configuration(proxyBeanMethods = false)
public class MyConfiguration {

    @Bean
    public HttpMessageConverters customConverters() {
        HttpMessageConverter<?> additional = ...
        HttpMessageConverter<?> another = ...
        return new HttpMessageConverters(additional, another);
    }

}

7.1.3. Custom JSON Serializers and Deserializers

@JsonComponent public class Example {

public static class Serializer extends JsonSerializer<SomeObject> {
    // ...
}

public static class Deserializer extends JsonDeserializer<SomeObject> {
    // ...
}

}

### 7.1.4. MessageCodesResolver
* `MessageCodesResolver` 바인딩 에러로부터 에러 코드를 생성하는 전략이 있음
* `spring.mvc.message-codes-resolver-format` 로 PREFIX_ERROR_CODE or POSTFIX_ERROR_CODE 설정 가능

### 7.1.5. Static Content
* 기본적으로 정적 컨텐츠를 서빙 하는 path
  * /static (or /public or /resources or /META-INF/resources)
* 기본적으로 리소스는 `/**` 로 매핑 되지만, `spring.mvc.static-path-pattern` 로 수정 가능
  * 모든 리소스를 `/resources/**` 로 relocating 하고 싶다면

spring.mvc.static-path-pattern=/resources/**

* 정적 리소스 locations 도 기본값에서 대체할 수 있음
  * `spring.resources.static-locations`
* Webjars 컨텐츠는 jar 로 패키징 되어 있고, path 는 `/webjars/**` 임
> 애플리케이션이 jar 로 패키지 되어 있다면, `src/main/webapp` 디렉토리는 사용하지 마라. 이는 war 패키징에서만 동작하고, jar 에선 무시 됨
* cache-busting 이나 version agnostic URLs 와 같은 추가 기능도 있음
* version agnostic URLs 예제
  * `/webjars/jquery/jquery.min.js` 추가 하면 `/webjars/jquery/x.y.z/jquery.min.js` 에서 접근 가능. `x.y.z` 는 Webjar 버전
* cacue busting(무효화) 쓰는 법, content hash 를 사용하는 것이다. 예제 `<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>`

spring.resources.chain.strategy.content.enabled=true spring.resources.chain.strategy.content.paths=/**

> Thymeleaf and FreeMarker 는 `ResourceUrlEncodingFilter` 자동구성으로 런타임시에 리소스 링크가 재작성 됨. JSP 는 수동으로 해줘야 함. 
* 자바스크립트 처럼 리소스를 동적으로 로딩할때, 파일 renaming 은 옵션이 아님. 
  * "fixed" 전략은 파일이름을 변경하지 않고 URL에 정적 문자열을 추가 한다

spring.resources.chain.strategy.content.enabled=true spring.resources.chain.strategy.content.paths=/** spring.resources.chain.strategy.fixed.enabled=true spring.resources.chain.strategy.fixed.paths=/js/lib/ spring.resources.chain.strategy.fixed.version=v12

* 위의 경우 `"js/lib/"` 는 고정 버전 전략을 하고 `"/v12/js/lib/mymodule.js"` 다른 리소스는 content 를 사용 `<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>`

### 7.1.6. Welcome Page
* static 과 templated welcome 페이지를 지원 함
* `index.html` 를 먼저 찾고, 없다면 `index` template 를 찾음. 

### 7.1.7. Custom Favicon
* `favicon.ico` 를 찾음

### 7.1.8. Path Matching and Content Negotiation
* Spring MVC 는 요청 path 를 보고, 정의된 매핑(@GetMapping annotations on Controller methods) 으로 핸들러 가능
* 스프링부트는 기본적으로 suffix 패턴은 disable 다
  * `"GET /projects/spring-boot.json"` 는 `@GetMapping("/projects/spring-boot")` 와 매치 안됨 : [best practice for Spring MVC applications](https://docs.spring.io/spring/docs/5.2.3.RELEASE/spring-framework-reference/web.html#mvc-ann-requestmapping-suffix-pattern-match)
  * 옛날에 "Accept" 요청 헤더를 보낼수 없을때 유용했음
* "Accept" 욫어 헤더 못보내는 HTTP clients 를 위한 다른 방법이 있음
  * 쿼리 파라미터를 넣어라. `"GET /projects/spring-boot?format=json"` 는 `@GetMapping("/projects/spring-boot")` 와 매핑 됨

spring.mvc.contentnegotiation.favor-parameter=true

We can change the parameter name, which is "format" by default:

spring.mvc.contentnegotiation.parameter-name=myparam

We can also register additional file extensions/media types with:

spring.mvc.contentnegotiation.media-types.markdown=text/markdown

* 굳이 suffix 패턴 매칭 하려면 

spring.mvc.contentnegotiation.favor-path-extension=true spring.mvc.pathmatch.use-suffix-pattern=true

### 7.1.9. ConfigurableWebBindingInitializer
* 생략
### 7.1.10. Template Engines
* FreeMarker
* Groovy
* Thymeleaf
* Mustache
> JSP 는 가능하면 쓰지 마라
* `src/main/resources/templates` 에 있는 템플릿이 자동으로 선택된다

### 7.1.11. Error Handling
* 기본적으로 모든 에러는 `/error` 매핑되고, 몇가지 상세 정보가 포함된 JSON 응답이 나옴
* 브라우저에서는 "whitelabel" 에러 뷰가 있음
  * 기본 동작을 바꾸려면 `ErrorController` 구현
* `@ControllerAdvice` 로 컨트롤러 / 예외 타입 /  JSON 내용 등을 커스텀 가능
```java
@ControllerAdvice(basePackageClasses = AcmeController.class)
public class AcmeControllerAdvice extends ResponseEntityExceptionHandler {

    @ExceptionHandler(YourException.class)
    @ResponseBody
    ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) {
        HttpStatus status = getStatus(request);
        return new ResponseEntity<>(new CustomErrorType(status.value(), ex.getMessage()), status);
    }

    private HttpStatus getStatus(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if (statusCode == null) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
        return HttpStatus.valueOf(statusCode);
    }

}

Custom Error Pages

}

#### Mapping Error Pages outside of Spring MVC
* 생략
### 7.1.12. Spring HATEOAS
* `@EnableHypermediaSupport` 붙혀서 사용

### 7.1.13. CORS Support
* Cross-origin resource sharing (CORS) is a W3C specification 으로 [대부분의 브라우저](https://caniuse.com/#feat=cors)에서 구현 됨
* `@CrossOrigin` 으로 사용 가능
* `addCorsMappings(CorsRegistry)` 로 글로벌 설정 가능
```java
@Configuration(proxyBeanMethods = false)
public class MyConfiguration {

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/api/**");
            }
        };
    }
}

7.2. The “Spring WebFlux Framework”

}

* functional "WebFlux.fn" 에서는 라우팅 구성을 실제 요청 핸들링과 구분한다.
```java
@Configuration(proxyBeanMethods = false)
public class RoutingConfiguration {

    @Bean
    public RouterFunction<ServerResponse> monoRouterFunction(UserHandler userHandler) {
        return route(GET("/{user}").and(accept(APPLICATION_JSON)), userHandler::getUser)
                .andRoute(GET("/{user}/customers").and(accept(APPLICATION_JSON)), userHandler::getUserCustomers)
                .andRoute(DELETE("/{user}").and(accept(APPLICATION_JSON)), userHandler::deleteUser);
    }

}

@Component
public class UserHandler {

    public Mono<ServerResponse> getUser(ServerRequest request) {
        // ...
    }

    public Mono<ServerResponse> getUserCustomers(ServerRequest request) {
        // ...
    }

    public Mono<ServerResponse> deleteUser(ServerRequest request) {
        // ...
    }
}

7.2.1. Spring WebFlux Auto-configuration

7.2.2. HTTP Codecs with HttpMessageReaders and HttpMessageWriters

@Configuration(proxyBeanMethods = false) public class MyConfiguration {

@Bean
public CodecCustomizer myCodecCustomizer() {
    return codecConfigurer -> {
        // ...
    };
}

}


### 7.2.3. Static Content
* 기본적으로 /static (or /public or /resources or /META-INF/resources 에서 정적 컨텐츠 제공
* `ResourceWebHandler` 로 수정 가능
* 기본적으로 `/**` 로 매핑 되는데 바꿀 수 있음

spring.webflux.static-path-pattern=/resources/**

* `spring.resources.static-locations` 로 정적 리소스 location 수정 가능
* Webjars  컨텐츠도 제공 함
> Spring WebFlux 은 서블릿 API 에 의존하지 않아 `src/main/webapp` 디렉터리를 사용하지 않음

### 7.2.4. Template Engines
* 동적 컨텐츠를 위한 템플릿 엔진
  * FreeMarker
  * Thymeleaf
  * Mustache

### 7.2.5. Error Handling
* `WebExceptionHandler` 로 모든 에러 핸들 가능.
* 머신 클라이언트에서는 JSON 응답과 세부 에러 내용이, 브라우저 클라이언트에서는 "whitelabel" 에러로 표시 됨
* 커스텀 하는 첫번째 단계는 `ErrorAttributes` 빈을 추가하는 것
* 오류 처리 동작을 변경하기 위해서는 `ErrorWebExceptionHandler` 를 등록해라
  * 근데 `WebExceptionHandler` 는 매우 low-level 임
* 따라서 `AbstractErrorWebExceptionHandler` 로 편리하게 할 수 있음
```java
public class CustomErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {

    // Define constructor here

    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {

        return RouterFunctions
                .route(aPredicate, aHandler)
                .andRoute(anotherPredicate, anotherHandler);
    }

}

Custom Error Pages

Web Filter Order
MetricsWebFilter Ordered.HIGHEST_PRECEDENCE + 1
WebFilterChainProxy (Spring Security) -100
HttpTraceWebFilter Ordered.LOWEST_PRECEDENCE - 10
Pyohwan commented 4 years ago

7.3 JAX-RS and Jersey

}

> Jersey 는 executable archives 지원에 제한적
* ` @Components` 로 HTTP 엔드포인트 등록 해줘야 함
```java
@Component
@Path("/hello")
public class Endpoint {

    @GET
    public String message() {
        return "Hello";
    }

}

7.4. Embedded Servlet Container Support

7.4.3. The ServletWebServerApplicationContext

7.4.4. Customizing Embedded Servlet Containers

Programmatic Customization

@Component public class CustomizationBean implements WebServerFactoryCustomizer {

@Override
public void customize(ConfigurableServletWebServerFactory server) {
    server.setPort(9000);
}

}

#### Customizing ConfigurableServletWebServerFactory Directly
*  위 코드의 사용이 제한적이면, Tomcat, Jetty, UndertowServletWebServerFactory 를 직접 등록할 수 있음
```java
@Bean
public ConfigurableServletWebServerFactory webServerFactory() {
    TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
    factory.setPort(9000);
    factory.setSessionTimeout(10, TimeUnit.MINUTES);
    factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/notfound.html"));
    return factory;
}

7.4.5. JSP Limitations

7.5. Embedded Reactive Server Support

7.6. Reactive Server Resources Configuration

Pyohwan commented 4 years ago

8. RSocket

8.1. RSocket Strategies Auto-configuration

8.2. RSocket server Auto-configuration

또는, RSocket TCP or websocket 서버가 독립적으로 내장 서버로 시작한다.

spring.rsocket.server.port=9898 # the only required configuration
spring.rsocket.server.transport=tcp # you're free to configure other properties

8.3. Spring Messaging RSocket support

8.4. Calling RSocket Services with RSocketRequester

}