공장 콩

마주칠 기회

스프링 시큐리티 강의를 들으면서 강사님이 DB에서 값을 받아오기 위해 FactoryBean을 구현하여 빈을 생성하는 것을 봤습니다. 나는 보통 @Component, @Configuration 또는 @Bean을 통해 빈을 생성하지만, 그렇게 생성하는 방법도 있다는 것을 알았다.

스프링 빈 등록

Spring은 Reflection API를 사용하여 지정된 클래스 이름으로 빈을 생성합니다. 내부적으로 bean 정의에 나타나는 클래스 이름으로 bean이 생성됩니다.

문제

Spring이 DI할 수 없는 클래스의 bean이 있을 수 있습니다. 예를 들어, 클래스 정보를 미리 알 수 없는 경우입니다. Java에서 Dynamic Proxy에 의해 생성된 객체는 자신이 어떤 클래스인지 미리 알지 못합니다. 클래스 자체도 내부적으로 정의되어 사용되기 때문입니다. 다른 예로는 정적 팩토리 클래스로 액세스해야 하는 객체, 타사에서 제공하는 클래스, 복잡한 생성 및 기본 설정이 있는 객체와 같이 new 연산자를 사용하여 생성할 수 없는 객체가 있습니다.이를 위해 Spring은 어댑터 클래스를 제공합니다. FactoryBean 준비라고 합니다.

공장 콩

FactoryBean은 인터페이스입니다. Spring에서 FactoryBean을 구현한 클래스는 다음 메소드를 통해 Bean으로 등록됩니다.

public interface FactoryBean {
    T getObject() throws Exception;
    Class<?> getObjectType();
    boolean isSingleton();
}
  • getObject() – 팩토리에서 생성된 객체를 반환합니다. 이 객체는 Spring 컨테이너에서 사용됩니다. 즉 반환된 객체는 빈으로 등록되어 싱글톤으로 관리된다.
  • getObjectType() – 이 FactoryBean에 의해 생성된 객체 유형을 반환합니다.
  • isSingleton() – 이 팩토리 빈이 싱글톤인지 여부를 나타냅니다. true를 반환하면 팩토리 빈이 반환하는 개체가 싱글톤이 아니라 팩토리 빈이 싱글톤임을 나타냅니다.

시험

테스트를 통해 이를 확인해보자.

먼저 bean으로 사용할 클래스를 정의하십시오.

public class Message {
    String text;

    private Message(String text) {
        this.text = text;
    }

    public static Message newMessage(String text) {
        return new Message(text);
    }

    public String getText() {
        return text;
    }
}

그런 다음 이 클래스 생성을 담당하는 MessageFactoryBean을 정의합니다. 이 클래스는 FactoryBean을 구현합니다.

@Component
public class MessageFactoryBean implements FactoryBean<Message> {
    @Override
    public Message getObject() throws Exception {
        return Message.newMessage("hello");
    }

    @Override
    public Class<?> getObjectType() {
        return Message.class;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }
}

코드에서 볼 수 있듯이 MessageFactoryBean은 bean으로 등록되고 이 bean을 검색하면 Message 객체가 반환됩니다.

@Autowired
ApplicationContext applicationContext;

@Test
void factoryBean() {
    Message m = applicationContext.getBean("messageFactoryBean", Message.class);
    for (String s : applicationContext.getBeanDefinitionNames()) {
        System.out.println(s);
    }
    Assertions.assertThat(m.getText()).isEqualTo("hello");
}

실행 결과는 다음과 같습니다.


FactoryBean 등록

@Component의 이름 값에 메시지를 부여하면 FactoryBean 이름도 메시지로 변경할 수 있다.

@Component("message")
public class MessageFactoryBean implements FactoryBean<Message> {
    // ...
}
@Autowired
ApplicationContext applicationContext;

@Test
void factoryBean() {
    Message m = applicationContext.getBean("message", Message.class);
    for (String s : applicationContext.getBeanDefinitionNames()) {
        System.out.println(s);
    }
    Assertions.assertThat(m.getText()).isEqualTo("hello");
}


@component(“메시지”)

FactoryBean 자체를 조회하려면 bean 이름 앞에 ‘&’를 입력하십시오.

@Autowired
ApplicationContext applicationContext;

@Test
void getFactoryBean() {
    MessageFactoryBean mfb = applicationContext.getBean("&message", MessageFactoryBean.class);
    for (String s : applicationContext.getBeanDefinitionNames()) {
        System.out.println(s);
    }
    Assertions.assertThat(Objects.requireNonNull(mfb.getObject()).getText()).isEqualTo("hello");
}


&FactoryBean 테스트