https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-dependencies
틀린 해석이 있다면 알려주세요 🌸
DI는 Spring 공부할때 기본적으로 나오는 내용이다
1.4.1. Dependency Injection 을 다뤄본다
1.4. Dependencies
일반적인 enterprise application은 단일 개체(bean)로 구성되지않는다.
단순한 응용 프로그램이라도 최종적으로 사용자가 일관된 응용프로그램으로 볼 수 있도록 하는 몇가지의 objects 들이 있다
독립적인 여러 bean 정의를 정의하는 것에서부터 개체가 협력하여 완전한 application 으로 갈 수 있는 방법을 설명한다
DI는 Spring 공부할때 기본적으로 나오는 내용이다
1.4.1. Dependency Injection
DI(Dependency Injection)는 객체가 작동할 때 필요한 다른 객체들(즉, 해당 객체와 함께 작동하는 다른 객체)을 생성자 인자, 팩토리 메서드의 인자, 또는 객체 인스턴스가 생성된 후에 설정되는 속성을 통해서만 정의하는 프로세스이다.
컨테이너는 객체를 생성할 때 이러한 종속성을 주입한다. 이 과정은 bean이 자체적으로 클래스를 직접 생성하거나 Service Locator 패턴을 사용하여 종속성의 인스턴스를 위치시키거나 생성하는 것과 근본적으로 반대되는 작업이다.
따라서 IoC(Inversion of Control)이라는 이름이 붙게 되었다.
DI 원칙을 사용하면 코드가 더 깨끗해지고 객체가 종속성을 제공받을 때 결합도가 더 낮아진다.
객체는 스스로 종속성을 검색하지 않으며, 종속성의 위치나 클래스를 알수 없다.
결과적으로 클래스는 특히 인터페이스 또는 추상 기본 클래스에 종속성이 있는 경우에 단위 테스트에서 stub 또는 mock 구현을 사용할 수 있도록 하여 테스트하기 쉬워진다.
아래에 설명될
Constructor-based dependency injection
Setter-based dependency injection
이 두가지가 주요 변형이다
Constructor-based Dependency Injection
생성자 기반 DI, 각각의 dependency 를 나타내는 여러 args를 사용하여 생성자를 호출하는 컨테이너에 의해 수행된다.
bean을 구성하기 위해 특정 args로 factory method를 호출하는 static method과 거의 동일 하며,
constructor 및 static factory method에 대한 arguments를 비슷하게 처리한다
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...
}
It is a POJO that has no dependencies on container specific interfaces, base classes, or annotations.
Constructor Argument Resolution
constructor argument resolution은 인수의 타입을 사용한다. bean 정의의 constructor 인수에 잠재적 모호성이 존재하지 않은 경우에 bean이 인스턴스화 될 때부터 해당 인수가 절절한 생성자에 제공되는 순서로 진행된다.
public class ThingOne {
public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
// ...
}
}
Assuming that the ThingTwo and ThingThree classes are not related by inheritance, no potential ambiguity exists. Thus, the following configuration works fine, and you do not need to specify the constructor argument indexes or types explicitly in the <constructor-arg/> element.
<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>
When another bean is referenced, the type is known, and matching can occur (as was the case with the preceding example). When a simple type is used, such as <value>true</value>, Spring cannot determine the type of the value, and so cannot match by type without help. Consider the following class:
Constructor argument type matching
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;
}
}
In the preceding scenario, the container can use type matching with simple types if you explicitly specify the type of the constructor argument by using the type attribute, as the following example shows:
Constructor argument index
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>
You can use the index attribute to specify explicitly the index of constructor arguments, as the following example shows:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
</bean>
index를 지정하면 생성자가 동일한 유형의 두 인수를 가질 수 있는 모호성을 해결할 수 있다
Constructor argument name 색인으로 구분도 가능하다
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="years" value="7500000"/>
<constructor-arg name="ultimateAnswer" value="42"/>
</bean>
이를 즉시 수행하려면 매개변수 이름을 조회할 수 있도록 debug flag를 사용하여 코드를 컴파일 해야한다. 컴파일할 수 없거나 하지 않으려면 @ConstructorProperties JDK 애노테이션을 사용하여 명시적으로 지정할 수 있다
public class ExampleBean {
// Fields omitted
@ConstructorProperties({"years", "ultimateAnswer"})
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
Setter-based Dependency Injection
Setter 기반 DI는 constructor, arguments 등이 없는 static factory method를 호출하여 bean을 인스턴스화한 후 bean에서 setter method를 호출하는 컨테이너 형식이다
only be dependency-injected by using pure setter injection.
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...
}
ApplicationContext는 곤리하는 bean에 대해 constructor 및 setter-based(설정 기반) DI를 지원해준다
생성자 접근 방식을 통해 일부 종속성을 미리 주입시킨 후 setter-based DI를 지원한다
You configure the dependencies in the form of a BeanDefinition, which you use in conjunction with PropertyEditor instances to convert properties from one format to another. However, most Spring users do not work with these classes directly (that is, programmatically) but rather with XML bean definitions, annotated components (that is, classes annotated with @Component, @Controller, and so forth), or @Bean methods in Java-based @Configuration classes. These sources are then converted internally into instances of BeanDefinition and used to load an entire Spring IoC container instance.
Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies. Note that use of the @Autowired annotation on a setter method can be used to make the property be a required dependency; however, constructor injection with programmatic validation of arguments is preferable.
The Spring team generally advocates constructor injection, as it lets you implement application components as immutable objects and ensures that required dependencies are not null. Furthermore, constructor-injected components are always returned to the client (calling) code in a fully initialized state. As a side note, a large number of constructor arguments is a bad code smell, implying that the class likely has too many responsibilities and should be refactored to better address proper separation of concerns.
Setter injection should primarily only be used for optional dependencies that can be assigned reasonable default values within the class. Otherwise, not-null checks must be performed everywhere the code uses the dependency. One benefit of setter injection is that setter methods make objects of that class amenable to reconfiguration or re-injection later. Management through JMX MBeans is therefore a compelling use case for setter injection.
Use the DI style that makes the most sense for a particular class. Sometimes, when dealing with third-party classes for which you do not have the source, the choice is made for you. For example, if a third-party class does not expose any setter methods, then constructor injection may be the only available form of DI.
- ApplicationContext는 모든 bean을 설명해주는 구성 meta-data로 생성되고 초기화 된다 (XML, Java, annotation)
- 각 bean에 대해 종속성은 form of properties, constructor arguments, or arguments to the static-factory method (if you use that instead of a normal constructor) 형식으로 표현된다 (이러한 종속성은 bean이 실제 생성될 때 bean에 제공)
- 각 속성 또는 constructor arguments는 설정할 값의 실제 정의이거나 container의 다른 bean에 대한 참조이다
- value가 되는 각 속성 또는 생성자 인수는 지정된 형식에서 해당 속성 또는 생성자 인수의 실제 유형으로 변환된다. 기본적으로 Spring은 문자열 형식으로 제공된 값을 int, long, String, boolean 등과 같은 모든 내장 유형으로 변환할 수 있다.
* Spring Contatiner는 생성될 때 각 bean의 구성을 검증한다. 그러나 bean의 속성 자체는 실제 생성될 때 까지 설정되지 않는다. 이부분이 중요!! SingleTon 범위이고 사전 instance화(default)로 설정된 bean들은 container가 생성될 때 생성된다.
bean의 범위에 의해 정의된다. (이것도 계속 공부해볼 생각)
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-scopes
Circular dependencies
contructor injection을 사용하는 경우 해결할 수 없는 circular dependency scenario가 생길 수 있다
Class A가 DI를 통해서 클래스 B의 인스턴스가 필요한 상태이고,
이 클래스 B는 생성자 주입을 받아 Class A의 인스턴스가 필요하다면 어떻게 될까?
class A, B가 서로 주입되도록 bean을 구성하면 Spring IoC container가 런타임에서 이를 감지하고 BeanCurrentlyInCreationException 를 발생신킨다
이럴 때는 constructor가 아닌 setter가 구성할 일부 클래스의 코드를 편집하거나, 또는 constructor 주입보다는 Setter 주입을 사용하면 된다. 권장하는 방법은 아니지만 이를 이용하여 Circular 종속성을 구성할 수 있다.
일반적인 경우에는 beanA와 beanB 사이의 circular dependecies는 bean 중 하나가 완전히 초기화 되기 전에 다른 빈에 주입될 수 있도록 강제한다
Spring은 Bean을 실제로 생성시킬 때 가능한 늦게 속성을 설정하고 종속성을 해결한다. 이 circular dependency가 없는 경우에는 하나 이상의 협력되는 빈이 종속 빈에 주입될 때 각 협력 빈들은 종속 빈에 주입전에 구성이 완전히 되어있다.
bean A가 bean B에 대한 종속성을 가지고 있으면 Spring IoC 컨테이너는 bean A에서 setter 메소드를 호출하기 전에 bean B를 완전히 구성한다. In other words, the bean is instantiated (if it is not a pre-instantiated singleton), its dependencies are set, and the relevant lifecycle methods (such as a configured init method or the InitializingBean callback method) are invoked.
Examples of Dependency Injection
setter based DI에 XML 구성 메타데이터 사용
<bean id="exampleBean" class="examples.ExampleBean">
<!-- setter injection using the nested ref element -->
<property name="beanOne">
<ref bean="anotherExampleBean"/>
</property>
<!-- setter injection using the neater ref attribute -->
<property name="beanTwo" ref="yetAnotherBean"/>
<property name="integerProperty" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
ExampleBean 클래스
public class ExampleBean {
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
public void setBeanOne(AnotherBean beanOne) {
this.beanOne = beanOne;
}
public void setBeanTwo(YetAnotherBean beanTwo) {
this.beanTwo = beanTwo;
}
public void setIntegerProperty(int i) {
this.i = i;
}
}
constructor 기반 DI 사용 예
<bean id="exampleBean" class="examples.ExampleBean">
<!-- constructor injection using the nested ref element -->
<constructor-arg>
<ref bean="anotherExampleBean"/>
</constructor-arg>
<!-- constructor injection using the neater ref attribute -->
<constructor-arg ref="yetAnotherBean"/>
<constructor-arg type="int" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean {
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
public ExampleBean(
AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
this.beanOne = anotherBean;
this.beanTwo = yetAnotherBean;
this.i = i;
}
}
contructor를 사용하는 대신 static 객체의 인스턴스를 반환하기 위해 팩토리 메소드 호출 예시
<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance">
<constructor-arg ref="anotherExampleBean"/>
<constructor-arg ref="yetAnotherBean"/>
<constructor-arg value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
factory method에 대한 arguments는 생성자가 실제 사용된 것과 정확히 동일한 static요소에 의해 제공 된다
An instance (non-static) factory method can be used in an essentially identical fashion (aside from the use of the factory-bean attribute instead of the class attribute), so we do not discuss those details here.
'Web > spring' 카테고리의 다른 글
[Spring Framework core] 1.4. Dependencies (3) (0) | 2023.03.02 |
---|---|
[Spring Framework core] 1.4. Dependencies (2) (0) | 2023.03.01 |
[Spring guide] Caching Data with Spring (0) | 2023.02.28 |
[Spring Boot IO] 1. Caching (0) | 2023.02.28 |
[Spring Framework integration] 6.Cache Abstraction (2) (0) | 2023.02.28 |