반응형
제어의 역행 (Inversion of Control, IoC)
- 객체 간의 의존 관계를 직접 연결하는 대신 컨테이너를 통해 관리하는 방식
- 어떤 객체를 사용할지에 대한 책임은 프레임워크에게 넘기고, 자신은 수동적으로 주입받는 객체를 사용
- 제어의 주체가 개발자에서 프레임워크에 넘어감, 제어가 역전됐다는 의미
- Spring Framework에서 IoC는 주로 DI를 통해 구현
의존성 주입 (Dependency Injection, DI)
- DI는 IoC의 한 형태
- 객체가 자신의 의존성(다른 객체와의 관계)을 직접 생성하거나 관리하지 않고, 외부로부터 주입받는 방식
- 의존성 주입은 원래 개발자가 했어야하는데, Spring 컨테이너가 진행할 예정
- 이 의존성 주입은 생성자 주입, Setter 주입 등 다양한 방식으로 이뤄질 수 있음
- DI를 통해 객체는 필요한 의존성을 외부로부터 받기 때문에, 객체 간의 결합도가 낮아짐
++ 컨테이너의 역할
- 객체를 생성(new) 및 관리(호출시 해당 객체 메서드 수행)
- 의존성 주입
++ 의존성
- 한 객체가 다른 객체를 사용할 때 의존성이 있다고함
- 어떤 메서드를 수행할 때에 다른 객체를 미리 new 해야하는 상황
- Tip : 의존성을 낮추는 방법은 인터페이스 등의 방법이 있고 의존성을 관리해주는 방법이 의존성 주입임
- ex) 아이폰과 애플워치는 의존 관계 ↓
만약 IPhone을 판매하는 Apple이 있다는 예시를 생각해보면,
Apple이라는 클래스 안에 IPhone이라는 객체를 가지는 구조를 띌 것임
public class Apple {
private IPhone iPhone;
}
이럴때 Apple이 IPhone에 의존성이 있다고 말함
즉, Apple이라는 클래스가 IPhone을 판매할 때 의존성을 가지는 관계라고 말함
의존성을 가지고 있을 경우, 코드를 작성하는 간단한 방법은 아래와 같음
public class Apple {
private IPhone iPhone;
public Apple() {
this.iPhone = new IPhone();
}
}
하지만 Apple클래스와 IPhone클래스는 강하게 결합되어 있고 의존성이 높다는 문제가 있음
만약 Apple에서 GalaxyPhone을 판매하고싶으면 Apple클래스 생성자 자체를 변경해야함
이를 해결하기 위해서는 IPhone의 상위 개념인 Phone Interface를 하나 생성할 것임
인터페이스는 의존성을 낮출 수 있음 > Apple에서는 IPhone외에도 GalaxyPhone을 팔수있게됨
public interface Phone {
}
public class IPhone implements Phone {
}
이렇게 상위 Interface를 생성한 뒤, Apple클래스에서 바로 IPhone 객체를 생성하는 것이 아니라
상위 인터페이스로 Phone 객체를 생성
Apple에서 판매하고 있는 상품의 종류가 바뀔 수 있으니
Apple안에서 바로 객체 IPhone을 생성하는 것이 아니라 외부에서 생성한 후 이를 주입해야함
public class Apple {
private Phone phone;
public Apple(Phone phone) {
this.phone = phone;
}
}
Spring 컨테이너가 관리하는 객체를 빈(Bean)이라고 부르며,
이러한 Bean들을 관리한다는 의미로 BeanFactory라고 부름
이는 디자인 패턴 중 팩토리 패턴과도 연관되어 있음
---------------------------------------------------------------------
의존성을 관리하기 위해 Spring에서 자주 쓰는 기법으로
의존성 주입 방식을 사용
아래 개념부터 알아가보자
1) 생성자 주입 (Constructor Injection, CI)
: 객체 생성 시 필요한 의존성을 생성자 매개변수로 전달하는 방식
- 생성자 통해 주입할때 에러가 발생하면 해당 객체는 생성되지 않음
- → 필수적일 때 사용하는 방식
- → Setter 주입보다 강제성이 짙
- 객체 상태의 불변성이 중요하고 의존성 변경 가능성이 낮은 경우 사용
2) Setter 주입 (Setter Injection, SI)
: 객체 생성 후 Setter 메서드를 통해 의존성을 설정하는 방식
- 생성자 주입 방식보다 덜 강제적임
- 기본생성자 + Setter 메서드를 활용하므로 기본생성자를 반드시 필요로함!
- 객체 만든 후 → Setter 주입
- 객체 생성 후 의존성 변경 가능성이 높은 경우 사용4
[ ver1 ] .xml 설정
1. 인터페이스를 통해 의존성 낮추기
인터페이스 자식 클래스
Phone IPhone/GalaxyPhone
Watch AppleWatch/GalaxyWatch
2. 의존성 주입으로 의존성을 효율적으로 관리하기
public class IPhone/GalaxyPhone implements Phone {
private Watch watch;
// 생성자 주입 방식
public IPhone/GalaxyPhone(Watch watch) {
this.watch = watch;
}
// Setter 주입 방식
public void setWatch(Watch watch) {
this.watch = watch;
}
@Override
public void powerOn() {
watch.powerOn();
}
}
public class AppleWatch/GalaxyWatch implements Watch {
@Override
public void powerOn() {
System.out.println("애플/갤럭시워치로 아이폰/갤럭시폰 전원 ON")
}
}
public Class Main {
public static void main(String[] args) {
[ver1_팩토리 클래스 만들어서 사용]
// 팩토리 패턴을 구현하기 위한 클래스 --> 객체명을 요청하면 해당 객체를 제공하는 클래스
// BeanFactory beanFactory = new BeanFactory();
// getBean() : 사용자 입력값에 따라 아이폰/갤럭시폰 객체를 리턴해줌
// Phone phone = (Phone)beanFactory.getBean(arg[0]); // Look Up
[ver2_스프링에서 기본 제공하는 팩토리 사용]
// 설정파일(.xml) 필요
AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml")
Phone phone = (Phone)factory.getBean("apple/galaxy"); // Look Up, (applicationContext.xml에서 설정한 이름으로 호출해야함)
phone.powerOn();
factory.close();
}
}
--------------------------------------------------------------
[applicationContext.xml]
1) 생성자 주입 태그
<beans>
// 애플폰or갤럭시폰 객체 new
<bean class="test.IPhone/GalaxyPhone">
// 생성자 주입
<constructor-args ref="appleWatch/galaxyWatch" />
</bean>
// 애플워치or갤럭시워치 객체 new
<bean class="test.AppleWatch/GalaxtWatch" id="appleWatch/galaxyWatch" />
</beans>
2) Setter 주입 태그
<beans>
// 애플폰or갤럭시폰 객체 new
<bean class="test.IPhone/GalaxyPhone">
// setter 주입
<property name="watch" ref="appleWatch/galaxyWatch" />
// 애플워치or갤럭시워치 객체 new
<bean class="test.AppleWatch/GalaxyWatch" id="appleWatch/galaxyWatch" />
</bean>
</beans>
---------------------------------
++ <constructor-arg>
- 생성자 주입을 할 때 사용 (생성자에 전달할 인자를 정의)
- ref : 생성자에 전달할 값
++ <property>
- 객체의 속성에 값을 주입할 때 사용
- property (== 속성, 필드, 멤버변수, attribute)
- name : setter 메서드명
- ref : 주입하려는 객체명(id명)
[ ver2 ] @어노테이션 기능
1. 인터페이스를 통해 의존성 낮추기
인터페이스 자식 클래스
Phone IPhone/GalaxyPhone
Watch AppleWatch/GalaxyWatch
2. 의존성 주입으로 의존성을 효율적으로 관리하기
@Component("apple/galaxy") // new
public class IPhone/GalaxyPhone implements Phone {
@Autowired // DI(의존주입)
@Qualifier("appleWatch/galaxyWatch")
private Watch watch;
@Override
public void turnOn() {
watch.powerOn();
}
}
@Component("appleWatch/galaxyWatch") // new
public class AppleWatch/GalaxyWatch implements Watch {
@Override
public void powerOn() {
System.out.println("애플/갤럭시워치로 아이폰/갤럭시폰 전원 ON")
}
}
public Class Main {
public static void main(String[] args) {
[스프링에서 기본 제공하는 팩토리 사용]
// 설정파일(.xml) 필요
AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml")
Phone phone = (Phone)factory.getBean("apple/galaxy"); // Look Up, (applicationContext.xml에서 설정한 이름으로 호출해야함)
phone.powerOn();
factory.close();
}
}
--------------------------------------------------------------
[applicationContext.xml]
<context:component-scan base-package="test" />
@어노테이션 기능은 다음글에서 자세히 작성
반응형
'개발 > Spring' 카테고리의 다른 글
Spring Framework_컨테이너 & 빈 관리 (0) | 2024.07.16 |
---|---|
Spring Framework_설치 (0) | 2024.07.16 |
Spring Framework_ApplicationContext, <bean></bean> (2) | 2024.07.16 |
Spring Framework_결합도(Coupling) (0) | 2024.07.16 |
Spring Framework_개념 (0) | 2024.07.16 |