[Spring] Java 기반 컨테이너(Container) 설정

SEB_BE_43_yeori316·2023년 2월 5일
0

Spring

목록 보기
9/22

Java 기반 컨테이너(Container) 설정

자바 기반 설정의 가장 중요 애너테이션 2가지

  • @Configuration

  • @Bean

  • 메서드가 Spring 컨테이너에서 관리할 새 객체를 인스턴스화, 구성 및 초기화한다는 것을 나타내는 데 사용

// DependencyConfig 클래스
컨텍스트를 인스턴스화할 때

@Configuration
public class DependencyConfig {

    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }
}
  • XML 설정 방식
<beans>
    <bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>

AnnotationConfigApplicationContext 를 사용하여 스프링 컨테이너를 인스턴스화 하는 방법

애너테이션을 이용해 Config 클래스 설정하는 방법

  • Spring 3.0에 도입된 AnnotationConfigApplicationContext
  • ApplicationContext 구현은 아래와 같은 애너테이션이 달린 클래스로 파라미터를 전달 받고 있습니다.
    • @Configuration 클래스
    • @Component 클래스
    • JSR-330 메타데이터
  • @Configuration 클래스가 입력으로 제공되면 @Configuration 클래스 자체가 Bean 정의로 등록되고 클래스 내에서 선언된 모든 @Bean 메서드도 Bean 정의로 등록됩니다.
public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(DependencyConfig.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}
  • @Component 클래스와 JSR-330 클래스가 제공되면 빈 정의로 등록되며 필요한 경우 해당 클래스 내에서 @Autowired 또는 @Inject와 같은 DI 메타데이터가 사용되는 것으로 가정합니다.
// @Autowired - MyServiceImpl, Dependency1, Dependency2에서 스프링 의존성 주입 애너테이션을 사용한 예제

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

@Bean 애너테이션을 사용하기

  • @Bean은 메서드-레벨 애너테이션이며, 에서 제공하는 일부 속성을 지원합니다.

  • @Bean 애너테이션은 @Configuration-annoted 또는 @Component-annoted 클래스에서 사용할 수 있습니다.

빈 선언

  • 애너테이션 방식의 configuration
@Configuration
public class DependencyConfig {

    @Bean
    public TransferServiceImpl transferService() {
        return new TransferServiceImpl();
    }
}
  • XML 방식의 configuration
<beans>
    <bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
  • 빈 정의가 있는 인터페이스를 구현하여 bean configuration을 설정할 수 있습니다
public interface BaseConfig {

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

@Configuration
public class DependencyConfig implements BaseConfig {

}

빈 의존성

  • @Bean 애너테이션이 추가된(@Bean-annotated) 메서드는 빈을 구축하는데 필요한 의존성을 나타내는데 매개 변수를 사용할 수 있습니다.
@Configuration
public class DependencyConfig {

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

@Configuration 애너테이션을 사용하기

  • @Configuration는 해당 객체가 bean definitions의 소스임을 나타내는 애너테이션입니다.

  • @Configuration는 @Bean-annoted 메서드를 통해 bean을 선언

  • @Configuration 클래스의 @Bean 메서드에 대한 호출을 사용하여 bean 사이의 의존성을 정의할 수도 있습니다.

Bean 사이에 의존성 주입

  • 빈이 서로 의존성을 가질 때, 의존성을 표현하는 것은 다른 bean 메서드를 호출하는 것처럼 간단합니다.
@Configuration
public class DependencyConfig {

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

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

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

@Configuration
public class DependencyConfig {

    @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() 메서드에서 1번씩 호출되었습니다.
  • 이 메서드는 ClientDaoImpl의 새 인스턴스를 만들고 이를 반환하므로 일반적으로 두 개의 인스턴스(각 서비스마다 하나씩)가 있어야 합니다.
    • 문제점 - Spring에 인스턴스화된 빈은 기본적으로 싱글톤 범위를 갖게 됩니다.
  • 하위 클래스의 하위 메서드는 상위 메서드를 호출하고 새 인스턴스를 만들기 전에 먼저 컨테이너에 캐시된(범위 지정) bean이 있는지 확인합니다.

Java 코드에서 애너테이션을 사용해 Spring 컨테이너를 구성하는 방법

  • Spring의 자바 기반 구성 기능 특징인 애너테이션을 사용해 구성의 복잡성을 줄일 수 있습니다.
  • @Import 애너테이션
    • XML 파일 내에서 요소가 사용되는 것처럼 구성을 모듈화하는데 사용됩니다.
    • 다른 구성 클래스에서 @Bean definitions를 가져올 수 있습니다.
@Configuration
public class DependencyConfigA {

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

@Configuration
@Import(DependencyConfigA.class)
public class DependencyConfigB {

    @Bean
    public B b() {
        return new B();
    }
}
  • 컨텍스트를 인스턴스화할 때 DependencyConfigA.class와 DependencyConfigB.class 모두 지정하는 대신 DependencyConfigB만 제공하면 됩니다.
public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(DependencyConfigB.class);

    // now both beans A and B will be available...
    A a = ctx.getBean(A.class);
    B b = ctx.getBean(B.class);
}
  • ctx에 Import(DependencyConfigA.class) 받은 DependencyConfigB.class 사용으로 인해 ctx.getBean(A.class)가 가능해집니다.
  • 컨테이너 인스턴스화를 단순화할 수 있습니다.
    • 많은 @Configuration 클래스를 기억할 필요 없이 하나의 클래스만 처리하면 됩니다.
  • 추가한 @Bean 애너테이션에서 의존성 주입
  • 문제점
    • 실제로 사용될 때 빈은 1개의 구성 파일만 @Import 받지 않고 여러 구성 클래스 간에 걸쳐 서로 의존성을 갖습니다.
    • XML을 사용할 때는 컴파일러가 관여하지 않아 컨테이너 초기화 중에 ref=”some Bean”을 선언하여 스프링으로 해결할 수 있어 문제가 되지 않습니다.
    • @Configuration 클래스를 사용할 때, 자바 컴파일러는 구성 모델에 제약을 두며, 다른 빈에 대한 참조는 유효한 자바 구문이어야 합니다.
@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의 생성자 주입은 스프링 프레임워크 4.3에서만 지원됩니다.
    • 대상 빈이 하나의 생성자만 정의하는 경우 @Autowired 지정할 필요가 없습니다.
@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");
}

0개의 댓글