IoC / DI

Minseo Kang·2023년 2월 11일
0

Spring Boot

목록 보기
12/27
post-thumbnail

01. DI(Dependency Injection)


개념

  • 객체 사용을 위한 주입

  • 의존성으로부터 격리시켜 코드 테스트에 용이

  • DI로 불가능한 상황을 Mock과 같은 기술을 통해 안정적인 테스트가 가능

  • 코드 확장하거나 변경할 때 영향을 최소화(추상화)

  • 순환참조 막을 수 있음




02. DI 실습


1) DI 개념을 사용하지 않는 경우


// Main.java
import java.io.UnsupportedEncodingException;

public class Main {

    public static void main(String[] args) throws UnsupportedEncodingException {

        String url = "www.naver.com/books/it?page=10&size=20&name=spring-boot";

        // Base64Encoder 객체 생성
        Base64Encoder base64Encoder = new Base64Encoder();
        String result = base64Encoder.encode(url);
        System.out.println("Base64Encoder : " + result);

        // URLEncoder 객체 생성
        UrlEncoder urlEncoder = new UrlEncoder();
        String urlResult = urlEncoder.encode(url);
        System.out.println("UrlEncoder : " + urlResult);
    }
}
// Base64Encoder.java
import java.util.Base64;

public class Base64Encoder {
    public String encode(String message) {
        return Base64.getEncoder().encodeToString(message.getBytes());
    }
}
// UrlEncoder.java
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

public class UrlEncoder {
    public String encode(String message) throws UnsupportedEncodingException {
        return URLEncoder.encode(message, "UTF-8");
    }
}

실행 결과


2) DI 개념을 사용하는 경우


// Main.java
public class Main {

    public static void main(String[] args) {

        // Encoding을 위한 url
        String url = "www.naver.com/books/it?page=10&size=20&name=spring-boot";

        // DI(외부에서 주입을 받는다)
        // Encoder 생성자의 매개변수를 new UrlEncoder() 로 바꾸면 해당 Encoder 주입받음
        Encoder encoder = new Encoder(new Base64Encoder());
        String result = encoder.encode(url);
        System.out.println(result);

    }

}
// Encoder.java
public class Encoder {

    private IEncoder iEncoder;

    public Encoder(IEncoder iEncoder) {
        this.iEncoder = iEncoder;
    }

    public String encode(String message) {
        return iEncoder.encode(message);
    }

}
// IEncoder.java
public interface IEncoder {
    String encode(String message);
}
// Base64Encoder.java
import java.util.Base64;

public class Base64Encoder implements IEncoder {
    @Override
    public String encode(String message) {
        return Base64.getEncoder().encodeToString(message.getBytes());
    }
}
// UrlEncoder.java
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

public class UrlEncoder implements IEncoder{
    @Override
    public String encode(String message) {
        try {
            return URLEncoder.encode(message, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }
}

실행 결과




03. IoC(Inversion of Control)


개념

  • 스프링에서는 일반적인 Java 객체를 new로 생성하여 개발자가 관리하는 것이 아님

  • 모든 객체의 제어 권한은 Spring Container에 있음

  • 개발자에서 프레임워크로 제어 객체 관리의 권한이 넘어갔으므로 "제어의 역전"이라고 함

  • Spring Container라는 공간에 우리가 사용하고자 하는 객체가 이미 만들어져 있고, 싱글톤 형태로 관리




04. IoC 실습


// IoCApplication.java
package com.example.IoC;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@SpringBootApplication
public class IoCApplication {

	public static void main(String[] args) {

		SpringApplication.run(IoCApplication.class, args);
		ApplicationContext context = ApplicationContextProvider.getContext();

		// 클래스로 찾기
		// Base64Encoder base64Encoder = context.getBean(Base64Encoder.class);
		// UrlEncoder urlEncoder = context.getBean(UrlEncoder.class);

		// 이름으로 찾기
		Encoder encoder = context.getBean("urlEncode", Encoder.class);
		String url = "www.naver.com/books/it?page=10&size=20&name=spring-boot";

		String result = encoder.encode(url);
		System.out.println(result);

	}

}

@Configuration // 한 개의 클래스에서 여러 개의 Bean을 등록할 것이라고 명시
class AppConfig {

	// Base64Encoder 주입
	// Bean으로 등록
	@Bean("base64Encode")
	public Encoder encoder(Base64Encoder base64Encoder) {
		return new Encoder(base64Encoder);
	}

	// UrlEncoder 주입
	// Bean으로 등록
	@Bean("urlEncode")
	public Encoder encoder(UrlEncoder urlEncoder) {
		return new Encoder(urlEncoder);
	}

}
package com.example.IoC;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class ApplicationContextProvider implements ApplicationContextAware {

    private static ApplicationContext context;

    // 여기에서 Spring이 주입해줌
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }

    public static ApplicationContext getContext() {
        return context;
    }

}
// Encoder.java
package com.example.IoC;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component
public class Encoder {

    private IEncoder iEncoder;

    public Encoder(@Qualifier("base74Encoder") IEncoder iEncoder) {
        this.iEncoder = iEncoder;
    }

    public void setIEncoder(IEncoder iEncoder) {
        this.iEncoder = iEncoder;
    }

    public String encode(String message) {
        return iEncoder.encode(message);
    }

}
// IEncoder.java
package com.example.IoC;

public interface IEncoder {
    String encode(String message);
}
// Base64Encoder.java
package com.example.IoC;

import org.springframework.stereotype.Component;
import java.util.Base64;

@Component("base74Encoder")
// Spring에게 객체로 관리해달라는 annotation, 이 객체가 Bean으로 등록됨
public class Base64Encoder implements IEncoder {
    @Override
    public String encode(String message) {
        return Base64.getEncoder().encodeToString(message.getBytes());
    }
}
// UrlEncoder.java
package com.example.IoC;

import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

@Component
public class UrlEncoder implements IEncoder{
    @Override
    public String encode(String message) {
        try {
            return URLEncoder.encode(message, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }
}

실행 결과




05. IoC 실습 정리


@Component

  • Spring Bean으로 등록
  • Application이 Bean을 관리하기 때문에 IoC 라고 함
  • 이름 따로 지어주고 싶으면 @Component(“빈 이름”)
  • 사용 : ApplicationContextProvider / Encoder / Base64Encoder / UrlEncoder

Spring Container에 접근해서 객체 가져오기

  • ApplicationContextProvider 클래스 만들기
  • ApplicationContextAware를 상속받음
  • setApplicationContext 메소드 오버라이딩
  • 주입 받아서 static 변수인 context 에 할당

Bean 찾기

  • by 클래스 타입, 이름
  • 클래스 타입으로 찾기
    Base64Encoder base64Encoder = context.getBean(Base64Encoder.class);
  • 이름으로 찾기
    Encoder encoder = context.getBean("urlEncode", Encoder.class);

Bean 주입받을 수 있는 곳

  • 변수 생성자, set 메소드
  • 사용 : Encoder 클래스에 setIEncoder 메소드 작성

@Qualifier

  • @Component가 하나이면 바로 매칭됨
  • 두 개 이상이면 Spring에서 어떤 것을 매칭해줘야 할지 결정을 못함
  • @Qualifier(“빈 이름”)
  • 사용 : Encoder의 생성자

@Configuration

  • 한 개의 클래스에서 여러 개의 Bean을 등록할 것이라고 명시
  • 한 클래스 내에서 Bean 두 개 이상 등록 시 @Bean(“이름”)
  • 사용 : AppConfig

개념 정리

  • 서비스 로직에서는 객체를 new로 만들지 않음
  • 항상 SpringContext로 가져올 것
  • 스프링이 실행될 때 @Component 가 붙은 것을 찾아서 직접 객체를 싱글톤 형태로 만들어서 Spring Container에서 관리함

0개의 댓글