스프링에서는 일반적인 JAVA 객체를 new로 생성하여 개발자가 관리하는 것이 아닌
Spring Container
에 모두 맡긴다
-> 개발자에서 프레임워크로 제어의 객체 관리의 권한이 넘어갔음으로 제어의 역전
이라고 한다
ex)
클래스와 서비스 간에 데이터를 주고받을 때 Spring Container에 생성하고자 하는 객체들이 이미 만들어져 들어가있고 Singleton 형태로 관리가 된다.
-> 컨테이너가 모든 객체를 관리(ex: 생명 주기)
내가 사용할 객체를 주입받는 다는 개념
ex) 웹으로부터 내가 사용할 객체를 주입받는다 가정
주입을 해주는 주체 -> Container(IOC)
의존성으로 부터 격리시켜 코드 테스트에 용이
DI를 통하여, 불가능한 상황을 MOCK(가짜 객체)와 같은 기술을 통하여, 안정적으로 테스트 가능
코드를 확장하거나 변경 할 때 영향을 최소화 한다(추상화)
순환 참조를 막을 수 있다
A객체가 B 객체에 의존하면 우선 코드로 부터 격리 -> B객체가 꼭 없더라도 A객체 테스트 가능
스프링 컨테이너는 스프링에서 자바 객체들을 관리하는 공간
자바 객체를 스프링에선 빈(Bean)
이라고 하는데, 스프링 컨테이너에서는 이 빈의 생성부터 소멸까지를 개발자 대신 관리 해 줌
파일에 저장된 정보의 형태
를 다른 것으로 변경하는 것
사람이 인지할 수 있는 형태의 데이터를 약속된 규칙에 의하여 컴퓨터가 사용하는 0과 1로 변환하는 과정을 통틀어 말하며 파일 압축이나 암호화 등의 목적으로 인코딩
public interface IEncoder {
String encode(String message);
}
import java.util.Base64;
public class Base64Encoder implements IEncoder {
public String encode(String message){
return Base64.getEncoder().encodeToString(message.getBytes());
}
}
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;
}
}
}
public class Encoder implements IEncoder{
// 의존을 가지고 있는 애를 외부에서 주입을 받기
private IEncoder iEncoder;
//default로 base64
// DI(외부에서 사용하는 객체 주입해주는 것) 적용 전
// public Encoder(){
// this.iEncoder = new Base64Encoder();
// }
public Encoder(IEncoder iEncoder){
this.iEncoder = iEncoder;
}
public String encode(String message){
return iEncoder.encode(message);
}
}
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
// Base64Encoder encoder = new Base64Encoder();
// 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 와 urlEncoder의 역할이 비슷하니 인터페이스 한개 뽑을수 있다
// 하지만 코드는 그래도 기존과 비슷.. 만들때 마다 계속 이렇게 생성해야할까?
// IEncoder encoder = new Base64Encoder();
// String result = encoder.encode(url);
//
// IEncoder urlEncoder = new UrlEncoder();
// String result2 = urlEncoder.encode(url);
// System.out.println(result2);
//DI로 바뀌는 과정을 보자
// 처음엔 BASE64 사용
// Encoder encoder = new Encoder();
// String result = encoder.encode(url);
// System.out.println(result);
// 위 코드를 url로 바꾸고자 한다 하면 위에 코드 그대로 하고 encoder의 생성자를 바꿈-> this.iEncoder = new UrlEncoder();
// 코드는 줄어들었지만 필요할때 마다 내부를 계속 바꾸는 건 비효율적
Encoder encoder = new Encoder(new Base64Encoder());
String result = encoder.encode(url);
System.out.println(result);
// url로 바꾸고자한다면 Encoder 안을 건드리는 것이 아닌 주입 객체만 바꿈
// new Encoder(new UrlEncoder());
}
}
package com.example.ioc2;
public interface IEncoder {
String encode(String message);
}
package com.example.ioc2;
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;
}
}
}
package com.example.ioc2;
import org.springframework.stereotype.Component;
import java.util.Base64;
// 스프링한테 객체(bean)를 관리해달라는 요청 -> @component : bean으로 등록
// -> loc2Application에 @SpringBootApplication가 붙고 -> 돋보기 클릭해보면 다루는 bean들이 나옴
// 이렇게 등록한다는 것은 스프링이 실행될때 @componenet 어노테이션이 붙은 객체를 싱글톤 형태로 만들어 Spring Container에서 관리한다는 뜻!
// 컨테이너에 접근하여 객체를 가져오기 위해서는 ApplicationContextProvider가 필요
// Component() 괄호안에 이름 입력시 bean 이름을 지정해줄 수 있음 디폴트 값은 맨앞에 대문자를 소문자로 한 값
@Component("base74Encoder") // 이를 지정하여 메인 밑의 클래스와 충돌할 일 없음
public class Base64Encoder implements IEncoder {
public String encode(String message){
return Base64.getEncoder().encodeToString(message.getBytes());
}
}
package com.example.ioc2;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
// @Component
public class Encoder implements IEncoder {
private IEncoder iEncoder;
// 두개의 인코더 사용하기 위해 위에 컴포넌트 어노테이션 제거하고 밑에처럼 생성자 수정
// public Encoder(@Qualifier("urlEncoder") IEncoder iEncoder){
// this.iEncoder = iEncoder;
// }
public Encoder(IEncoder iEncoder){
this.iEncoder = iEncoder;
}
// bean을 주입받을 수 있는 장소 -> 변수/생성자/ set 메서드
public void setiEncoder(IEncoder iEncoder){
this.iEncoder = iEncoder;
}
public String encode(String message){
return iEncoder.encode(message);
}
}
package com.example.ioc2;
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;
}
}
package com.example.ioc2;
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 Ioc2Application {
public static void main(String[] args) {
SpringApplication.run(Ioc2Application.class, args);
ApplicationContext context = ApplicationContextProvider.getContext();
// 여기를 name: urlEncode로 하면은 urlEncode로 바뀜
Encoder encoder = context.getBean("base64Encode", 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);
// 클래스 타입으로 찾기
// Base64Encoder base64Encoder = context.getBean(Base64Encoder.class);
// UrlEncoder urlEncoder = context.getBean(UrlEncoder.class);
//아직 까지는 코드내에서 직접 생성
// Encoder encoder = new Encoder(base64Encoder);
// String result = encoder.encode(url);
// System.out.println(result);
//이러한 방법도 가능
// encoder.setiEncoder(urlEncoder);
// result = encoder.encode(url);
// System.out.println(result);
// 인코더에 annotation을 붙이는 경우 Base64,url 둘다 컴포넌트 이기 때문에
// 스프링에서 어떤것을 매칭해야 할지 결정을 못해줌
// @Qualifier("urlEncoder") IEncoder iEncoder
// String url = "www.naver.com/books/it?page=10&size=20&name=spring-boot";
// Encoder encoder = context.getBean(Encoder.class);
// String result = encoder.encode(url);
// System.out.println(result);
// 두개의 Encoder 를 사용하고 싶은 경우 밑에 클래스 생성
}
}
@Configuration // 한개의클래스에서 여러가지 빈을 등록할거라는 의미
class AppConfig{
@Bean("base64Encode") // 미리 빈을 등록, base64Encoder를 컨테이너가 알아서 주입
public Encoder encoder(Base64Encoder base64Encoder){
return new Encoder(base64Encoder);
}
@Bean("urlEncode")
public Encoder encoder(UrlEncoder urlEncoder){
return new Encoder(urlEncoder);
}
}