3주차 개념정리
정말..알아야할게 끝도 없다
DI(Dependency Injection)
스프링이 다른 프레임워크와 차별화되어 제공하는 의존 관계 주입 기능으로 객체를 직접 생성하는 게 아니라 외부에서 생성한 후 주입 시켜주는 방식이다.
- 강한결합
강한 결합은 어떠한 객체가 다른 객체에 강한 의존성을 가지고 있음을 뜻한다.
자바를 배울 때 상속과 포함관계, 그리고 인터페이스에 대해서 공부를 했었는데 그 개념과 굉장히 유사한 것 같다
Controller1이 Service1의 객체를 생성하고 생성자를 통해 강한결합이 되어있다. 즉 “의존 대상 B가 변하면, 그것이 A에 영향을 미친다”고 한다. 즉, B의 기능이 추가되거나 변경되면 그 영향이 A에 미치는 것이다
class BurgerChef {
private HamBurgerRecipe hamBurgerRecipe;
public BurgerChef() {
hamBurgerRecipe = new HamBurgerRecipe();
}
}
위의 예시를 보면 햄버거 레시피가 변화게 되었을 때, 변화된 레시피에 따라서 BurgerChef 클래스를 수정해야 한다. 레시피의 변화가 요리사의 행위에 영향을 미쳤기 때문에 요리사는 레시피에 의존한다고 말할 수 있다.
- 느슨한결합
해결방법은 여러가지가 있다
1. 인터페이스로 추상화
2. 각 객체에 대한 객체 생성을 한번만 하고 생성된 객체를 모든 곳에서 재사용하면된다.
1. 인터페이스로 추상화
class BurgerChef {
private BurgerRecipe burgerRecipe;
public BurgerChef() {
burgerRecipe = new HamBurgerRecipe();
//burgerRecipe = new CheeseBurgerRecipe();
//burgerRecipe = new ChickenBurgerRecipe();
}
}
interface BugerRecipe {
newBurger();
}
class HamBurgerRecipe implements BurgerRecipe {
public Burger newBurger() {
return new HamBerger();
}
}
위 코드에서 볼 수 있듯이, 다양한 버거 레시피에 의존할 수 있는 BurgerChef가 되었다. 이처럼 의존 관계를 인터페이스로 추상화하게 되면, 더 다양한 의존 관계를 맺을 수 있고, 실제 구현 클래스와의 관계가 느슨해지며 결합도가 낮아진다.
2. 객체 생성 후 재사용
class BurgerRepository {
BurgerRepository burgerRepository = new BurgerRepository();
//햄버거 저장소 객체 생성
}
class BurgerRecipe{
private final BurgerRepository burgerRepository;
// BurgerRepository 객체 사용
public BurgerRecipe(BurgerRepository burgerRepository) {
this.burgerRepository = burgerRepository;
}
}
// 버거레시피 객체 생성후 버거저장소사용
BurgerRecipe burgerRecipe = new BurgerRecipe(burgerRepository);
이러한 두번째 방식이 의존성 주입의 예시다.
즉 , A 객체에서 B, C객체를 사용(의존)할 때 A 객체에서 직접 생성 하는 것이 아니라 외부(IOC컨테이너)에서 생성된 B, C객체를 조립(주입)시켜 setter 혹은 생성자를 통해 사용하는 방식이다.
그럼 위의 그림처럼 더 이상 하나의 Controller 마다 새로운 여러 객체들을 항상 만들어서 사용할 필요가 없고 이미 생성했던 객체의 멤버변수들을 재사용할 수 있다.
프로그램에서 이런 흐름이 뒤바뀌는 것을 제어의 역전(IoC)이라고 한다.
이처럼 의존관계를 외부에서 결정해주는 것이 DI(의존성 주입)이라 하였는데 스프링은 도대체 외부의 객체들을 어떻게 주입해줄까?
스프링 컨테이너(IoC)
IoC
- 위에서 잠깐 언급했듯이 IoC란 Inversion of Control의 줄임말이며, 제어의 역전이라고 한다.
- 스프링 애플리케이션에서는 오브젝트(빈)의 생성과 의존 관계 설정, 사용, 제거 등의 작업을 애플리케이션 코드 대신 스프링 컨테이너가 담당한다.
- 이를 스프링 컨테이너가 코드 대신 오브젝트에 대한 제어권을 갖고 있다고 해서 IoC라고 부른다.
- 따라서, 스프링 컨테이너를 IoC 컨테이너라고도 부른다.
IoC 컨테이너
- 스프링에서는 IoC를 담당하는 컨테이너를 빈 팩토리, DI 컨테이너, 애플리케이션 컨텍스트라고 부른다.
- 오브젝트의 생성과 오브젝트 사이의 런타임 관계를 설정하는 DI 관점으로 보면, 컨테이너를 빈 팩토리 또는 DI 컨테이너라고 부른다.
- 그러나 스프링 컨테이너는 단순한 DI 작업보다 더 많은 일을 하는데, DI를 위한 빈 팩토리에 여러 가지 기능을 추가한 것을 애플리케이션 컨텍스트라고 한다.
- 정리하자면, 애플리케이션 컨텍스트는 그 자체로 IoC와 DI 그 이상의 기능을 가졌다고 보면 된다.
위의 그림을 보면 IoC 컨테이너가 관리하는 여러가지 객체들이 있는데 이것을 Bean 이라고 한다. 즉, IoC 컨테이너의 가장 기초적인 역할을 오브젝트를 생성하고 이를 관리하는데 그런 오브젝트는 빈이라 부르는 것!
빈(Bean) 팩토리
- 스프링 컨테이너의 최상위 인터페이스이다.
- 개발자가 직접 제어가 불가능한 외부 라이브러리를 bean으로 만들려고 할 때 사용
- 스프링 빈을 관리하고 조회하는 역할을 담당한다.
- 대표적으로 getBean() 메소드를 제공한다.
빈의 사용방법은?
1.직접 객체를 생성하여 빈으로 등록 요청
2. 아래 코드는 메서드에 @Bean을 붙여 return된 값을 빈으로 등록하는 것
3. 멤버 변수 선언 위에 @Autowired만 붙여주면 끝이다
잠깐 뭐라고? Autowired?
Autowired
DI를 할 때 사용하는 어노테이션이며, 의존 관계의 타입에 해당하는 빈을 찾아 주입하는 역할을 한다. 쉽게 말하자면, 스프링 서버가 올라 갈 때 애플리케이션 컨텍스트가 @Bean이나 @Service, @Controller 등 어노테이션을 이용하여 등록한 스프링 빈을 생성하고, @Autowired 어노테이션이 붙은 위치에 의존 관계 주입을 수행하게 된다.
그렇다면, @Autowired 어노테이션이 붙은 위치에 어떻게 의존 관계를 주입하는 걸까? 우선 @Autowired 어노테이션의 코드를 살펴 보자.
/**
* Note that actual injection is performed through a BeanPostProcessor which in turn means
* that you cannot use @Autowired to inject references into BeanPostProcessor or
* BeanFactoryPostProcessor types. Please consult the javadoc for the
* AutowiredAnnotationBeanPostProcessor class (which, by default, checks for the presence
* of this annotation).
* Since:
* 2.5
* See Also:
* AutowiredAnnotationBeanPostProcessor, Qualifier, Value
* Author:
* Juergen Hoeller, Mark Fisher, Sam Brannen
*/
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
- @Target
- 생성자와 필드, 메소드에 적용 가능하다.
- @Retention
- 컴파일 이후(런타임 시) JVM에 의해 참조가 가능하다. 런타임 시 이 어노테이션의 정보를 리플렉션으로 얻을 수 있다.
💡 리플렉션이란?
리플렉션은 구체적인 클래스 타입을 알지 못해도, 그 클래스의 메서드, 타입, 변수들에 접근할 수 있도록 해주는 자바 API.
아하? 그러니까 예전에 @RequiredArgsConstructor를 @Controller에 아무렇게나 가져다 붙였던 적이 있었는데 그건 선언된 멤버 변수 @Bean을 담은 생성자를 자동으로 만들어주며 @Autowired 까지 붙여주었던 것이었다..
만약 IoC에서 Bean을 수동적으로 불러오려면 어떻게 할 수 있을까?
@Component
public class ProductService {
private final ProductRepository productRepository;
@Autowired
public ProductService(ApplicationContext context) {
// 1.'빈' 이름으로 가져오기
ProductRepository productRepository = (ProductRepository) context.getBean("productRepository");
// 2.'빈' 클래스 형식으로 가져오기
// ProductRepository productRepository = context.getBean(ProductRepository.class);
this.productRepository = productRepository;
}
컴포넌트(Component)
- 개발자가 직접 작성한 class를 빈으로 등록할 때 사용
- 스프링 서버가 처음 뜰 때 스프링 IoC 에 '빈'을저장
// 1. ProductService 객체 생성
ProductService productService = new ProductService();
// 2. 스프링 IoC 컨테이너에 빈 (productService) 저장
// productService -> 스프링 IoC 컨테이너
- @ComponentScan 으로 설정해 준 package 위치와 하위 package들까지만 적용 가능한 범위에 속한다. (패키지 외부에 @Component는 등록이 안되는 것을 주의)
@Configuration
@ComponentScan(basePackages = "com.sparta.springcore")
class BeanConfig { ... }
- 사실 ComponentScan이 빈들을 찾아주는데 이 때 application에 아래의 어노테이션이 default값으로 설정되어 있어서 알아서 등록해준다
@SpringBootApplication
Bean & Component의 차이점을 주의
참고로 @Configuration 은 해당 클래스가 Bean 구성 클래스임을 스프링 IoC에게 알려주기 위함으로 사용된다.
References :
https://galid1.tistory.com/494
'프로그래밍 > Spring' 카테고리의 다른 글
AOP & Logging (slf4j) (0) | 2022.08.05 |
---|---|
[SPRING BOOT]JWT, Thymeleaf, form (2/2) (0) | 2022.08.04 |
[SPRING BOOT]JWT, Thymleaf, form (1/2) (2) | 2022.08.04 |
Spring MVC Architecture (0) | 2022.07.27 |
게시판 만들기 (Usecase Diagram & API) (0) | 2022.07.26 |