Ch. 03 Spring DI 흉내내기
2023. 1. 11. 16:18
1. Spring DI 흉내내기
- 다형성, factory method 으로 변경에 유리한 코드짜기
- getObject를 사용하여 변경을 할때 코드에는 손을 대지않고 config.txt만 바꿔서 객체 생성이 가능
<config.txt>
car=com.fastcampus.ch3.diCopy1.Truck
engine=com.fastcampus.ch3.diCopy1.Engine
<Main.java>
class Car {}
class SportsCar extends Car{}
class Truck extends Car{}
class Engine{}
public class Main1 {
public static void main(String[] args) throws Exception{
Car car = getCar();
Car car2 = (Car)getObject("car");
Engine engine = (Engine)getObject("engine");
// getObject할때 해당 class가 생성되서 반환
System.out.println("car = "+ car);
System.out.println("car2 = "+ car2);
System.out.println("engine = "+ engine);
}
static Car getCar() throws Exception{
//config.txt내용을 읽어서 Properties에 map으로 저장 (String,String)
Properties p = new Properties();
p.load(new FileReader("config.txt"));
Class clazz = Class.forName(p.getProperty("car"));
return (Car)clazz.newInstance();
}
static Object getObject(String key) throws Exception{
//config.txt내용을 읽어서 Properties에 map으로 저장
Properties p = new Properties();
p.load(new FileReader("config.txt"));
//클래스 객체(설계도)를 얻어서
Class clazz = Class.forName(p.getProperty(key));
// 객체를 생성해서 반환
return clazz.newInstance();
}
}
<객체컨테이너 (ApplicationContext)>
- class의 map(객체 저장소)에다가 객체를 저장함
- 위의 properties에는 (String, String)이었는데 여기서는 객체를 저장해야하므로 (String, Object)이다.
(String으로 받은 다음 반복문으로 객체생성해서 map에 넣어줌)
<Main2.java>
...
class AppContext {
Map map; // 저장소
AppContext(){
try {
// 1. 먼저 Properties를 받음
Properties p = new Properties();
p.load(new FileReader("config.txt"));
// 2. Properties 내용을 map에 저장
map = new HashMap(p);
// 3. 반복문으로 map에 저장되어 있는 Class이름을 얻어서 객체를 생성한 후
// 다시 map에 저장
for(Object key : map.keySet()){
Class clazz = Class.forName((String) map.get(key));
map.put(key,clazz.newInstance());
}
} catch (Exception e) {
e.printStackTrace();
}
}
Object getBean(String key){
return map.get(key);
}
}
public class Main2 {
public static void main(String[] args) throws Exception{
AppContext ac = new AppContext();
Car car2 = (Car)ac.getBean("car");
Engine engine = (Engine)ac.getBean("engine");
// getBean할때 해당 class가 생성되서 반환
System.out.println("car2 = "+ car2);
System.out.println("engine = "+ engine);
}
}
<Component Scanning> - 자동 객체 등록하기
- 지금까지 config.txt에 사용할 객체를 등록해놓고 그걸 AppContext에서 읽어다가 map에 등록 (공통 객체만)
- 그외에도 @Component 방법이 있음
- class 앞에다가 @Component를 붙이면 반복문으로 @Component 붙은거 다찾아서 객체 생성해서 map에 저장
(guava 라이브러리)
<Main3.java>
@Component class Car {}
@Component class SportsCar extends Car{}
@Component class Truck extends Car{}
@Component class Engine{}
class AppContext {
Map map; // 저장소
AppContext(){
map = new HashMap();
doComponentScan();
}
private void doComponentScan() {
try {
// 1. 해당 패키지 내의 클래스 목록 set에 가져온다.
// 2. 반복문으로 클래스 하나씩 읽어와서 @Component이 붙어 있는지 확인
// 3. @Component가 붙어 있으면 객체를 생성해서 map에 저장
ClassLoader classLoader= AppContext.class.getClassLoader();
ClassPath classPath = ClassPath.from(classLoader);
Set<ClassPath.ClassInfo> set = classPath.getTopLevelClasses("com.fastcampus.ch3.diCopy3");
for(ClassPath.ClassInfo classInfo : set){
Class clazz = classInfo.load();
Component componet = (Component) clazz.getAnnotation(Component.class);
if(componet!=null){
String id = StringUtils.uncapitalize(classInfo.getSimpleName());
map.put(id, clazz.newInstance());
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Object getBean(String key) {return map.get(key);}
}
...
<by Name, by Type> - 객체 찾기
- 위에서 한게 이름(id)으로 찾기 이고 value를 돌면서 타입으로 찾기도 가능함 (instanceof 사용)
//byName
Object getBean(String key) {
return map.get(key);
}
//byType
Object getBean(Class clazz) {
for (Object obj : map.values()) {
// 이 obj가 해당 class의 자손이면 true
if (clazz.isInstance(obj)) return obj;
}
}
AppContext ac = new AppContext();
Car car = (Car)ac.getBean("car"); // byName으로 객체 검색
Car car2 = (Car)ac.getBean(Car.class); // byType으로 객체 검색
<Autowired> - 객체 자동 연결 (byType)
- Car, Engine, Door 객체를 생성하고 수동으로 car.engine=engine; car.door=door; 연결해줘야함
- class안에 인스턴스 변수 앞에 @AutoWired를 적어주면 맵에서 자동으로 찾아서 연결시켜줌
@Component class Car {
@Autowired Engine engine;
@Autowired Door door;
class AppContext {
Map map; // 저장소
AppContext() {
map = new HashMap();
doComponentScan();
doAutowired();
}
private void doAutowired() {
// map 에 저장된 객체의 iv중에 @Autowired가 붙어 있으면
// map 에 iv의 타입에 맞는 객체를 찾아서 연결(객체의 주소를 iv 저장)
try{
for(Object bean : map.values()){
for( Field fld : bean.getClass().getDeclaredFields()) {
if (fld.getAnnotation(Autowired.class) != null) { // byType
fld.set(bean, getBean(fld.getType())); // car.engine = obj
}
}
}
} catch (Exception e){
e.printStackTrace();
}
}
<Resource> - 객체 자동 연결 (byName)
- AutoWired랑 역할은 같음.
- class안에 인스턴스 변수 앞에 @Resource(name="engine2")로 연결 가능
- name 안적어주면 인스턴스 변수의 Type의 첫글자를 소문자로 바꿔주고 이름으로 넣음
@Component class Car {
@Resource Engine engine;
@Resource Door door;
class AppContext {
Map map; // 저장소
AppContext() {
map = new HashMap();
doComponentScan();
doAutowired();
doResouce();
}
private void doResouce() {
// map 에 저장된 객체의 iv중에 @Resource가 붙어 있으면
// map 에 iv의 이름에 맞는 객체를 찾아서 연결(객체의 주소를 iv 저장)
try{
for(Object bean : map.values()){
for( Field fld : bean.getClass().getDeclaredFields()) {
if (fld.getAnnotation(Resource.class) != null) { // byName
fld.set(bean, getBean(fld.getName())); // car.engine = obj
}
}
}
} catch (Exception e){
e.printStackTrace();
}
}
'Spring > 스프링의 정석' 카테고리의 다른 글
Ch. 03 Spring DB연결, DAO (0) | 2023.01.13 |
---|---|
Ch. 03 Spring DI (0) | 2023.01.12 |
Ch. 02 Spring DispatcherServlet, 데이터변환과 검증 (0) | 2023.01.10 |
Ch. 02 Spring 예외처리 (0) | 2023.01.09 |
Ch. 02 Spring 쿠키와 세션 (0) | 2023.01.09 |