ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring Framework 6.2 문서 읽고 이해하기 ... IOC Container (3)
    개발하면서/타인글보면서 2025. 3. 2. 11:00
    반응형

    엔터프라이즈 급 어플리케이션은 대부분 객체 하나만 갖고 있지 않는다.

    심지어 간단한 어플레이션도 사용자에게 일관된 기능을 제공하기 위해 서로 협력하는 객체들로 구성된다.

    앞에서는 독립적으로 동작하는 빈을 얘기했는데,
    지금부터는 어플리케이션의 목표를 온전히 달성하기 위해 객체끼리 협력하는 방법을 설명한다.

    의존성 주입

    의존성 주입(Dependency injection, DI)은

    생성자 주입, factory 메서드로 인자 또는 setter 메서드로 property 설정 방식으로 의존성을 정의하는 프로세스다.

    컨테이너는 빈 생성할 때 정의한 의존관계들을 주입한다.

    이 과정들은 기본적으로 생성자나 서비스 로케이터 패턴을 이용해서 의존성의 위치나 인스턴스화를 스스로 제어한다. (IoC)

     

    DI 원칙 덕분에 코드는 좀더 깔끔해지고, 디커플링은 의존관계를 더 효과적으로 제공한다.

    빈으로 생성될 객체는 종속성이 무엇인지, 위치나 타입 등을 몰라도 된다.

    그 결과, 테스트가 쉬운 클래스가 되는데, 특히 interface나 abstract class로 의존관계를 정의했다면
    단위테스트에서 스텁이나 목객체를 이용하기 더욱 쉬워진다.

     

    DI는 크게 두가지 방법이 있다.

    생성자 기반 의존정 주입(DI)과 Setter 메서드를 이용한 의존성 주입이 있다.

     

     

    생성자 기반 의존성 주입

    생성자 기반 의존성 주입은 컨테이너가 의존관계인 argument와 함께 생성자를 호출한다.

    static factory 메소드 호출하는 방식도 생성자 방식과 거의 비슷하다.

     

    아래는 컨테이너의 인터페이스나 기본 클래스, annotation을 사용하지 않는 POJO의 생성자 기반 의존성 주입 예시다.

    public class SimpleMovieLister {
    
    	// the SimpleMovieLister has a dependency on a MovieFinder
    	private final MovieFinder movieFinder;
    
    	// a constructor so that the Spring container can inject a MovieFinder
    	public SimpleMovieLister(MovieFinder movieFinder) {
    		this.movieFinder = movieFinder;
    	}
    
    	// business logic that actually uses the injected MovieFinder is omitted...
    }

     

    생성자의 argument 매칭은 타입을 이용하여 처리한다.

    동일한 타입의 빈이 여러개가 있어 모호한 argument가 정의되어 있지 않다면 정의된 순서대로 빈을 생성한다.

    package x.y;
    
    public class ThingOne {
    
    	public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
    		// ...
    	}
    }

     

    ThingTwo, ThingThree가 상속하지 않아 모호하지 않는다고 가정해 보면,

    아래 설정만으로 훌륭하게 동작한다!! <constructor-arg /> element에 index나 타입 정의를 신경 쓰지 않아도 된다.

    <beans>
    	<bean id="beanOne" class="x.y.ThingOne">
    		<constructor-arg ref="beanTwo"/>
    		<constructor-arg ref="beanThree"/>
    	</bean>
    
    	<bean id="beanTwo" class="x.y.ThingTwo"/>
    
    	<bean id="beanThree" class="x.y.ThingThree"/>
    </beans>

     

    또 다른 예로 아래와 같이 Bean이 아닌 int, String 같이 자료형을 받을 경우 인덱스 번호나(0부터 시작) type으로 값을 주입할 수 있다.

    package examples;
    
    public class ExampleBean {
    
    	// Number of years to calculate the Ultimate Answer
    	private final int years;
    
    	// The Answer to Life, the Universe, and Everything
    	private final String ultimateAnswer;
    
    	public ExampleBean(int years, String ultimateAnswer) {
    		this.years = years;
    		this.ultimateAnswer = ultimateAnswer;
    	}
    }

     

    <bean id="exampleBean" class="examples.ExampleBean">
    	<constructor-arg type="int" value="7500000"/>
    	<constructor-arg type="java.lang.String" value="42"/>
    </bean>
    
    또는
    
    <bean id="exampleBean" class="examples.ExampleBean">
    	<constructor-arg index="0" value="7500000"/>
    	<constructor-arg index="1" value="42"/>
    </bean>

     

    Parameter 이름으로도 값 세팅할 수 있는데 아래처럼 생성자에 @ConstructorProperties를 사용하거나
    annotation을 쓰지 않으려면 컴파일할 때 -parameter flag를 추가하면 된다.

    XML의 property 값은 String으로 정의하고 conversion service를 이용해서 실제 타입으로 변환 후 값을 할당한다.

    Spring Type Conversion

    package examples;
    
    public class ExampleBean {
    
    	// Fields omitted
    
    	@ConstructorProperties({"years", "ultimateAnswer"})
    	public ExampleBean(int years, String ultimateAnswer) {
    		this.years = years;
    		this.ultimateAnswer = ultimateAnswer;
    	}
    }
    
    
    <bean id="exampleBean" class="examples.ExampleBean">
    	<constructor-arg name="years" value="7500000"/>
    	<constructor-arg name="ultimateAnswer" value="42"/>
    </bean>

    Setter 메서드 의존성 주입

    Setter 기반 DI는 컨테이너가 기본 생성자 또는 parameter가 없는 factory 메서드를 실행해서 빈을 초기화 후 setter 메소드를 실행한다.

    아래는 별도의 인터페이스, 기본 클래스, 어노테이션과 의존성이 없는 POJO를 Setter 메소드 의존성 주입 예시다.

    public class SimpleMovieLister {
    
    	// the SimpleMovieLister has a dependency on the MovieFinder
    	private MovieFinder movieFinder;
    
    	// a setter method so that the Spring container can inject a MovieFinder
    	public void setMovieFinder(MovieFinder movieFinder) {
    		this.movieFinder = movieFinder;
    	}
    
    	// business logic that actually uses the injected MovieFinder is omitted...
    }

     

    PropertyEditor 인스턴스와 함께 BeanDefinition 형식으로 의존성을 정의하는 방법도 있지만 잘 사용하지 않는다.

    XML bean 설정 또는 @Component, @Controller 같은 annotation 설정,

    또는 @Configuration 클래스의 @Bean 메소드를 이용한다.

    앞에 3가지 방법은 내부적으로 BeanDefinition 인스턴스로 변환되고, 전체 IoC 컨테이너 인스턴스 로드하는 데 사용된다.

     

    생성자 기반 vs Setter 메서드 기반
    Spring 팀은 생성자 기반 방식을 권장한다.
    1. 컴포넌트를 불변 객체로 구현할 수 있고,
    2. 생성자 기반 컴포넌트는 항상 완전하게 초기화된 인스턴스를 리턴한다. (new 한 다음에 별도 설정이 필요 없다)
    3. 추가로, 생성자 argument 개수가 많다면 책임이 많다고 볼 수도 있어 관심사를 적절하게 분리할 타이밍임을 알아챌 수 있다.

     

     

    종속성 해결 프로세스

    1. ApplicaitonContext가 생성되고 모든 bean 정의가 있는 메타데이터 설정을 초기화한다.

        메타데이터 설정은 XML, Java, annotation으로 할 수 있다.

    2. 각각의 빈의 의존성은 property, 생성자 파라미터, static-factory 메서드 파라미터에 표현되어 있다.

    3. property와 생성자 파라미터는 실제 값이거나 컨테이너에 있는 다른 빈 참조다.

    4. property나 생성자 파라미터에 지정한 형식에서 실제 유형으로 변환한다.

        기본적으로 Spring은 문자열로 지정된 값을 int, long, String, boolean 등 모든 기본 실제 유형으로 변환한다.

     

    Spring Container는 컨테이너가 생성될 때 모든 빈 설정이 유효한지 검사한다.

    하지만 bean의 property는 실제 생성되기 전까지 할당되지 않는다.

    컨테이너가 생성될 때 빈은 singleton-scoped(default)으로 설정되고 미리 인스턴스화된다.

    다른 경우는, 요청이 있을 때 빈을 생성하는 방법도 있다.

    Customizing the Nature of a Bean

     

    depdends-on 사용하기

    한 Bean이 다른 Bean에 의존성이 있는 경우 XML의 <ref>나 autowiring을 이용한다.

    하지만 데이터베이스 드라이버 초기화처럼 의존관계가 덜한 관계도 있다. ※ 실제 사용하지는 않지만 초기화는 필요

    이럴 때는 @DependesOn 어노테이션을 이용해서 강제로 의존관계의 Bean을 초기화할 수 있다.

    <bean id="beanOne" class="ExampleBean" depends-on="manager"/>
    <bean id="manager" class="ManagerBean" />
    
    
    
    <!-- multiple depends-on  -->
    <bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
    	<property name="manager" ref="manager" />
    </bean>
    
    <bean id="manager" class="ManagerBean" />
    <bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
    반응형

    댓글

Designed by Tistory.