3. 객체 컨테이너(ApplicationContext) 만들기
- 컨테이너는 객체 저장소 : Map을 저장소로 사용한다.
AppContext ac = new AppContext();
Car car = (Car)ac.getBean("car");
Engine engine = (Engine)ac.getBean("engien");
class AppContext {
Map map = new HashMap();
AppContext() {
map.put("car", new SportsCar());
map.put("engine", new Engine());
}
Object getBean(String id) {return map.get(id);}
}
이렇게 하드코딩하면 변경에 불리하다.
[AppConfig.java]
class AppConfig {
@Bean public Car car() {return new SportsCar();}
@Bean public Engine engine() {return new Engine();}
}
이렇게 코드를 바꾸면 변경 사항이 생겼을 때 AppConfig.java만 수정하면 되기 때문에 변경에 유리하게 된다.
public AppContext(Class clazz) throws Exception { // AppConfig.class
Object appConfig = clazz.newInstance();
for(Method m : clazz.getDeclaredMethods())
for(Annotation anno : m.getDeclaredAnnotations())
if(anno.annotationType() == Bean.class) // @Bean이 있으면
map.put(m.getName(), m.invoe(appConfig, null));
}
| Key | Value |
| "car" | 0x100 |
| "engine" | 0x200 |
| ... | ... |
4. 객체 찾기 - by Name, by Type
AppContext ac = new AppContext();
Car car = (Car)ac.getBean("car"); // 이름(id)으로 찾기
Car car2 = (Car)ac.getBean(Car.class); // 타입으로 찾기
Object getBean(String id) { // 이름으로 찾기
return map.get(id);
}
0x100 반환 -> 지정한 이름을 Map의 Key에서 찾는다. (Key)
Object getBean(Class clazz) { // 타입으로 찾기
for(Object obj : map.values()) {
if(clazz.isInstance(obj)) // obj instanceof clazz
return obj;
}
return null;
}
타입을 비교할 땐 isinstanceof를 이용해서 찾는다. instanceof가 true인 것을 반환한다. (Value)
<실습>
- 이름으로 찾기 : by Name
[AppContext.java]
package com.fastcampus.ch3.di2;
import java.util.HashMap;
import java.util.Map;
public class AppContext {
Map map = new HashMap();
AppContext() {
map.put("car", new SportsCar());
map.put("engine", new Engine());
map.put("door", new Door());
}
public Object getBean(String id) {
return map.get(id);
}
}
[Main.java]
package com.fastcampus.ch3.di2;
class Car{
Engine engine;
Door door;
@Override
public String toString() {
return "Car{" +
"engine=" + engine +
", door=" + door +
'}';
}
public void setEngine(Engine engine) {
this.engine = engine;
}
public void setDoor(Door door) {
this.door = door;
}
}
class SportsCar extends Car {
@Override
public String toString() {
return "SportsCar{" +
"engine=" + engine +
", door=" + door +
'}';
}
}
class Engine {}
class Door {}
public class Main {
public static void main(String[] args) {
AppContext ac = new AppContext();
Car car = (Car)ac.getBean("car"); // by Name
System.out.println("car = " + car);
}
}
- 타입으로 찾기 : by Type
[Main.java 코드 추가]
public class Main {
public static void main(String[] args) {
AppContext ac = new AppContext();
Car car = (Car)ac.getBean("car"); // by Name
Car car2 = (Car)ac.getBean(Car.class); // by Name
System.out.println("car = " + car);
System.out.println("car2 = " + car2);
}
}
[AppContext.java 코드 추가]
AppContext() {
map.put("car", new SportsCar());
map.put("engine", new Engine());
map.put("door", new Door());
}
public Object getBean(Class clazz) {
for(Object obj : map.values())
if(clazz.isInstance(obj))
return obj;
return null;
}
public Object getBean(String id) {
return map.get(id);
}
}

둘 다 객체를 잘 가져오는 것을 확인할 수 있다.
- 자바 설정파일을 이용해서 하드 코딩을 대체
[Main.java 수정]
public class Main {
public static void main(String[] args) {
// AppContext(Class clazz) - 생성파일 자바클래스 지정
AppContext ac = new AppContext(AppConfig.class);
Car car = (Car)ac.getBean("car"); // by Name
Car car2 = (Car)ac.getBean(Car.class); // by Name
Engine engine = (Engine) ac.getBean("engine"); // by Name
Door door = (Door) ac.getBean(Door.class); // by Type
System.out.println("car = " + car);
System.out.println("car2 = " + car2);
System.out.println("door = " + door);
System.out.println("engine = " + engine);
}
}
[AppContext.java]
package com.fastcampus.ch3.di2;
import org.springframework.context.annotation.Bean;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class AppContext {
Map map = new HashMap();
AppContext() {
map.put("car", new SportsCar());
map.put("engine", new Engine());
map.put("door", new Door());
}
AppContext(Class clazz) {
Object config = null;
try {
config = clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
Method[] methods = clazz.getDeclaredMethods();
for(Method m : methods) {
System.out.println("m = " + m.getName());
for(Annotation anno : m.getDeclaredAnnotations()){
if(anno.annotationType() == Bean.class)
try {
map.put(m.getName(), m.invoke(config, null));
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
// map.put("car", new Car());
}
}
}
public Object getBean(Class clazz) {
for(Object obj : map.values())
if(clazz.isInstance(obj))
return obj;
return null;
}
public Object getBean(String id) {
return map.get(id);
}
}
[AppConfig.java]
package com.fastcampus.ch3.di2;
import org.springframework.context.annotation.Bean;
public class AppConfig {
// <bean id="car" class="com.fastcampus.ch3.Car">
@Bean public Car car() { // 메서드 이름이 Bean의 이름
// map.put("car", new Car());
Car car = new Car();
return car;
}
@Bean public Engine engine() {return new Engine();}
@Bean public Door door() {return new Door();}
}

Bean - 저장소가 관리하는 객체
5. 객체를 자동 연결 하기(1) - @Autowired
AppContext ac = new AppContext();
Car car = (Car)ac.getBean("car");
Engine engine = (Engein)ac.getBean("engine");
Door door = (Door)ac.getBean("door");
car.setEngine(engine); // car.engine = engine
cer.getDoor(door) // car.door = door
car.setEngine(engine); // car.engine = engine
cer.getDoor(door) // car.door = door
이 부분이 객체간의 관계를 설정해주는 부분인데, @Autowired를 이용해서 이를 자동 연결할 수 있다.
이걸 사용하면 해당 코드를 사용하지 않아도 된다.
class Car {
Engine engine;
Door door;
}
=>
class Car {
@Autowired Engine engine;
@Autowired Door door;
}
engine이라면 0x200을 넣어주고, door라면 0x300을 넣어준다. (예를 들어서!)
@Autowired는 by Type이다.
변경 사항에 더 유리해지고, 작성해야 하는 코드도 줄어들게 된다.

5. 객체를 자동 연결 하기(2) - @Resource
class Car {
@Resource Engine engine;
@Resource Door door;
}
@Resource를 사용하면 by Name으로 연결한다.
여기에도 @Resource(name="engine")이 생략된 것이다.
만약 다른 객체를 사용하고 싶으면, 이름을 지정해줄 수 있다.
class Car {
@Resource(name="engine2") Engine engine;
@Resource Door door;
}
이렇게 사용하면 해당 참조변수가 SuperEngine을 가리켜서 사용할 수 있게 된다.

<실습>

수동으로 engine과 door를 넣어줄 수 있지만 해당 코드를 주석처리 하고도 자동으로 값이 들어가도록 한다.
AppContext.java에 doAutowired메소드를 추가한다.
package com.fastcampus.ch3.di2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class AppContext {
Map map = new HashMap();
AppContext() {
map.put("car", new SportsCar());
map.put("engine", new Engine());
map.put("door", new Door());
}
AppContext(Class clazz) {
Object config = null;
try {
config = clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
Method[] methods = clazz.getDeclaredMethods();
for(Method m : methods) {
System.out.println("m = " + m.getName());
for(Annotation anno : m.getDeclaredAnnotations()){
if(anno.annotationType() == Bean.class)
try {
map.put(m.getName(), m.invoke(config, null));
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
// map.put("car", new Car());
}
}
doAutowired(); // @Autowired를 찾아서 빈(객체)간의 자동 연결처리
}
private void doAutowired() {
for(Object bean : map.values()) {
for(Field fld : bean.getClass().getDeclaredFields()) {
if(fld.getAnnotation(Autowired.class)!=null)
try {
fld.set(bean, getBean(fld.getType())); // car.engine = ob;
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
public Object getBean(Class clazz) {
for(Object obj : map.values())
if(clazz.isInstance(obj))
return obj;
return null;
}
public Object getBean(String id) {
return map.get(id);
}
}
[Main.java]
package com.fastcampus.ch3.di2;
import org.springframework.beans.factory.annotation.Autowired;
class Car{
Engine engine;
@Autowired Door door;
@Override
public String toString() {
return "Car{" +
"engine=" + engine +
", door=" + door +
'}';
}
public void setEngine(Engine engine) {
this.engine = engine;
}
public void setDoor(Door door) {
this.door = door;
}
}
class SportsCar extends Car {
@Override
public String toString() {
return "SportsCar{" +
"engine=" + engine +
", door=" + door +
'}';
}
}
class Engine {}
class Door {}
public class Main {
public static void main(String[] args) {
// AppContext(Class clazz) - 생성파일 자바클래스 지정
AppContext ac = new AppContext(AppConfig.class);
Car car = (Car)ac.getBean("car"); // by Name
Car car2 = (Car)ac.getBean(Car.class); // by Name
Engine engine = (Engine) ac.getBean("engine"); // by Name
Door door = (Door) ac.getBean(Door.class); // by Type
// 빈들끼리의 관계를 설정 - 수동
// car.setEngine(engine);
// car.setDoor(door);
System.out.println("car = " + car);
System.out.println("car2 = " + car2);
System.out.println("door = " + door);
System.out.println("engine = " + engine);
}
}
구분하기 위해 Door에만 @Autowired를 붙인다.

이번엔 doResource메소드를 생성해서 실습을 진행한다.
[AppContext.java]
private void doResource() {
for(Object bean : map.values()) {
for(Field fld : bean.getClass().getDeclaredFields()) {
if(fld.getAnnotation(Resource.class)!=null)
try {
fld.set(bean, getBean(fld.getName())); // car.engine = ob;
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
[Main.java]
class Car{
// @Resource(name="door")
@Resource Engine engine;
@Autowired Door door;

빈을 잘 찾아서 주입한 것을 확인할 수 있다.
'SpringBoot' 카테고리의 다른 글
| ch3 05. Spring 애너테이션 (0) | 2023.07.30 |
|---|---|
| ch3 04. Bean과 ApplicationContext (0) | 2023.07.30 |
| ch3 02. Java Reflection API (0) | 2023.07.19 |
| ch3 01. Spring DI의 원리(1) (0) | 2023.07.18 |
| ch2 16. thymeleaf 사용하기 (0) | 2023.07.18 |