✔️ 제어의 역전: 제어의 흐름을 전통적인 방식과 다르게 뒤바꾸는 것
Control = flow Control
: if문과 for문을 의미 ➡️ 실행 흐름이 바뀌는 것 ➡️ 실행 흐름이 역전되는 것
전략 패턴이라고도 불림
전통적인 방식은 우리가 다른 사람이 작성한 코드(라이브러리)를 호출해서 그 결과를 받는 흐름
IoC에서는 우리가 제공한 코드 (사용자 코드)를 라이브러리 측에서 호출 함
IoC는 전통적인 방식에서 호출 흐름이 바뀜
💡 전통적인 방식에서는
turboDrive()
안에서 TurboEngin 을 생성해 주고 있다. 하지만 SuperEngine 으로 바꾸고 싶을 때 또 가서 바꿔야 한다.
➡️turboDrive()
는 (잘)변하지 않는 코드인데 계속 안에서 TurboEngin과 다른 Engin들(변하는 코드)을 바꿔줘야 한다. 이 방식은 좋은 코드로 성립되지 못한다.
➡️ 변하는 것과 변하지 않는 것은 분리해야 좋은 코드가 될 수 있다.
➡️ IoC 활용
drive()
에서 사용할 부분을 사용자 측에서 제공을 해 줘야 함 new SuperEngin()
➡️ 수동 DI(의존성 주입)@AutoWired
를 붙이면 자동 주입이 된다. ➡️ 자동 DI(의존성 주입)❗️ 라이브러리
: 다른 사람이 쓴 코드를 의미
library
: 단순히 기능만 제공
framwork
: 기능 뿐만 아니라 프로그래밍 패턴과 형식까지 제공
✔️ 사용할 객체를 외부에서 주입받는 것
✔️ 클래스간의 의존관계를 스프링 컨테이너가 자동으로 연결해주는 것
❗️ Dependency: 객체가 다른 객체와 상호작용하는 것
클래스 A가 클래스 B,C와 상호작용한다면 객체 A는 객체B,C와 의존관계이다.
✔️ 재사용 가능한 컴포넌트, iv, getter&setter, 기본 생성자
✔️ 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
BeanFactory
ApplicationContext
✔️ Bean을 저장, 관리(생성, 소멸 연결)
✔️ BeanFactory를 확장해서 여러 기능을 추가 정의
설정을 XML로 하느냐 Java 코드로 하느냐로 나눈다. 또한 웹인지 웹이 아닌지로 나누게 된다.
: 스프링이 꼭 웹 개발에서만 쓰이는 것이 아니다.
사실 상, XML로 쓰느냐 java 코드로 쓰는냐는 같다.
: 검색하면, XML 태그 대신에 어노테이션 쓰는거 뿐이다.
: java 코드는 어노테이션을 쓰는 일
각 장점은 있지만, java 코드가 유리(어노테이션)하다.
: java 코드는 컴파일러가 다 체크해 준다. XML은 텍스트 문서여서 체크하기 쉽지 않다. XML validator가 다 검사하긴 하지만 아무래도 컴파일만 못하다. XML 설정이 관리하기 어렵기도 하다.
: 점점 XML은 안쓰고 어노테이션 사용 추세로 가고있다.
✔️ 스프링이 관리하는 빈(Bean)을 참조변수에 자동으로 주입해주는 어노테이션 1
✔️ By Type으로 값을 찾아서 연결
검색 된 빈이 여러 개일 경우, 그 중에 참조변수와 이름이 일치하는 것을 주입
주입 대상이 변수일 때, 검색된 빈이 1개가 아니면 예외 발생
인터페이스 관련
: https://okky.kr/questions/413840
주입 대상이 배열일 때, 검색 된 빈이 여러 개라도 예외 발생 ❌
: TurboEngine, SuperEngine 둘다 저장 가능
@Autowired(required = false)
일 때, 주입할 빈을 못찾아도 예외 발생 ❌
➡️ null
인스턴스 변수, setter, 참조형 매개변수를 가진 생성자, 메서드에 적용
: 초기화 하는 곳
@Value("red")
이렇게 값을 넣어주지 않으면 주입할 빈이 없다고 에러가 난다.
@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
생략 가능하다. 알아서 넣어준다.@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
를 생략할 시, 제대로 동작 하지 않는다.✔️ 스프링이 관리하는 빈(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-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
: 프로퍼티 데이터 불러옴
systemProperties 값이나 systemEnvironment값을 가져오다가 주입을 해 줄 수 있다.
systemProperties는 System.getProperties()
를 하면 systemProperties에 담긴 properties 객체가 저장된다. 거기에서 유저의 시간대를 가져온다.
systemEnvironment는 System.getenv()
를 하면 환경변수들이 Map으로 저장이 된다. 거기에서 PWD(현재 작업 디렉토리)를 불러온다.
setting.properties 파일이 있으면 이 파일로 부터 값을 읽어올 수 있다. 외부파일에다가 등호를 구분자로 해서 key와 value을 구분해서 매개변수 키 값이 들어간다. 다른 타입도 가능하다.
autosaveDir=/autosave
autosave=true
autosaveInterval=30
@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
를 활용하여 프로그램에서 사용할 수 있다.
❗️ JSR: Java Spec Request
: spec에 요청사항이 있으면 번호를 붙여서 정의한다.
annotation-api-jar
가 필요하다.✔️ <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" />
✔️ <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