<현재 톰캣 서버 포트 번호 8080 으로 자동으로 실행되는데

사용자 구성정보에 포트 번호 9090 으로 변경하기>

 

 

 

1. WebServerConfiguration class 만들기

 

@Configuration(proxyBeanMethods = false)
public class WebServerConfiguration {
    @Bean
    ServletWebServerFactory customerWebServerfactory(){
        TomcatServletWebServerFactory serverFactory = new TomcatServletWebServerFactory();
        serverFactory.setPort(9090);
        return serverFactory;
    }
}

setPort 사용하여 포트 9090으로 변경

proxyBeanMethods = false

특별하게 bin 사이에 상호 메소드 호출을 통해 의존관계 주입을 넣을게 아니라면
proxyBeanMethods를 fasle로 설정

 

 

2. 테스트 해보면 오류

 

아래와 같이 멀티풀 오류가 뜸

Unable to start web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to multiple ServletWebServerFactory beans : customerWebServerfactory,tomcatWebServerFactory

 

* 사용자 구성정보와 자동구성정보에 똑같은 Bean이 있다면

사용자 구성정보에 있는 Bean이 우선적 그 후 자동구성정보가 실행되는데

 

사용자 구성정보에 적힌 Bean에 이미 Tomcat이 있어 자동구성정보도 실행되면서 에러 발생

 

 

 

3. 해결방안

 

자동정보인 tomcatWebServerFactory에 Bean에 아래 어노테이션 추가

 

@ConditionalOnMissingBean

 

사용자 구성 정보에 개발자가 이거랑 같은 타입의 빈을 구성정보로 만들었는지 확인 후

없을 경우에 해당 빈 생성해주는 어노테이션

 

 

 

4. 다시 테스트 해보면 9090 포트로 Tomcat 서버 확인 가능

 

 

1. 먼저 true일 경우와 false 일 경우를 테스트

 

 

2. 성공,실패할 경우 클래스

 

-성공할 경우 클래스 Config1

@Configuration
@Conditional(TrueConditon.class)
static class Config1{
    @Bean
    MyBean myBean(){
        return new MyBean();
    }
}

@Conditional에 TrueCondition.class엘리먼트 구현

true일 경우 등록시킬 MyBean 생성 - 등록되는지만 확인할 것으로 bean에 내용은 없다

static class MyBean{}

 

-실패할 경우 클래스 Config2

@Configuration
@Conditional(FalseConditon.class)
static class Config2{
    @Bean
    MyBean myBean(){
        return new MyBean();
    }
}

@Conditional에 FalseCondition.class엘리먼트 구현

 

 

3. 각각 condition이라는 인터페이스를 구현한 클래스 코딩

static class TrueConditon implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return true;
    }
}

static class FalseConditon implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return false;
    }
}

TrueCondition은 true 반환,  FalseCondition은 false 반환

 

4. 테스트

 

- true일 경우 테스트 성공

@Test
void conditional() {
    //true
    AnnotationConfigApplicationContext ac1 = new AnnotationConfigApplicationContext();
    ac1.register(Config1.class);
    ac1.refresh();

    MyBean bean = ac1.getBean(MyBean.class);
}

 

 

-false 일 경우 테스트 실패

//false
AnnotationConfigApplicationContext ac2 = new AnnotationConfigApplicationContext();
ac2.register(Config2.class);
ac2.refresh();

MyBean bean2 = ac2.getBean(MyBean.class);

 

 

true 랑 똑같이 코딩하여도 아래와 같은 bean 찾을 수 없다는 에러 발생

org.springframework.beans.factory.NoSuchBeanDefinitionException

 

bean이 생성되지 않았으므로 당연한 결과지만 예외처리 될 수 있게 코딩 변경

 

//false
ApplicationContextRunner contextRunner = new ApplicationContextRunner();
contextRunner.withUserConfiguration(Config2.class)
        .run(context -> {
            //Assertions.assertThat(context).hasSingleBean(MyBean.class); 갖고있어야 성공
            Assertions.assertThat(context).doesNotHaveBean(MyBean.class);   // 갖고있지 않아야 성공
            Assertions.assertThat(context).doesNotHaveBean(Config2.class);
        });

 

ApplicationContextRunner : 예외처리

 

 

 

 

+ 더 나아가 condition을 메타 어노테이션으로 사용하기

아예 어노테이션으로 만들어보기

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Conditional(TrueConditon.class)
@interface TrueConditonal{}

@TrueConditonal
@Configuration
//@Conditional(TrueConditon.class)
static class Config1{
    @Bean
    MyBean myBean(){
        return new MyBean();
    }
}

 

기존에 사용하던 @Coditonal 지우고 성공할 경우 어노테이션인 @TrueConditional을 만들어줌

 

 

+ Condition의 조건을 결정한 로직 만들기 - @BooleanConditional

AnnotatedTypeMetadata을 사용하여 true, false 두가지 다 적용할 수 있고

그 조건을 어노테이션 엘리먼트로 지정하는 방법

 

Boolen으로 true,false 값 전달

 

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Conditional(BoolenConditon.class)
@interface BoolenConditonal{
    boolean value();
}

@Configuration
@BoolenConditonal(true)
//@Conditional(TrueConditon.class)
static class Config1{
    @Bean
    MyBean myBean(){
        return new MyBean();
    }
}

@Configuration
@BoolenConditonal(false)
static class Config2{
    @Bean
    MyBean myBean(){
        return new MyBean();
    }
}

 

 

static class BoolenConditon implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
       Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(BoolenConditonal.class.getName());
        Boolean value = (Boolean)annotationAttributes.get("value");
        return value;
    }
}

 

어노테이션 메타 정보(@BoolenConditonal(true)에 있는 true, false)를 다 가져와 어떤 값을 넣었는지 읽어여함

metadata.getAnnotationAttributes(BoolenConditonal.class.getName());

가져온 후 Boolean값으로 value 값 읽어야함

Boolean value = (Boolean)annotationAttributes.get("value");

 

 

최종적으로 코딩이 짧아지고 간결해짐

 

 

'SpringBoot > 프로젝트' 카테고리의 다른 글

Environment properties 적용  (2) 2023.12.06
자동 구성 정보 대체하기  (0) 2023.11.30
[Spring Boot] jetty 서버 구성 추가하기  (0) 2023.11.29
Test Code 작성  (0) 2023.11.27
DI를 이용한 Decorator  (2) 2023.11.27

1.META-INF에 Jetty 추가

이 구성들은 자동 구성 방식에 의해 AutoConfiguration의 imports 파일에 기술

 

2. 잘 올라갔는지 grale에서 확인

 

3. 톰캣과 마찬가지로 config 파일 생성

Tomcat Seblet Container 말고 Jetty Seblet Container 사용할 수 있도록 스타터 추가 후

Configuration 클래스를 만들어 Jetty 사용하는 웹서버 Factory bean이 만들어지도록 등록

 

이 구성들은 자동 구성 방식에 의해 AutoConfiguration의 imports 파일에 기술

 

톰캣과 두 개 이름이 동일할 경우 충동한다고 스프링 컨테이너가 초기화할 때 에러 발생 할 수 있으므로 이름 구분해주기

TomcatWebServerConfig 파일에서도 이름 지정

@Bean("tomcatWebServerFactory")

 

4. 설정만 해줬을때는 아래와 같은 오류 발생

2개 있다고 안됨

Unable to start ServletWebServerApplicationContext due to multiple ServletWebServerFactory beans : tomcatWebServerFactory,JettyWebServerFactory

 

 조건을 달아서 어떤 구성 정보를 활용할건지 알려줘야함

 

5.JettyWebServerConfig에 @Condition 추가

@Conditional(JettyWebServerConfig.JettyCondition.class)
static public class JettyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return false;    // true 반환하면 이 빈 구성 정보를 사용하겠다는 뜻, tomcat 말고 jetty 사용
    }
}

 

true 반환하면 이 빈 구성 정보를 사용하겠다는 뜻

jetty 사용하고 싶으면JettyWebServerConfig에서 반환값 true 설정해주고

나머지 TomcatWebServerConfig은 false 반환

 

Tomcat을 사용하고 싶으면 반대로 설정해주면 됨

 

 

6. 실행하고 console 확인

'SpringBoot > 프로젝트' 카테고리의 다른 글

자동 구성 정보 대체하기  (0) 2023.11.30
@Conditional 학습 테스트  (0) 2023.11.30
Test Code 작성  (0) 2023.11.27
DI를 이용한 Decorator  (2) 2023.11.27
섹션2. 스프링 부트 시작하기  (0) 2023.11.21

Test Code 란 ?

의도된 대로 정확히 작동하는지 검증하는 절차

 

  • 작성한 코드가 제대로된 기능을 수행하는지 검증
  • 빠른 시간에 테스트 가능

 

성공하는 경우 Test

@Test
void helloApi(){
    TestRestTemplate rest = new TestRestTemplate();

    ResponseEntity<String> res =
            rest.getForEntity("http://localhost:8080/hello?name={name}", String.class, "Spring");


    assertThat(res.getStatusCode()).isEqualTo(HttpStatus.OK);
    assertThat(res.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE)).startsWith(MediaType.TEXT_PLAIN_VALUE);
    assertThat(res.getBody()).isEqualTo("*HelloSpring*");
}

 

TestRestTemplate rest = new TestRestTemplate();
  • RestTemplate : 오류나면 예외를 던짐
  • TestRestTemplate : 오류나도 해당 결과값을 보내줌

 응답 검증 3가지

assertThat(res.getStatusCode()).isEqualTo(HttpStatus.OK);

  1.statis code 200 ,enum값과 일치하는 지 학인 -> HttpStatus.OK
        static + import 단축키 alt + Enter

 

assertThat(res.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE)).startsWith(MediaType.TEXT_PLAIN_VALUE);


  2. header(content-type) text/plain

 

assertThat(res.getBody()).isEqualTo("*HelloSpring*");


  3. body Hello Spring

 

 

실패하는 경우


//실패하는 테스트
@Test
void failHelloApi(){
    TestRestTemplate rest = new TestRestTemplate();

    ResponseEntity<String> res =
            rest.getForEntity("http://localhost:8080/hello?name=", String.class);

    assertThat(res.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
}

 

'SpringBoot > 프로젝트' 카테고리의 다른 글

자동 구성 정보 대체하기  (0) 2023.11.30
@Conditional 학습 테스트  (0) 2023.11.30
[Spring Boot] jetty 서버 구성 추가하기  (0) 2023.11.29
DI를 이용한 Decorator  (2) 2023.11.27
섹션2. 스프링 부트 시작하기  (0) 2023.11.21

+ Recent posts