본문 바로가기
dev/Spring

[Spring] Di 따라하기 3

by dev_Step 2022. 6. 22.

이전 1~2 까지는 Properties 객체를 통해서 config.xml 파일을 읽어와서

Map에 (K,V) 값으로 객체를 저장한 것을 getBean(String key) 또는 getBean(Class clazz) 를 통해서

가지고 와서 사용했는데 

이번에는 @Component를 통해서 Map에 (K , V) 로 저장해 보도록 하겠다. 또한 Map에 저장된 객체를 통해서

해당 @Autowied / @Resource가 붙은 객체에 자동으로 주입해보도록 하겠다.

 

>>> 1. @Component 를 통해서 Map에 객체 저장하기

  -- 아래의 코드중에 doComponentScan() 함수를 집중적으로 보자면

  -- ClassPath를 통해서 ClassLoader를 받아서 해당 패키지 내의 모든 클래스를 Set에 저장한 후 

  -- 저장된 Class를 for문을 통해서 한개씩 돌리면서 @Component 어노테이션이 붙은지 확인 한 후 

  -- @Component 가 붙은것만 객체를 생성하여 Map에 저장하도록 하였다.

@Component
class WoodHouse extends House{}
@Component
class BrickHouse extends House{}
@Component
class Window{}
@Component
class Floor{}

class Ditest4
{

Map map;

public DiTest4() throws Exception{
    map = new HashMap();
    doComponentSacn();

    for(Object s : map.keySet()){
        System.out.println((String)s + "<<-- @Component 붙여서 저장된 객체들");
    }


}

private void doComponentSacn() throws Exception{

    try {
        ClassLoader classLoader = DiTest4.class.getClassLoader();
        ClassPath classpath = ClassPath.from(classLoader);

        //패키지 내의 클래스를 다 저장.
        Set<ClassPath.ClassInfo> set = classpath.getTopLevelClasses("com.fastcampus.ch3.testDi4");
        // 저장된 클래스 중에 Component 어노테이션이 있는지 확인
        for(ClassPath.ClassInfo classInfo : set){
            Class clazz = classInfo.load();
            Component component = (Component) clazz.getAnnotation(Component.class);

            //@Component가 있다면 객체를 생성하여 map에 저장
            if(component!=null){
                String id = StringUtils.uncapitalize(classInfo.getSimpleName());
                map.put(id,clazz.newInstance());
            }
        }
    } catch(Exception e){
        e.printStackTrace();
    }


}

 

>> @Component를 통해서 Map에 저장된 객체를 나타낸 결과이다.

 

2.  Map에 저장된 객체를 통해서 해당 @Autowied / @Resource가 붙은 객체에 자동으로 주입해보도록 하겠다.

 

그러기 전에 앞서.

>> House Class에  매개변수로 Window, Floor 가 있다고 할때 해당 클래스에 객체를 주입해주려면

class House{
    Window window;
    Floor floor;

    @Override
    public String toString() {
        return "House{" +
                "window=" + window +
                ", floor=" + floor +
                '}';
    }
}

 

>> house.window = window

>> house.floor = floor 를 통해서 수동으로 객체를 주입해줘야 한다.

주입 전
주입 후

 

만약에 사용하는 멤버변수에 사용자객체가 많이 있다면 일일이 주입을 해줘야 하는 불편함이 있을텐데 이를 자동으로 주입하도록 하는 로직을 한번 알아보자

 

이번에는 

doAutoWired() , doResource() 함수를 집중적으로 확인해보자


@Component
class House{
    @Autowired  //@Resource
    Window window;

    @Autowired  //@Resource
    Floor floor;

    @Override
    public String toString() {
        return "House{" +
                "window=" + window +
                ", floor=" + floor +
                '}';
    }
}

@Component
class WoodHouse extends House{}
@Component
class BrickHouse extends House{}
@Component
class Window{}
@Component
class Floor{}

public class DiTest4 {

    Map map;

    public DiTest4() throws Exception{
        map = new HashMap();
        doComponentSacn();
        doAutoWired();
        doResource();
        for(Object s : map.keySet()){
            System.out.println((String)s + "<<-- @Component 붙여서 저장된 객체들");
        }


    }

    private void doResource() {
        try {
            for(Object bean : map.values()){
                for(Field fld : bean.getClass().getDeclaredFields()){
                    if(fld.getAnnotation(Resource.class)!=null){
                        fld.set(bean,getBean(fld.getName()));
                    }
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private void doAutoWired() {
        try {
            for(Object bean : map.values()){
                for(Field fld : bean.getClass().getDeclaredFields()){
                    if(fld.getAnnotation(Autowired.class)!=null){
                        fld.set(bean,getBean(fld.getType()));
                    }
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private void doComponentSacn() throws Exception{

        try {
            ClassLoader classLoader = DiTest4.class.getClassLoader();
            ClassPath classpath = ClassPath.from(classLoader);

            //패키지 내의 클래스를 다 저장.
            Set<ClassPath.ClassInfo> set = classpath.getTopLevelClasses("com.fastcampus.ch3.testDi4");
            // 저장된 클래스 중에 Component 어노테이션이 있는지 확인
            for(ClassPath.ClassInfo classInfo : set){
                Class clazz = classInfo.load();
                Component component = (Component) clazz.getAnnotation(Component.class);

                //@Component가 있다면 객체를 생성하여 map에 저장
                if(component!=null){
                    String id = StringUtils.uncapitalize(classInfo.getSimpleName());
                    map.put(id,clazz.newInstance());
                }
            }
        } catch(Exception e){
            e.printStackTrace();
        }


    }

    Object getBean(String key){
        return map.get(key);
    }

    Object getBean(Class clazz){
        for(Object obj : map.values()){
            if(clazz.isInstance(obj)){
                return obj;
            }
        }
        return null;
    }

    public static void main(String[] args) throws Exception {

        DiTest4 d = new DiTest4();

        House house = (House)d.getBean("house");
        Window window = (Window)d.getBean("window");
        Floor floor = (Floor)d.getBean("floor");

        //수동으로 객체를 주입해준다.
        //house.window = window;
        //house.floor = floor;

        System.out.println("house = " + house);
        System.out.println("window = " + window);
        System.out.println("floor = " + floor);


    }




}

 

>> doResource(), doAutoWried() 코드가 둘다 유사해보인다.

>> map에 저장되어 있는 객체들중 하나를 꺼내서 getDeclaredFields() 를 통해서 해당 클래스의 모든 필드에 접근 할 수 있는 권한을 얻은 fld를 얻게 되고, 해당 fld를 통해서 Autowired.class 어노테이션이 붙은 클래스인지 확인 할 수 있다.

이때 Autowired 어노테이션이 붙어있다면 해당 객체에 해당 타입의 객체(Bean)을 주입해준다.

 

>> 두개의 차이점은 fld.set(bean, getBean(fld.getType() // fld.getName()); 의 차이 인데.

>> @Autowired의 경우는 By Type 으로 Map에서 Value(객체/타입) 값을 가지고 해당 Bean을 찾아서 대입해주고,

>> @Resource의 경우는By Name 으로 Map에서 Key 값을 가지고 해당 Bean을 찾아서 대입해준다.

private void doAutoWired() {
    try {
                for(Object bean : map.values()){
                    for(Field fld : bean.getClass().getDeclaredFields()){
                        if(fld.getAnnotation(Autowired.class)!=null){
                            fld.set(bean,getBean(fld.getType()));
                        }
                    }
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
     }       
}   
   
private void doResource() {   
    try {
        for(Object bean : map.values()){
            for(Field fld : bean.getClass().getDeclaredFields()){
                if(fld.getAnnotation(Resource.class)!=null){
                    fld.set(bean,getBean(fld.getName()));
                }
            }
        }
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
}

 

 

 

 

 

 

 

 

'dev > Spring' 카테고리의 다른 글

[Spring] Transaction, Commit, Rollback  (0) 2022.07.06
[Spring] Di 따라하기 4  (0) 2022.06.22
[Spring] Di 따라하기 2  (0) 2022.06.22
[Spring] Di 따라하기 1  (0) 2022.06.22
[Spring] Export / Import  (0) 2022.06.20