스프링부트 기본 2. IoC / DI

min seung moon·2021년 6월 27일
0

Spring

목록 보기
28/50

1. IoC(Inversuib of Contol)

  • IoC(Inversuib of Contol)
    • 스프링에서는 일반적인 Java 개겣를 new로 생성하여 개발자가 관리하는 것이 아닌 Spring Container에 모두 맡긴다
    • 즉, 개발자에서 -> 프레임워크로 제어의 객체 관리의 권한이 넘어 갔음으로 "제어의 역전"이라고 한다

2. DI(Dependency Injection)

01. DI의 장점

  • 의존성으로 부터 격리시켜 코드 테스트에 용이하다
  • DI를 통하여, 불가능한 상황을 Mock와 같은 기술을 통하여, 안정적으로 테스트 가능
  • 코드를 확장하거나 변경할 때 영향을 최소화 한다(추상화)
  • 순환참조를 막을 수 있다

3. 프로젝트 시작

  • ioc 이름의 프로젝트 생성

01. 작동하는지 테스트


02. 테스트를 위한 하드코딩

  • 디자인 패턴, 분리, IoC 개념 없이 그냥 만들기
  • Main.java
package com.company.design.ioc;

public class Main {

    public static void main(String[] args) {
        String url = "www.naver.com/books/it?page=10&size=20&name=spring-boot";

        // Base 64 encoding
        /*
        Encoder encoder = new Encoder();
        String result = encoder.encode(url);
        System.out.println(result);
         */

        // url encoding
        UrlEncoder urlEncoder = new UrlEncoder();
        String urlResult = urlEncoder.encode(url);
        System.out.println(urlResult);

    }
}
  • Encoder.java
package com.company.design.ioc;

import java.util.Base64;

public class Encoder {
    public String encode(String message) {
        return Base64.getEncoder().encodeToString(message.getBytes());
    }
}
  • UrlEncoder.java
package com.company.design.ioc;

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

public class UrlEncoder {

    public String encode(String message) {
        try {
            return URLEncoder.encode(message, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        }
    }
}

03. 분리(추상화 및 DI)

  • Encoder.java와 UrlEncoder.java가 유사하다는 점을 확인 할 수 있다
    • 그렇기에 추상화를 활용하여 재사용성을 높인다

-1. 추상화 작업

  • IEncoder.Interface
package com.company.design.ioc;

public interface IEncoder {
    String encode(String message);
}
  • Encoder.java -> Base64Encoder.java
package com.company.design.ioc;

import java.util.Base64;

public class Base64Encoder implements IEncoder{
    public String encode(String message) {
        return Base64.getEncoder().encodeToString(message.getBytes());
    }
}
  • UrlEncoder.java
package com.company.design.ioc;

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

public class UrlEncoder implements IEncoder{

    public String encode(String message) {
        try {
            return URLEncoder.encode(message, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        }
    }
}
  • Main.java
package com.company.design.ioc;

public class Main {

    public static void main(String[] args) {
        String url = "www.naver.com/books/it?page=10&size=20&name=spring-boot";

        // Base 64 encoding
        IEncoder base64Encoder = new Base64Encoder();
        String base64Result = base64Encoder.encode(url);
        System.out.println(base64Result);


        // url encoding
        IEncoder urlEncoder = new UrlEncoder();
        String urlResult = urlEncoder.encode(url);
        System.out.println(urlResult);

    }
}


-2. Encoder.java 이용하기

  • Encoder.java
    • IEncoder 객체를 갖고 생성자에서 활용하기
    • 비효율적이고 본질(객체)을 건든다
package com.company.design.ioc;

import java.util.Base64;

public class Encoder{

    private IEncoder iEncoder;

    public Encoder() {
        this.iEncoder = new Base64Encoder();
        //this.iEncoder = new UrlEncoder();
    }

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

}
  • Main.java
package com.company.design.ioc;

public class Main {

    public static void main(String[] args) {
        String url = "www.naver.com/books/it?page=10&size=20&name=spring-boot";


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


    }
}




-3. DI 적용하기

  • 외부에서 내가 사용하는 객체를 주입을 시켜주는 것
    • 유사 클래스도 IEncoder를 상속 받아서 주입만 해주면 된다
  • Encoder.java
package com.company.design.ioc;

public class Encoder{

    private IEncoder iEncoder;

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

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

}

  • Main.java
package com.company.design.ioc;

public class Main {

    public static void main(String[] args) {
        String url = "www.naver.com/books/it?page=10&size=20&name=spring-boot";


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

    }
}


04. IoC Project 시작

  • Spring Container에서 관리
  • Spring Initializr 활용
  • 이전 작성한 파일 복 붙 (Main파일 제외)

01. 객체관리(spring boot에서의 DI)

  • 이전에 코드에서는 객체를 직접 만들어 주입하였기에 이번에는 spring container를 활용

-1. @Component

  • Spring(IoC) Container에 Bean을 등록하도록 하는 메타데이터를 기입하는 어노테이션
  • 개발자가 직접 작성한 Class를 Bean으로 등록하기 위한 어노테이션
  • SpringIocApplication의 @SpringBootApplication에 등록된다
  • 스프링이 실행되면 @Component Class를 찾아서 Singleton으로 Container에서 관리를 한다
  • Base64Encoder.java
package com.example.springioc;

import org.springframework.stereotype.Component;

import java.util.Base64;

// bean으로 등록된다
// SpringIocApplication의 @SpringBootApplication에서 등록된다
@Component
public class Base64Encoder implements IEncoder{
    public String encode(String message) {
        return Base64.getEncoder().encodeToString(message.getBytes());
    }
}
  • UrlEncoder.java
package com.example.springioc;

import org.springframework.stereotype.Component;

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

@Component
public class UrlEncoder implements IEncoder{

    public String encode(String message) {
        try {
            return URLEncoder.encode(message, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        }
    }
}

-2. Componenet를 활용할 클래스(ApplicationContextProvider)

  • ApplicationContextProvider.java
    • ApplicationContextAware.interface 상속
      • 빈이 실행되는 환경인 ApplicationContext 인스턴스에 접근할 수 있다.
      • bean을 관리하는 ApplicationContext 인스턴스에 직접 접근이 필요한 경우 사용하는 Interface이다
package com.example.springioc;

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;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }

    public static ApplicationContext getContext() {
        return context;
    }
}

-3. 실행을 위한 추가적인 수정

  • Encoder.java
package com.example.springioc;

public class Encoder{

    private IEncoder iEncoder;

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

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

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

-4. 테스트

  • Main Class SpringApplication.java
package com.example.springioc;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class SpringIocApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringIocApplication.class, args);

		ApplicationContext context = ApplicationContextProvider.getContext();

		// Bean 찾는 법

		// context에 받아온 class type으로 Bean 찾기기
		Base64Encoder base64Encoder = context.getBean(Base64Encoder.class);
		UrlEncoder urlEncoder = context.getBean(UrlEncoder.class);
		Encoder encoder = new Encoder(base64Encoder);

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

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

		encoder.setIEncoder(urlEncoder);
		result = encoder.encode(url);
		System.out.println(result);
	}

}


02. 주입 객체 지정

  • Encoder.java
    • @Component 추가
      • 생성자에서 IEncoder를 주입 받는데 현재 Base64Encoder와 UrlEncoder 2 케이스로 무엇을 주입 받을지 인식을 못함
      • @Qualifier("주입할 객체 Component 명")을 사용
      • 원래 base64Encoder인데 Component에서 이름 변경으로 base74Encoder로 받음
package com.example.springioc;

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

@Component
public class Encoder{

    private IEncoder iEncoder;

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

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

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

  • Base64Encoder.java
    • @Component("component 명")
package com.example.springioc;

import org.springframework.stereotype.Component;

import java.util.Base64;

// bean으로 등록된다
// SpringIocApplication의 @SpringBootApplication에서 등록된다
@Component("base74Encoder")
public class Base64Encoder implements IEncoder{
    public String encode(String message) {
        return Base64.getEncoder().encodeToString(message.getBytes());
    }
}

  • SpringApplicaition.java
    • Encoder.java에서 이미 주입할 객체의 타입을 지정해주었기 때문에 바로 Encoder.class 사용
package com.example.springioc;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class SpringIocApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringIocApplication.class, args);

		ApplicationContext context = ApplicationContextProvider.getContext();

		// Bean 찾는 법

		Encoder encoder = context.getBean(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);

	}

}


03. 주입 객체 다중 지정

  • @Configuration
    • @Configuration 한개의 클래스에서 여러개의 빈을 등록하는 어노테이션
  • Encoder.java
    • Bean을 따로 정하기 위해서 Component 해제
    • 다중 인젝션을 위해서 지정 해제
package com.example.springioc;

public class Encoder{

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

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

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

  • SpringApplication.java
    • AppConfig Class 생성 및 @Configuration추가
    • context.getBean("contextName", 주입 객체.class)
package com.example.springioc;

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 SpringIocApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringIocApplication.class, args);

		ApplicationContext context = ApplicationContextProvider.getContext();

		// Bean 찾는 법

		// context에 받아온 class type으로 Bean 찾기기
		// Encoder encoder = context.getBean(Encoder.class);

		// Bean 이름으로 찾기
		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);

	}

}

// Bean으로 등록
// @Configuration 한개의 클래스에서 여러개의 빈을 등록하는 어노테이션
@Configuration
class AppConfig {

	// 이름을 동일하게 해두면 안된다
	@Bean("base64Encode")
	public Encoder encoder(Base64Encoder base64Encoder) {
		return new Encoder(base64Encoder);
	}

	// 이름을 동일하게 해두면 안된다
	@Bean("urlEncode")
	public Encoder encoder(UrlEncoder urlEncoder) {
		return new Encoder(urlEncoder);
	}
}



profile
아직까지는 코린이!

0개의 댓글