IoC란?
- 설계 원칙
- 제어의 역전 (Inversion of Control)
DI란?
- 디자인 패턴
- 의존성 주입
- 의존성이란?
→ 우리는 DI 패턴을 이용하여 IoC 설계 원칙을 구현하고 있다
- 강하게 결합돼있는 Consumer 와 Chicken
public class Consumer {
void eat() {
Chicken chicken = new Chicken();
chicken.eat();
}
public static void main(String[] args) {
Consumer consumer = new Consumer();
consumer.eat();
}
}
class Chicken {
public void eat() {
System.out.println("치킨을 먹는다.");
}
}
//이 때는 치킨이 아니라 피자가 먹고 싶을 때 코드 수정이 힘들다 -> 약하게 결합시켜야함
- 약하게 결합시키기 - Interface 활용 (약한 결합 및 약한 의존성)
public class Consumer {
void eat(Food food) {
food.eat();
}
public static void main(String[] args) {
Consumer consumer = new Consumer();
consumer.eat(new Chicken());
consumer.eat(new Pizza());
}
}
interface Food {
void eat();
}
class Chicken implements Food{
@Override
public void eat() {
System.out.println("치킨을 먹는다.");
}
}
class Pizza implements Food{
@Override
public void eat() {
System.out.println("피자를 먹는다.");
}
}
DI 사용 )
주입이란 ? 여러 방법을 통해 필요로 하는 객체를 해당 객체에 전달하는 것필드에 직접 주입
- 필드에 직접 주입 (Food 를 Consumer에 포함시키고 Food에 필요한 객체를 주입받아 사용할 수 있다.)
public class Consumer {
Food food;
void eat() {
this.food.eat();
}
public static void main(String[] args) {
Consumer consumer = new Consumer();
consumer.food = new Chicken();
consumer.eat();
consumer.food = new Pizza();
consumer.eat();
}
}
interface Food {
void eat();
}
class Chicken implements Food{
@Override
public void eat() {
System.out.println("치킨을 먹는다.");
}
}
class Pizza implements Food{
@Override
public void eat() {
System.out.println("피자를 먹는다.");
}
}
- 메서드를 통한 주입 (set 메서드를 사용하여 필요한 객체를 주입받아 사용할 수 있다)
public class Consumer {
Food food;
void eat() {
this.food.eat();
}
public void setFood(Food food) {
this.food = food;
}
public static void main(String[] args) {
Consumer consumer = new Consumer();
consumer.setFood(new Chicken());
consumer.eat();
consumer.setFood(new Pizza());
consumer.eat();
}
}
interface Food {
void eat();
}
class Chicken implements Food{
@Override
public void eat() {
System.out.println("치킨을 먹는다.");
}
}
class Pizza implements Food{
@Override
public void eat() {
System.out.println("피자를 먹는다.");
}
}
제어의 역전(IoC)
- 제어의 흐름이 Consumer → Food 에서 Food → Consumer 로 역전
- Food를 Consumerd에게 전달해주는 식으로 변경함으로써 Consumer은 추가적인 코드변경 없이 어느 Food가 되었든지 전부 먹을 수 있게 되었다.
IoC Container
- DI를 사용하기 위해서는 객체 생성이 우선 되어야하며, Spring 프레임워크가 필요한 객체를 생성하고 관리하는 역할을 대신 해준다.
- 빈(Bean) : Spring이 관리하는 객체
- Spring IoC 컨테이너 : 'Bean'을 모아둔 컨테이너
- Spring 'Bean' 등록 방법
@Component //'Bean' 으로 등록하고자 하는 클래스 위에 설정
public class MemoService { ... }
- Spring 서버가 뜰 때 IoC 컨테이너에 'Bean'을 저장
- @Component가 설정된 클래스에 대해서 Spring이 해 주는 일 확인하기
// 1. MemoService 객체 생성, 'Bean'이름 : 클래스의 앞글자만 소문자로 변경
MemoService memoService = new MemoService();
// 2. Spring IoC 컨테이너에 Bean (memoService) 저장
// memoService -> Spring IoC 컨테이너
- Spring 서버가 뜰 때 @ComponentScan에 설정해 준 packages 위치와 하위 packages 들을 전부 확인하여 @Component가 설정된 클래스들을 ‘Bean’으로 등록 해준다
@Configuration
@ComponentScan(basePackages = "com.sparta.memo")
class BeanConfig { ... }
- @SpringBootApplication에 의해 default 설정 되어있다. ( com.sparta.memo/MemoApplication.java )
Spring 'Bean' 사용 방법 )
- @Autowired
1. 필드 위에
@Component
public class MemoService {
@Autowired // Spring에서 IoC 컨테이너에 저장된 memoRepository 'Bean'을 해당 필드에 DI
private MemoRepository memoRepository;
// ...
}
2. 'Bean'을 주입할 때 사용할 메서드 위에
@Component
public class MemoService {
private final MemoRepository memoRepository;
@Autowired //객체의 불변성을 확보할 수 있기 때문에 생성자를 사용하여 DI를 하는 것이 좋다
public MemoService(MemoRepository memoRepository) {
this.memoRepository = memoRepository;
}
// ...
}
- @Autowired 적용 조건
- Spring IoC 컨테이너에 의해 관리되는 클래스에서만 가능
- Spring IoC 컨테이너에 의해 관리되는 ‘Bean’객체만 DI에 사용될 수 있습니다.
- @Autowired 생략 조건
- Spring 4.3 버젼 부터 @Autowired 생략가능
- 단, 생성자 선언이 1개 일때만 가능
- Lombok 의 @RequiredArgsConstructor를 사용
- Spring 4.3 버젼 부터 @Autowired 생략가능
@Component
@RequiredArgsConstructor // final로 선언된 멤버 변수를 파라미터로 사용하여 생성자를 자동으로 생성합니다.
public class MemoService {
private final MemoRepository memoRepository;
// public MemoService(MemoRepository memoRepository) {
// this.memoRepository = memoRepository;
// }
...
}
- ApplicationContext : BeanFactory등을 상속하여 기능을 확장한 Container
- BeanFactory 는 'Bean'의 생성, 관계설정등의 제어를 담당하는 IoC 객체
- 스프링 IoC 컨테이너에서 'Bean'을 수동으로 가져오는 방법
@Component
public class MemoService {
private final MemoRepository memoRepository;
public MemoService(ApplicationContext context) {
// 1.'Bean' 이름으로 가져오기
MemoRepository memoRepository = (MemoRepository) context.getBean("memoRepository");
// 2.'Bean' 클래스 형식으로 가져오기
// MemoRepository memoRepository = context.getBean(MemoRepository.class);
this.memoRepository = memoRepository;
}
...
}
3 Layer Annotation
- Controller, Service, Repository의 역할로 구분된 클래스들을 ‘Bean’으로 등록할 때 해당 ‘Bean’ 클래스의 역할을 명시하기위해 사용된다
'Spring' 카테고리의 다른 글
Spring 개념 정리 ) 쿠키와 세션 (0) | 2024.08.23 |
---|---|
Spring 개념 정리 ) 인증과 인가 / 쿠키와 세션, JWT (0) | 2024.08.22 |
Spring 개념 정리 ) Bean (0) | 2024.08.21 |
Spring 개념 정리 ) JPA CORE (0) | 2024.08.20 |
Spring 개념 정리 ) Gradle, 서버, HTTP 란? (0) | 2024.08.14 |