본문 바로가기

공부 자료/Spring

[Spring Core] Java 기반 컨테이너 설정

[@Bean]

: 메서드-레벨 애너테이션으로 <bean/>에서 제공하는 일부 속성을 지원함

: @Configuration-annoted 또는 @Component-annoted 클래스에서 사용이 가능함

: 대상 빈이 하나의 생성자만 정의하는 경우 @Autowired 지정할 필요가 없음

 

빈 선언)

: 메서드에 @Bean 애너테이션을 추가해 Bean으로 선언 가능

: 빈 정의가 있는 인터페이스를 구현해 bean configuration 설정도 가능

(' implements 빈_정의가_있는_인터페이스명 '을 클래스에 붙여 설정 가능)

(1) 
@Configuration
public class AppConfig {

	//빈 선언
    @Bean
    public TransferServiceImpl transferService() {
        return new TransferServiceImpl();
    }
}

(2)
public interface BaseConfig {

    @Bean
    default TransferServiceImpl transferService() {
        return new TransferServiceImpl();
    }
}

//빈을 가진 인터페이스를 구현해 configuration 설정
@Configuration
public class AppConfig implements BaseConfig {

}

 

 

빈 의존성)

: @Bean 애너테이션이 추가된 메서드는 빈을 구축하는데 필요한 의존성을 나타내는데 매개 변수를 사용할 수 있음

(메서드 매개변수를 사용함으로서 종속성 구체화 가능)

* 메서드명() >> 메서드명(매개변수) 이용 가능

@Configuration
public class AppConfig {

    @Bean
    public TransferService transferService(AccountRepository accountRepository) {
        return new TransferServiceImpl(accountRepository);
    }
}

 

 

수명주기 콜백 수신)

: @Bean은 일반 수명 주기 콜백을 지원하고 @PostConstruct 및 주석 사용이 가능함

public class BeanOne {
    public void init() {
        // initialization logic
    }
}
public class BeanTwo {
    public void cleanup() {
        // destruction logic
    }
}

@Configuration
public class AppConfig {
    @Bean(initMethod = "init") //임의의 초기화 메서드 initMethod 지정 지원
    public BeanOne beanOne() {
        return new BeanOne();
    }
    @Bean(destroyMethod = "cleanup") //임의의 소멸 콜백 메서드 destroyMethod 지정 지원
    public BeanTwo beanTwo() {
        return new BeanTwo();
    }
}

 

 

[@Configuration]

: 해당 객체가 bean definitions의 소스임을 나타내는 애너테이션

: @Bean-annoted 메서드를 통해 bean을 선언

: 클래스의 @Bean 메서드에 대한 호출을 사용해 bean 사이의 의존성 정의 가능

 

Bean 사이 의존성 주입)

: 빈간 의존성을 주입하는 것은 클래스 @Bean 내에서 메서드가 선언된 경우만 작동하며 일반 @Component 클래스를 사용해 선언은 불가능함

@Configuration
public class AppConfig {

    @Bean
    public BeanOne beanOne() {
        return new BeanOne(beanTwo()); //beanTwo와 의존 관계
    }

    @Bean
    public BeanTwo beanTwo() {
        return new BeanTwo();
    }
}

 

 

Java를 기반으로 설정된 환경에서 내부적으로 작동하는 방식에 대한 정보)

@Configuration
public class AppConfig {

    @Bean
    public ClientService clientService1() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao()); //(1) clientDao() 메서드 호출
        return clientService;
    }

    @Bean
    public ClientService clientService2() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao()); //(2) clientDao() 메서드 호출
        return clientService;
    }

    @Bean
    public ClientDao clientDao() { //(2) ClientDatoIml 새 인스턴스를 만들고 반환
        return new ClientDaoImpl();
    }    
}

문제점 -  clientService1,2에서 clientDao 메서드를 모두 호출하기에 각 인스턴스가 존재해야 하지만 빈은 기본 싱글톤 범위를 가짐

: 문제점 해결을 위해 하위 클래스의 하위 메서드는 상위 메서드를 호출하고 새 인스턴스를 만들기 전 먼저 컨테이너에 캐시된(범위 지정) bean이 있는지 확인이 필요

 

 

Java 기반 구성 작성)

: Spring의 Java 기반 구성 기능을 사용할 경우 주석 사용이 가능하므로 구성의 복잡성을 줄일 수 있음

 

@import 애너테이션 사용

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

@Configuration
@Import(ConfigA.class) //다른 구성 클래스에서 @Bean definitions를 가져올 수 있음
public class ConfigB {
    @Bean
    public B b() {
        return new B();
    }
}

: 컨텍스트를 인스턴스화할 때 둘 다 지정할 필요 없이 ConfigA.class 예제와 같이 명시적으로 제공만 하면 됨

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);
    // now both beans A and B will be available...
    A a = ctx.getBean(A.class);
    B b = ctx.getBean(B.class);
}

: @Configuration 접근 방식은 생성하는 동안 잠재적으로 많은 수의 클래스를 기억할 필요 없이 하나의 클래스만 처리하면 되기 때문에 컨테이너 인스턴스화를 단순화

 

가져온 @Bean 정의에 의존성 주입

: 클래스 사용시 @Configuration Java 컴파일러는 다른 빈에 대한 참조가 유효한 Java 구문이어야 한다는 점에서 구성 모델에 제약을 둠

@Configuration
public class ServiceConfig {
    @Bean
    public TransferService transferService(AccountRepository accountRepository) {
        return new TransferServiceImpl(accountRepository);
    }
}

@Configuration
public class RepositoryConfig {
    @Bean
    public AccountRepository accountRepository(DataSource dataSource) {
        return new JdbcAccountRepository(dataSource);
    }
}

@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {
    @Bean
    public DataSource dataSource() {
        // return new DataSource
    }
}

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
    // everything wires up across configuration classes...
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");
}

<해결방법>

: @Bean은 종속성을 설명하는 임의의 수의 매개변수를 가질 수 있기에 빈 의존성을 설명하는 임의 개수 파라미터를 가질 수 있음

: @Autowired 및 @Value 주입 및 다른 bean과 동일한 기능을 사용할 수 있음

@Configuration
public class ServiceConfig {

    @Autowired
    private AccountRepository accountRepository;
    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl(accountRepository);
    }
}

@Configuration
public class RepositoryConfig {

    private final DataSource dataSource;

    public RepositoryConfig(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(dataSource);
    }
}

@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {
    @Bean
    public DataSource dataSource() {
        // return new DataSource
    }
}

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
    // everything wires up across configuration classes...
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");
}