1.12.4 @Configuration 어노테이션 by ks

@Configuration은 오브젝트가 빈 정의의 소스인 것을 가리키는 클래스 레벨의 어노테이션이다. @Configuration 클래스는 public @Bean 어노테이트된 메소드를 통해 빈을 선언한다. @Configuration 클래스에 @Bean 메소드를 부르는 것은 또한 내부 빈 의존성을 정의하기 위해 사용될 수 있다. Basic Concepts: @Bean and @Configuratio을 봐

내부 빈 의존성 주입

빈이 다른 하나에 의존성을 가질 때, 의존성은 하나의 빈 메소드가 다른 것을 부르는 것 처럼 간단하다.

@Configuration
public class AppConfig {

    @Bean
    public BeanOne beanOne() {
        return new BeanOne(beanTwo());
    }

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

beanOne은 생성자주입을 통해 beanTwo를 참조한다.

내부 빈 의존성을 선언하는 이 방법은 오로지 @Bean 메소드가 @Configuration클래스안에서 선언되어있을 떄 동작한다. 단순히 @Component 클래스를 사용해서 이너 빈을 선언할 수 없다.

lookup 메소드 주입

일찍이 얘기했던 것처럼, lookup method injection 은 드물게 사용하는 미리 선언된 기능이다. 이는 싱글톤 스코프의 빈이 프로토타입 스코프 빈 의존성을 가지는 경우에 유용하다. configuration의 이 타입을 위해 자바를 사용하는 것은 이 패턴을 확장하는 본 의미를 제공한다. 아래는 lookup메소드 주입을 사용하는 방법을 나타낸 예제다.

참조 : https://javaslave.tistory.com/40

싱글톤 빈이 프로로타입 스코프의 빈을 참조할 때, 프로토타입 빈이 싱글톤처럼 관리가 되니까 이걸 해결하기 위한 방법으로, <lookup-method name="" bean=""/>을 사용해서도 가능하다. 근데 성능저하를 발생해서 권장하지도 않고, 실제로도 잘 안쓰이는 방식.

public abstract class CommandManager {
    public Object process(Object commandState) {
        // grab a new instance of the appropriate Command interface
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    // okay... but where is the implementation of this method?
    //이거에 대한 답은, 
    protected abstract Command createCommand();
}

자바 configuration을 사용해서 추상 createCommand() 메소드가 새로운 command(prototype) 오브젝트를 찾는 방법을 오버라이드하는 CommandManager의 서브클래스를 생성할 수 있다.

---

@Bean
@Scope("prototype")
public AsyncCommand asyncCommand() {
    AsyncCommand command = new AsyncCommand();
    // inject dependencies here as required
    return command;
}

@Bean
public CommandManager commandManager() {
    // return new anonymous implementation of CommandManager with createCommand()
    // overridden to return a new prototype Command object
    
    //여기서 확장 구현함.
    return new CommandManager() {
        protected Command createCommand() {
            return asyncCommand();
        }
    }
}

자바 기반 configuration이 내부적으로 동작하는지에 대한 심층 정보

아래 예제는 @Bean 어노테이트된 메소드이다.

@Configuration
public class AppConfig {

    @Bean
    public ClientService clientService1() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao());
        return clientService;
    }

    @Bean
    public ClientService clientService2() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao());
        return clientService;
    }

    @Bean
    public ClientDao clientDao() {
        return new ClientDaoImpl();
    }
}

ClientDao()는 clientService1()이 한번 부르고 clientService2()가 한번 더 부른다. 이 메소드가 ClinetDaoImpl의 새로운 인스턴스를 생성하고 반환한 이후로 보통 두개의 인스턴스(각 서비스 당 하나)를 기대한다. 그거는 문제일지 모른다. : 스프링에선, 인스턴스화된 빈들은 기본적으로 싱글톤 빈이다. 이는 문제가 될 수 있다. .:모든 @Configuration 클래스들은 CGLIB의 시작해 올라가는 시간(startup-time)에 서브클래스가 된다. 서브클래스에서는 자식 메소드가 부모 메소드를 부르고 새로운 인스턴스를 생성하기 전에 (스코프된) 캐시된 빈들을 처음 컨테이너에서 체크한다.

빈의 스코프에 따라 이는 달라질 수 있다. 싱글톤에 대해 더 얘기해보자.

스프링 3.2 에서는 CGLIB클래스가 org.springframework.cglib아래서 리패키지하는 더이상 CGLIB가 필요하지않다.그리고 스프링 코어 JAR에 포함된 것도.

CGLIB가 역동적으로 시작시간(startup-time)에 기능을 추가한다는 사실때문에 몇 한계가 있다. configuration 클래스들은 마지막이면 안된다. 하지만 4.3에서는 모든 생성자들이 configuration클래스에서 @Autowired 의 사용이나 기본 주입을 위한 하나의 기본 생성자 아닌 선언을 포함하여 허가가된다.

만약 CHLIB 한계를 피하고 싶으면 @Bean메소드를 @Configuration 클래스가 아닌 곳에서 선언하는 것을 고려해봐라(예를들어서, 일반 @Component 클래스). Cross-method 콜은 @Bean 메소드들 사이에서 방해받지 않는다. 그래서 생성자나 메소드 레벨에서 의존성 주입을 하는 것에 독점적으로 의존해야만 한다.

그래서 문제가 될수 있다 다음에 어쩌자는건지 찾아봤는데, 이런식으로 여러번 메소드를 호출해도 매번 동일한 오브젝트가 돌아온대. 두번 부른다고 해서 두개가 생성되는게 아니라. 그래서 이런 식으로여러번 호출하는 것은 스코프를 보장받지 못하기 때문에 쓰면 안되는 방식이래.

참조 : http://toby.epril.com/?p=939

Last updated