Framework/Spring

[Spring] Annotation Configuration은 어떻게 할까?

KOOCCI 2022. 8. 1. 21:09

목표 : Spring의 Anntation 설정을 할 수 있다.


 이번엔 계속 XML에서 설정해온 Configuration 정보들을 Annotation으로 어떻게 할 수 있을지 알아볼 예정이다.

해당 링크를 중심으로 하나씩 알아가 보도록 하자.

 

Annotation-based Container Configuration

먼저 Annotation이 XML보다 Spring configuration하는 것에 있어서 더 좋은가? 에 대한 질문으로 시작한다.

답은 역시나 "상황에 따라 다르다" 이다.

 

XML 방식은 소스 코드 외, XML만 보고도 설정을 알 수 있다. 즉, 설정과 소스 코드의 분리가 가능하다.

그러나, 프로젝트가 커질수록 XML 관리가 복잡해지기 시작한다.

Annotation은  더 짧고 간결한 구성이 가능하지만 소스 코드와 설정의 분리가 애매해진다.

 

context namespace

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

    <context:annotation-config/>

</beans>

context라는 namespace를 넣어준 것에 집중하자.

그리고, <context:annotation-config/> 를 적용하며, 이제 Annotation을 통해 설정할 수 있도록 변경되었다.

 

@Autowired

Autowired는 Bean과 Bean간의 관계에 대해, Spring Container가 자동으로 연결해주는 것이다.

그 때, Bean의 이름, Bean의 Type등으로 관계를 설정할 수 있다.

public class MovieRecommender {

    private final CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

그 예시를 보자.

MovieRecommender의 생성자에 Autowired Annotation을 사용하였다.

CustomerPreferenceDao와 관계를 설정해준 것이다.

이는 이전 XML 문법의 다음과 같다.

<bean id="customerPreferenceDao" class="kr.co.test.CustomerPreferenceDao"/>
  
<bean id="movieRecommender" class="kr.co.test.MovieRecommender">
    <constructor-arg ref="customerPreferenceDao" />
</bean>

다만, Spring 버전이 올라가면서, 생성자 주입 시, 자동 Autowired 가 될 수 있고, 필드에 쓰는 경우도 많다.

public class MovieRecommender { 
	private final CustomerPreferenceDao customerPreferenceDao;
    // Autowired 생략 가능
	public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
		this.customerPreferenceDao = customerPreferenceDao;
	}
}
@RequiredArgsConstructor // Lombok 활용
public class MovieRecommender { 
    private final CustomerPreferenceDao customerPreferenceDao;
}
public class MovieRecommender { 
    @Autowired // 필드에 설정 가능
    private final CustomerPreferenceDao customerPreferenceDao;
}

 

@Primary

Autowired를 할 때, 이름 혹은 타입 등으로 설정할 수 있는데, 그 중, 타입으로 연결했더니 중복인 경우, 어떤 것을 Autowired를 할지 모를 경우가 있다. 그 때 활용할 수 있는 Annotation이다.

@Configuration
public class MovieConfiguration {

    @Bean
    @Primary
    public MovieCatalog firstMovieCatalog() { ... }

    @Bean
    public MovieCatalog secondMovieCatalog() { ... }

    // ...
}

@Configuration : Bean 설정으로 등록되면서, Spring 설정과 관련있는 Bean이라는 것을 알려주는 Annotation

@Bean : XML에서 Bean 설정하는 것 대신, Java Annotation을 통해 Bean 설정할 수 있게 도와주는 Annotation

 

위 예시를 보았을 때, @Primary가 없다고 가정하면 에러가 나오게 된다. (No Unique Bean)

 

@Qualifiers 

public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(@Qualifier("main") MovieCatalog movieCatalog,
            CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

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

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog">
        <qualifier value="main"/> 

        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <qualifier value="action"/> 

        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>

같은 타입의 Bean을 만들 때, Quialifier는 Autowired 시, Qualifier 라는 Annotation과 Value를 넣으면, 선택하여 실행시킬 수 있다.

 

@Resource

@Autowired와 같은 역할을 한다고 보면된다.

약간의 차이는 Bean의 이름을 통해 주입하며, 생성자에 적용할 수 없다.

 

@Value

보통 Application에서 XML 외에 application.properties 라는 파일을 만들어, property를 읽어들인다.

@Configuration
@PropertySource("classpath:application.properties")
public class AppConfig { }

위와 같이, Property를 연결하는 구문을 만들고, application.properties를 만들어준다.

그리고, Key=Value 형태로 값을 저장하면, 해당 값을 가져와 쓸 수 있다.

 

@PostConstruct, @PreDestroy

LifeCycle과 관련된 Annotation이다.

@PostConstruct 는 Bean이 생성되기 전에 실행되는 함수이다.

@PreDestroy는 Bean의 소멸을 앞두고 실행되는 함수이다. 단, context.close()가 필요하다.

 

WrapUp

Annotation이라는 것이 사용자의 편의를 생각해, 만들어지다보니 암기해야할 것처럼 느껴질 수 있다.

다만, 사용을 하다보면 자연스럽게 알게되고, 자주 쓰다보면 당연스레 써지게 되기도 한다.

다만, 이 Annotation의 역할과 상황을 잘 기억하여 쓸 수 있도록 하자.