본문 바로가기

우아한테크코스

📋 테스트로 배우는 Spring Configuration 강의

2021-05-25글

배경 지식 강의

스프링 컨테이너와 스프링빈

컨테이너

스프링 컨테이너에 빈을 등록하기 위해서는 Configuration이 필요하다.

  • XML
  • Annotation 기반의 configuration
  • java bean configuration

XML로 Configuration 설정하는 예시

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userRepository" class="nextstep.helloworld.core.xmlConfig.UserRepository"/>

    <bean id="userService" class="nextstep.helloworld.core.xmlConfig.UserService">
        <property name="userRepository" ref="userRepository"/>
    </bean>
</beans>
  • userRepository와 userService를 빈으로 등록함
ApplicationContext context = new ClassPathXmlApplicationContext("application-config.xml");

String[] beanDefinitionNames = context.getBeanDefinitionNames();
  • ClassPathXmlApplicationContext로 해당 XML파일을 로드해온다.
  • getBeanDefinitionNames() : 등록된 빈이름을 가져온다.

java bean configuration

학습 테스트 코드

class JavaConfigTest {
    @Test
    void javaConfig() {
        ApplicationContext context = new AnnotationConfigApplicationContext(HelloApplication.class);
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        System.out.println(Arrays.toString(beanDefinitionNames));

        AuthService authService = context.getBean(AuthService.class);
        assertThat(authService).isNotNull();
    }
}
  • HelloApplication 클래스를 기반으로 빈 등록을 할 것이다.

AuthenticationPrincipalConfig

// Java-based Configuration을 하기 위한 클래스로 지정하기
@Configuration
public class AuthenticationPrincipalConfig {

    // AuthService 빈을 등록하기
    @Bean
    public AuthService authService() {
        return new AuthService();
    }

    // AuthenticationPrincipalArgumentResolver를 빈 등록하고 authService에 대한 의존성을 주입하기
    @Bean
    public AuthenticationPrincipalArgumentResolver authenticationPrincipalArgumentResolver() {
        return new AuthenticationPrincipalArgumentResolver(authService());
    }
}
  • @Configuration : 메타 데이터를 설정할 수 있는 클래스가 된다.
  • @Bean 과 특정 객체를 반환하는 메서드로 해당 객체를 빈으로 등록할 수 있다.
  • 빈들의 의존성 또한 직접 맺어줄 수 있다.

@Configuration 클래스도 빈 등록이 되나요?

🙆‍♀️

@Configuration 클래스의 메서드 순서와 빈 등록 순서는 상관이 없나요?

🙆‍♀️

@Test
void useSpringBean() {
    ApplicationContext context = new AnnotationConfigApplicationContext(HelloApplication.class);
    String[] beanDefinitionNames = context.getBeanDefinitionNames();
    System.out.println(Arrays.toString(beanDefinitionNames));

    AuthService authService = context.getBean(AuthService.class); // 싱글톤 // new AuthService()이니까
    AuthenticationPrincipalArgumentResolver resolver = context.getBean(AuthenticationPrincipalArgumentResolver.class);
    assertThat(resolver.getAuthService()).isEqualTo(authService);
}

resolver에 있는 AuthService 객체와 빈으로 등록된 AuthService가 같은 객체인가?

🙆‍♀️


외부 파일의 값을 이용하기

properties 파일 접근

// Java-based Configuration을 하기 위한 클래스로 지정하기
// application.properties 파일을 활용하기 위한 설정 추가하기
@Configuration
@PropertySource("classpath:application.properties")
public class PropertySourceConfig {

    private final Environment env;

    public PropertySourceConfig(Environment env) {
        this.env = env;
    }

    // application.properties의 security-jwt-token-secret-key 값을 활용하여 JwtTokenKeyProvider를 빈으로 등록하기
    @Bean
    public JwtTokenKeyProvider jwtTokenKeyProvider() {
        return new JwtTokenKeyProvider("security-jwt-token-secret-key");
    }
}
  • Environment 라는 필드가 있다.
@Test
void key() {
    ApplicationContext context = new AnnotationConfigApplicationContext(PropertySourceConfig.class);
    String[] beanDefinitionNames = context.getBeanDefinitionNames();
    System.out.println(Arrays.toString(beanDefinitionNames));

    JwtTokenKeyProvider jwtTokenKeyProvider = context.getBean(JwtTokenKeyProvider.class);
    assertThat(jwtTokenKeyProvider.getSecretKey()).isEqualTo("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIiLCJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE1MTYyMzkwMjJ9.ih1aovtQShabQ7l0cINw4k1fagApg3qLWiB8Kt59Lno");
}
  • PropertySourceConfig 클래스로 컨텍스트를 만든다.
  • 사실 이 properties를 객체로 만들어 접근할 수도 있다.

@Value 주입

// 컴포넌트 스캔을 통한 빈 등록
@Component
public class JwtTokenExpireProvider {
    // application.properties의 security-jwt-token-expire-length 값을 활용하여 validityInMilliseconds값 초기화 하기
    private long validityInMilliseconds;

    public JwtTokenExpireProvider(@Value("${security-jwt-token-expire-length}") long validityInMilliseconds) {
        this.validityInMilliseconds = validityInMilliseconds;
    }

    public long getValidityInMilliseconds() {
        return validityInMilliseconds;
    }
}
// Java-based Configuration을 하기 위한 클래스로 지정하기
// application.properties 파일을 활용하기 위한 설정 추가하기
// nextstep.helloworld.core.environment 내에 있는 스프링 빈을 스캔하기
@Configuration
@PropertySource("classpath:application.properties")
@ComponentScan("nextstep.helloworld.core.environment")
public class ValueConfig {
}

환경에 따라 properties 설정이 가능한가요?

🙆‍♀️ 환경에 맞게 deploy하기 - profile


스프링 컨테이너 설정 방법 히스토리

컨테이너 설정을 할 때는 메타정보를 통해 이루어진다.

맨 처음에는 XML기반으로 진행되었다.
이러면 프로덕션 코드와 의존 관계, 빈 등록 정보를 분리할 수 있었다.

이후 어노테이션 기반이 등장하면서 XML과 혼용하여 사용하였다.
등록할 빈들을 어노테이션으로 관리하였다.

Spring 3.0부터는 Java Bean 기반으로 이루어졌다.
XML로 관리하던 내용들을 Bean 등록을 통해 관리하였다.


Auto Configuration?

  • jar dependency 기반으로 스프링 애플리케이션을 자동으로 설정해준다.

별다른 설정을 하지 않았는데도, DB 등에 관련한 것들을 마음 껏 쓸 수 있었다.

그 이유는 @SpringBootApplication 에 있는 @EnableAutoConfiguration 덕분이다.

컨텍스트를 로드하면 이정도의 AutoConfiguration들이 등록된다.

ex) jdbcTemplate을 생성해주지 않았는데도 자동으로 주입된다.

빈으로 등록되지 않은 클래스를 사용하면 컴파일 에러가 떠야하는데 나지 않는 이유는?

@ConditionalOnClass 덕분이다.

  • DataSource, JdbcTemplate이 로드가 되면 동작한다.

H2ConsoleAutoConfiguration

h2 DB설정을 해주지 않았는데도 h2와 관련된 설정이 자동으로 된다.

이것도 이미 등록되어있기 때문에 가능하다.