[Spring] Spring DI

Jeini·2023년 5월 27일
0

🍃  Spring

목록 보기
16/33
post-thumbnail

💡 IoC(Inversion Of Control)


✔️ 제어의 역전: 제어의 흐름을 전통적인 방식과 다르게 뒤바꾸는 것

  • Control = flow Control
    : if문과 for문을 의미 ➡️ 실행 흐름이 바뀌는 것 ➡️ 실행 흐름이 역전되는 것

  • 전략 패턴이라고도 불림

✏️ 전통적인 방식과 IoC의 차이

  • 전통적인 방식은 우리가 다른 사람이 작성한 코드(라이브러리)를 호출해서 그 결과를 받는 흐름

  • IoC에서는 우리가 제공한 코드 (사용자 코드)를 라이브러리 측에서 호출 함

  • IoC는 전통적인 방식에서 호출 흐름이 바뀜

💡 전통적인 방식에서는 turboDrive() 안에서 TurboEngin 을 생성해 주고 있다. 하지만 SuperEngine 으로 바꾸고 싶을 때 또 가서 바꿔야 한다.
➡️ turboDrive() 는 (잘)변하지 않는 코드인데 계속 안에서 TurboEngin과 다른 Engin들(변하는 코드)을 바꿔줘야 한다. 이 방식은 좋은 코드로 성립되지 못한다.
➡️ 변하는 것과 변하지 않는 것은 분리해야 좋은 코드가 될 수 있다.
➡️ IoC 활용

  • loC에서는 라이브러리의 코드는 변하지 않는 코드로 자리잡고 있다.
    : 변경되는 부분을 밖으로 분리 ➡️ 좋은 설계
    : drive() 에서 사용할 부분을 사용자 측에서 제공을 해 줘야 함 new SuperEngin() ➡️ 수동 DI(의존성 주입)
    : 여기에 @AutoWired 를 붙이면 자동 주입이 된다. ➡️ 자동 DI(의존성 주입)

❗️ 라이브러리
: 다른 사람이 쓴 코드를 의미

❗️ library와 framework의 차이

  • library
    : 단순히 기능만 제공

  • framwork
    : 기능 뿐만 아니라 프로그래밍 패턴과 형식까지 제공

💡 DI(Dependency Injection)

✔️ 사용할 객체를 외부에서 주입받는 것
✔️ 클래스간의 의존관계를 스프링 컨테이너가 자동으로 연결해주는 것

❗️ Dependency: 객체가 다른 객체와 상호작용하는 것

클래스 A가 클래스 B,C와 상호작용한다면 객체 A는 객체B,C와 의존관계이다.

💡 bean


✔️ 재사용 가능한 컴포넌트, iv, getter&setter, 기본 생성자

📎 bean 종류

✔️ Servlet & JSP bean ➡️ EJB ➡️ Spring Bean

🫛 Servlet & JSP bean
: MVC의 Model, EL scope, JSP container가 관리

🫛 EJB(Enterprise Java Beans)
: 복잡한 규칙, EJB container가 관리

  • 대기업이 쓸려고 하니까 복잡해 졌음

🫛 Spring Bean
: POJO(Plain Old Java Object) (EJB의 반댓말)
: 단순, 독립적, Spring container가 관리

  • EJB를 심플하게 만듬

  • POJO
    : 해당 프레임워크에 종속된 "무거운" 객체를 만들게 된 것에 반발해서 사용되게 된 용어

  • Bean
    : Spring Container가 관리하는 객체

  • Spring container
    : Bean 저장소, Bean을 저장, 관리(생성, 소멸 연결)
    ➡️ 빈의 life cycle

    • 1️⃣ BeanFactory
      ➡️ Bean을 생성, 연결 등의 기본 기능을 정의
    • 2️⃣ ApplicationContext
      ➡️ BeanFactory를 확장해서 여러 기능을 추가 정의
      ➡️ Spring container랑 같은 말이라고 보면 된다.

💡 ApplicationContext


✔️ Bean을 저장, 관리(생성, 소멸 연결)
✔️ BeanFactory를 확장해서 여러 기능을 추가 정의

⚒️ ApplicationContext의 종류

  • 다양한 종류의 ApplicationContext(interface) 구현체를 제공

  • 설정을 XML로 하느냐 Java 코드로 하느냐로 나눈다. 또한 인지 웹이 아닌지로 나누게 된다.
    : 스프링이 꼭 웹 개발에서만 쓰이는 것이 아니다.

  • 사실 상, XML로 쓰느냐 java 코드로 쓰는냐는 같다.
    : 검색하면, XML 태그 대신에 어노테이션 쓰는거 뿐이다.
    : java 코드는 어노테이션을 쓰는 일

  • 각 장점은 있지만, java 코드가 유리(어노테이션)하다.
    : java 코드는 컴파일러가 다 체크해 준다. XML은 텍스트 문서여서 체크하기 쉽지 않다. XML validator가 다 검사하긴 하지만 아무래도 컴파일만 못하다. XML 설정이 관리하기 어렵기도 하다.
    : 점점 XML은 안쓰고 어노테이션 사용 추세로 가고있다.

➡️ root applicationContext & servlet applicationContext란?

💡 @Autowired


✔️ 스프링이 관리하는 빈(Bean)을 참조변수에 자동으로 주입해주는 어노테이션 1
✔️ By Type으로 값을 찾아서 연결

  • 검색 된 빈이 여러 개일 경우, 그 중에 참조변수와 이름이 일치하는 것을 주입

  • 주입 대상이 변수일 때, 검색된 빈이 1개가 아니면 예외 발생

  • 인터페이스 관련
    : https://okky.kr/questions/413840

  • 주입 대상이 배열일 때, 검색 된 빈이 여러 개라도 예외 발생 ❌
    : TurboEngine, SuperEngine 둘다 저장 가능

  • @Autowired(required = false) 일 때, 주입할 빈을 못찾아도 예외 발생 ❌
    ➡️ null

  • 인스턴스 변수, setter, 참조형 매개변수를 가진 생성자, 메서드에 적용
    : 초기화 하는 곳

  • @Value("red") 이렇게 값을 넣어주지 않으면 주입할 빈이 없다고 에러가 난다.

✏️ @Autowired는 생성자 생략 ⭕️

@Component
class Car {
    String color;
    int oil;
    Engine engine;
    Door[] doors;

//    @Autowired 생성자는 생략이 가능하다. 알아서 넣어주기 때문
    public Car(@Value("red") String color, @Value("100") int oil, Engine engine, Door[] doors) {
        this.color = color;
        this.oil = oil;
        this.engine = engine;
        this.doors = doors;
    }
    ...
  • 생성자에는 @Autowired 생략 가능하다. 알아서 넣어준다.
    : 웬만해서는 매개변수를 다 적어 줄 생성자를 이용해서 주입받게 하는 것이 좋다.
    : iv나 setter에 하나하나 직접 붙이는 것은 주입해야 할 빈을 빼먹을 수 있기 때문이다.

✏️ 기본 생성자가 있을 경우 @Autowired는 생성자 생략 ❌

@Component
class Car {
    String color;
    int oil;
    Engine engine;
    Door[] doors;
    
     public Car() {}

//    @Autowired 생성자는 생략이 가능하다. 알아서 넣어주기 때문
     public Car(@Value("red") String color, @Value("100") int oil, Engine engine, Door[] doors) {
        this.color = color;
        this.oil = oil;
        this.engine = engine;
        this.doors = doors;
    }
    ...
  • 기본 생성자가 있을 경우 @Autowired 를 생략할 시, 제대로 동작 하지 않는다.
    : 기본 생성자에 동작했기 때문에 null로 처리된다.

💡 @Resource


✔️ 스프링이 관리하는 빈(Bean)을 자동으로 참조 변수에 주입해주는 어노테이션 2
✔️ By Name으로 값을 찾아서 연결

  • Spring container(= ApplicationContext)에서 이름으로 빈을 검색해서 참조 변수에 자동 주입(DI)

  • 일치하는 이름의 빈이 없으면, 예외 발생

  • 이름 생략 가능하다.
    : 참조 변수의 이름이 빈이 된다. (Engine ➡️ engine)

  • 1️⃣ @Resource(byName) / 2️⃣ @Autowired(byType) ➡️ @Qualifier(byName)
    : spring은 2번 방식으로 동작함
    : 1번은 spring 어노테이션이 아님 (라이브러리 들고 와야 함)
    : 1번이나 2번 둘 중에 편하거 사용하면 됨

💡 @Component


✔️ <component-scan>으로 @Component가 클래스를 자동 검색해서 빈으로 등록

<context:component-scan base-package="kr.ac.jipark09" />
  • 서브 패키지까지 검색
package kr.ac.jipark09;
import org.springframwork.stereotype.*;

//<bean id="superEngine" class="kr.ac.jipark09.SuperEngine" />
//@Component("superEngine")
@Component
class SuperEngine extends Engine {}
  • @Controller, @Service, @Repository, @ControllerAdvice 의 메타 어노테이션

❗️ 왜 @Controller 어노테이션도 compontent-scan에 의해 빈으로 등록 될까?

: @Controller 정의에 @Component가 들어있다. 그래서 compontent-scan에 의해 같이 자동 검색이 된다. 나머지 어노테이션도 마찬가지다.

💡 @Value & @PropertySource


✔️ @Value
: 어노테이션이 필드나 메서드(혹은 생성자)의 파라미터 수준에서 표현식 기반으로 값을 주입해주는 어노테이션

✔️ @PropertySource
: 프로퍼티 데이터 불러옴

  • systemProperties 값이나 systemEnvironment값을 가져오다가 주입을 해 줄 수 있다.

  • systemPropertiesSystem.getProperties() 를 하면 systemProperties에 담긴 properties 객체가 저장된다. 거기에서 유저의 시간대를 가져온다.

  • systemEnvironmentSystem.getenv() 를 하면 환경변수들이 Map으로 저장이 된다. 거기에서 PWD(현재 작업 디렉토리)를 불러온다.

  • setting.properties 파일이 있으면 이 파일로 부터 값을 읽어올 수 있다. 외부파일에다가 등호를 구분자로 해서 key와 value을 구분해서 매개변수 키 값이 들어간다. 다른 타입도 가능하다.

✏️ setting.properties

autosaveDir=/autosave
autosave=true
autosaveInterval=30

✏️ @Value & @PropertySource 활용해 보기

@Component
@PropertySource("setting.properties")
class SysInfo {
    // 원래 있는 것에서 값 가져오기
    @Value("#{systemProperties['user.timezone']}")
    String timeZone;
    @Value("#{systemEnvironment['PWD']}")
    String currDir;
    // 내가 만들어 준 것에서 값 가져오기
    @Value("${autosaveDir}")
    String autosaveDir;
    @Value("${autosaveInterval}")
    int autosaveInterval;
    @Value("${autosave}")
    boolean autosave;

    @Override
    public String toString() {
        return "SysInfo{" +
                "timeZone='" + timeZone + '\'' +
                ", currDir='" + currDir + '\'' +
                ", autosaveDir='" + autosaveDir + '\'' +
                ", autosaveInterval=" + autosaveInterval +
                ", autosave=" + autosave +
                '}';
    }
}

public class ApplicationContextTest {
    public static void main(String[] args) {
    
        ApplicationContext ac = new GenericXmlApplicationContext("config.xml");
        System.out.println(ac.getBean(SysInfo.class));
        // 환경 변수 갖고 오기
        Map<String, String> map = System.getenv();
        System.out.println("map=" + map);
        Properties properties = System.getProperties();
        System.out.println("properties= " + properties);

    }
}

✏️ 결과 값

SysInfo{timeZone='Asia/Seoul', currDir='/Users/j_in/IdeaProjects/ch3', autosaveDir='/autosave', autosaveInterval=30, autosave=true}
map=...PWD=/Users/j_in/IdeaProjects/ch3....
properties= {gopherProxySet=false, awt.toolkit=sun.lwawt.macosx.LWCToolkit, java.specification.version=11 ...
  • @Value 값을 key값으로 채워주면, key과 연결된 value들이 채워지게 된다.

  • 어떤 값을 얻을려면 어떤 key값을 써야 하는지만 알면 System.properties 나 환경 변수 같은 것을 @Value 를 활용하여 프로그램에서 사용할 수 있다.

💡 Spring 어노테이션 vs 표준 어노테이션(JSR-330)


❗️ JSR: Java Spec Request
: spec에 요청사항이 있으면 번호를 붙여서 정의한다.

  • 표준 어노테이션: java에서 제공
  • Spring 어노테이션: 스프링에서 제공

✏️ 330으로 정의된 표준 어노테이션들

  • @Resource에 대응하는 스프링 어노테이션이 없다. annotation-api-jar 가 필요하다.

💡 빈의 초기화: <Property> & setter

✔️ <Property>를 이용한 빈 초기화: setter를 이용

// 새로운 객체가 계속 만들어짐
<bean id="car" class="kr.ac.jipark09.Car" scope="prototype">
	<property name="color" value="red" />
    <property name="oil" value="100" />
    <property name="engine" ref="engine" />
    <property name="doors">
    	<array value-type="kr.ac.jipark09.Door">
        	<ref bean="door" />
            <ref bean="door" />
        </array>
    </property>
</bean>
    
    
<bean id="engine" class="kr.ac.jipark09.Engine" />
<bean id="door" class="kr.ac.jipark09.Door" />

  • setter가 무조건 있어야 프로퍼티에 사용 가능하다.
    : setter 호출해서 정의하는 거하고 프로퍼티 태그하고 같다.
    ➡️ setter를 따로 호출하지 않아서 우리가 작성해야 하고 관리해야 하는 코드가 줄어드니까 좋다. 설정파일에 다 들어있다.

📎 list, set, map

  • String이나 기본형일 때는 value / 참조형일 때는 ref

💡 빈의 초기화: <construsctor-arg> & 생성자


✔️ <construsctor-arg>를 이용한 빈 초기화: 생성자를 이용

✏️ 생성자 이용

<bean id="car" class="kr.ac.jipark09.Car">
        <constructor-arg name="color" value="red" />
        <constructor-arg name="oil" value="100" />
        <constructor-arg name="engine" ref="engine"/>
        <constructor-arg name="doors">
            <array value-type="kr.ac.jipark09.Door">
                <ref bean="door"/>
                <ref bean="door"/>
            </array>
        </constructor-arg>
</bean>
<bean id="engine" class="kr.ac.jipark09.Engine" />
<bean id="door" class="kr.ac.jipark09.Door" scope="prototype" />


Reference
: https://fastcampus.co.kr/dev_academy_nks

profile
Fill in my own colorful colors🎨

0개의 댓글