관리 메뉴

제뉴어리의 모든것

[Section2] [Spring Core] Spring Framework의 핵심 개념 - 자바 기반 컨테이너 설정 본문

코드스테이츠/정리 블로깅

[Section2] [Spring Core] Spring Framework의 핵심 개념 - 자바 기반 컨테이너 설정

제뉴어리맨 2022. 8. 12. 14:50

@Bean 과 @Configuration

  • @Configuration : 
    스프링 컨테이너의 구성정보임을 알리는 애노테이션.
    클래스 내부에 @Bean이 붙은 메소드들의 반환값들이 싱글톤이 지켜지도록 해줌. (메소드에 @Bean만 붙어도 빈등록되지만, 포함하는 클래스에 @Configuration이 안 붙어 있으면 싱글톤 보장 안됨)
    @Configuration이 붙은 클래스 또한 스프링빈으로 등록된다.


  • @Bean :
    메소드에 붙여서 리턴되는 타입의 객체가 스프링빈으로 등록되야됨을 알리는 애노테이션.


구성정보를 사용하여 스프링 컨테이너 생성

  • 애노테이션을 이용 (@Configuration)

    - TestConfig 클래스
@Configuration
class TestConfig{
    
    @Bean
    UserRepository userRepository(){
        System.out.println("TestConfig.userRepository 호출");
        return new UserRepositoryImpl();
    }

    @Bean
    DiscountInfo discountInfo() {
        System.out.println("TestConfig.discountInfo  호출");
        return new RateDiscountInfo();
    }

    @Bean
    UserService userService(){
        System.out.println("TestConfig.userService  호출");
        return new UserServiceImpl(userRepository());
    }

    @Bean
    OrderService orderService() {
        System.out.println("TestConfig.orderService  호출");
        return new OrderServiceImpl(discountInfo(), userRepository());
    }
}

 

- 스프링 컨테이너 생성 부분

package com.spring_ex01.cmarket.springContainer.annotation;

import com.spring_ex01.cmarket.discount.DiscountInfo;
import com.spring_ex01.cmarket.discount.RateDiscountInfo;
import com.spring_ex01.cmarket.order.OrderService;
import com.spring_ex01.cmarket.order.OrderServiceImpl;
import com.spring_ex01.cmarket.user.UserRepository;
import com.spring_ex01.cmarket.user.UserRepositoryImpl;
import com.spring_ex01.cmarket.user.UserService;
import com.spring_ex01.cmarket.user.UserServiceImpl;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

public class annotationConfigTest {

    @Test
    @DisplayName("@Configuration을 이용한 스프링 컨테이너 생성")
    void testConfigurationSpringContainer() {
        ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);

        String[] beanDefinitionNames = ac.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }
}

 

  • 빈 등록할 클래스 자체를 넣는 방법
ApplicationContext ac = new AnnotationConfigApplicationContext(OrderServiceImpl.class, UserServiceImpl.class, UserRepositoryImpl.class, OrderServiceImpl.class, RateDiscountInfo.class);

 

 

 

 

  • xml 파일을 이용

    - xmlAppConfig.xml (xml 파일)
<?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">


    <!-- services -->

    <bean id="orderService" class="com.spring_ex01.cmarket.order.OrderServiceImpl">
        <constructor-arg name="discountInfo" ref="discountInfo"/>
        <constructor-arg name="userRepository" ref="userRepository"/>

    </bean>
    <bean id="discountInfo" class="com.spring_ex01.cmarket.discount.RateDiscountInfo"/>
    <bean id="userRepository" class="com.spring_ex01.cmarket.user.UserRepositoryImpl"/>

    <bean id="userService" class="com.spring_ex01.cmarket.user.UserServiceImpl">
        <constructor-arg name="userRepository" ref="userRepository"/>
    </bean>

    <!-- more bean definitions for services go here -->
</beans>

 

- 스프링 컨테이너 생성 부분

package com.spring_ex01.cmarket.springContainer.xml;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class xmlConfigTest {

    @Test
    @DisplayName("xml 구성정보로 스프링 컨테이너 생성")
    void testXmlSpringContainer() {

        ApplicationContext ac = new GenericXmlApplicationContext("xmlAppConfig.xml");

        String[] beanDefinitionNames = ac.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }
}

 

 

빈 초기화 메소드, 소멸 메소드

 

  • 빈 초기화 메소드
    스프링 컨테이너가 빈을 등록하기 위해 빈을 생성하고 빈으로써 필요한 모든 속성들을 세팅하고 나서 바로 호출되는 초기화를 위한 메소드이다.

    - java 코드로의 방법
    1. 빈으로 등록될 자바 클래스에 InitializingBean 인터페이스를 구현하도록 한다.
    2. InitializingBean 인터페이스의 구현 메소드인 
    public void afterPropertiesSet() throws Exception 를 구현한다.

    사용코드
@Configuration
class TestConfig implements InitializingBean{ //인터페이스 implements


    public void afterPropertiesSet() throws Exception { //초기화 메소드
        System.out.println("스프링빈으로써 필요한 속성 다 세팅하고 돌아가는 콜백 메소드");
    }

    @Bean
    UserRepository userRepository(){
        System.out.println("TestConfig.userRepository 호출");
        return new UserRepositoryImpl();
    }

    @Bean
    DiscountInfo discountInfo() {
        System.out.println("TestConfig.discountInfo  호출");
        return new RateDiscountInfo();
    }

    @Bean
    UserService userService(){
        System.out.println("TestConfig.userService  호출");
        return new UserServiceImpl(userRepository());
    }

    @Bean
    OrderService orderService() {
        System.out.println("TestConfig.orderService  호출");
        return new OrderServiceImpl(discountInfo(), userRepository());
    }
}

 

TestConfig 클래스의 객체를 빈으로 등록하면서 속성값을 모두 세팅한 뒤 afterPropertiesSet 메소드가 호출 된다. 

 

참고 : @Configuration 이 붙은 클래스는 스프링빈의 구성정보 역할을 하고 팩토리빈의 역할을 하지만, 그 해당 클래스 또한 빈으로써 등록되는 클래스이다.

 

- XML 로의 방법

위에 xml로 빈 등록 방법 내용을 참고할것.

<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>

빈으로 등록할 <bean> 안에 init-method 속성으로 초기화 메소드를 지정하면 된다.

  public void init() {
        // do some initialization work
    }


- @PostConstruct 방법

[추후 정리]

 

  • 빈 소멸자 메소드

    - java 코드로의 방법

    org.springframework.beans.factory.DisposableBean 인터페이스를 구현하면 빈을 포함하는 컨테이너가 파괴될 때 빈이 콜백을 받을 수 있다.

     1. 소멸 메소드를 호출시키고 싶은 클래스에 DisposableBean인터페이스를 implements한다

    class TestConfig implements InitializingBean, DisposableBean

     2. 인터페이스의 구현 메소드인 public void destroy() throws Exception 를 구현한다.

    @Override 
    public void destroy() throws Exception 

    System.out.println("빈 파괴시 호출되는 소멸자 메소드!"); 
    }


     3. ApplicationContext (스프링 컨테이너)를 명시적으로 닫아줘야한다.

    ((ConfigurableApplicationContext)ac).close(); 
    //ApplicationContext 명시적 닫기 (명시적으로 닫아야 소멸 메소드들 호출됨)

    출처 : https://stackoverflow.com/questions/14423980/how-to-close-a-spring-applicationcontext

 


- XML로의 방법

1. <bean> 에 destroy-method 속성 추가

<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="destroy"/>

 

2. 속석에 기입한 동일한 메소드를 작성 
void destroy() { 

System.out.println("OrderServiceImpl destroy 호출!"); 

}

 

 

참조 : https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-lifecycle-initializingbean

 

Core Technologies

In the preceding scenario, using @Autowired works well and provides the desired modularity, but determining exactly where the autowired bean definitions are declared is still somewhat ambiguous. For example, as a developer looking at ServiceConfig, how do

docs.spring.io



- @PreDestroy 방법

[추후 정리]

 

 

@Import 사용 방법

@Configuration
public class ConfigA {

    @Bean
    public A a() {
        return new A();
    }
}

@Configuration
@Import(ConfigA.class)
public class ConfigB {

    @Bean
    public B b() {
        return new B();
    }
}

ApplicationContext를 인스턴스화할 때 ConfigA.class와 ConfigB.class 모두 지정하는 대신 ConfigB만 제공하면 됨.

 

즉, @Configuration으로 지정된 클래스가 많을때, @Import를 사용하여 ApplicationContext의 매개변수를 단순화 시킬 수 있다.