[스프링SPRING]제어의 역행(IoC)과 의존성 주입(DI)

제어의 역행(IoC, Inversion of Control)

  • 객체, 메서드 호출시 처리하지않고 외부(spring)에서 처리한다.
  • 블럭 끼워넣기 개발이라고도 칭함




의존성 주입(DI, Dependency Injection)

  • 제어의 역행이 발생할 때, 스프링 내부에 있는 객체가 해당 클래스를 사용할 때 필요한 관계를 관리하는 동작 또는 기법
  • 여기서 의존성이란 객체가 혼자서는 처리할 수 없음을 뜻한다.
    • 따라서 객체를 직접 생성해서 구현한다 ex) jsp model2에서의 DAO객체생성
    • 관계를 약화시키면서 간접 구현 즉 호출해야한다.
  • 따라서 의존성 주입이란 객체를 직접 생성/제어하는 것이 아니라, 제어의 역행을 사용해서 특정 객체를 필요한 객체의 외부에 가져다가 연결하는 것을 뜻한다.
  • 객체가 필요한 어떤 객체를 생성자 혹은 setter를 통해 주입하는 것을 말한다.
  • 예를 들어 사람이란 객체가 있다. 머리, 몸통, 팔, 다리부분으로 구성되어있는 객체이다. 여기서 머리만 바꿔끼우면(= 주입) 사람A에서 사람B가 될 수 있다.




의존성 주입 3가지 방법

스프링 프레임워크는 @Autowired 애노테이션을 이용하여 다양한 의존성주입방법을 제공한다.
@Autowired 애노테이션은 Spring에게 의존성을 주입하라는 지시자 역할로 쓰이는데 생성자, 필드, 세터에 붙일 수 있다.

  1. 생성자 주입
    • @Autowired 애노테이션 생략가능 -> lombok을 활용해 @ReqyiredArgsConstructor와 상수 조합
      1
      2
      3
      4
      5
      @RestController
      @ReqyiredArgsConstructor
      public class ApiController{
      private final UserService service;
      }
  2. 필드 주입: 변수 선언부에 @Autowired 애노테이션을 붙인다.
    • 가장 편리한 방법이면서 실무에서 자주 사용하는 주입 방법
      1
      2
      3
      4
      5
      @RestController
      public class ApiController{
      @Autowired
      private UserService service;
      }
  3. 수정자 주입(Setter): Setter 메소드에 @Autowired 애노테이션을 붙인다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Component
    public class ApiController {
    private UserService userService;

    @Autowired
    public void setUserService(UserService userService) {
    this.userService = userService;
    }
    }

위의 세가지 방법 중 spring에서 권장하는 방법은 생성자를 통한 주입 방법이다.
생성자주입방법이 좋은 이유는 순환참조 방지, 테스트 코드 작성 용이하기 때문이다.




어노테이션을 쓰지않고 xml을 이용한 DI

1 생성자를 이용한 예시

  1. .xml파일에 spring beans DTD 연결
  2. .xml파일의 <bean>태그로 객체생성하기 (new 키워드랑 동일한 결과)
    • constructor-arg : “생성자 사용해서 값 주입”
    • 원하는 파라미터 갯수만큼 <constructor-arg>태그생성
      • 태그 생성시 순서 : 파라미터 순서그대로 대입됨
    • student.xml 코드
    • value담는 방식은 두가지 1. <value>태그사용 2.value=""속성사용
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      <?xml version="1.0" encoding="UTF-8"?>

      <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
      <beans>
      <bean id="studentBean" class="com.itwill.spring.ItwillStudent">
      <property name="name" value="홍길동"></property>
      <property name="classNum"><value>7</value></property>
      </bean>

      <!-- 생성자이용하여 DI -->
      <bean id="conBean1" class="com.itwill.spring.ItwillStudent">
      <constructor-arg>
      <value>사용자3</value>
      </constructor-arg>
      </bean>
      <bean id="conBean2" class="com.itwill.spring.ItwillStudent">
      <constructor-arg value="사용자4" />
      <constructor-arg value="1" />
      </bean>
      </beans>
  1. Student.java 인터페이스 생성
1
2
3
4
5
public interface Student {
//인사 구현
public void showInfo();

}
  1. ItwillStudent.java 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class ItwillStudent implements Student{
private String name;
private int classNum;

//생성자 만들기
public ItwillStudent(){}
public ItwillStudent(String name){
this.name = name;
}
public ItwillStudent(String name,int classNum){
this.name = name;
this.classNum = classNum;
}

//get메서드는 필요가 없다. set()은 필수
public void setName(String name) {
this.name = name;
}
public void setClassNum(int classNum) {
this.classNum = classNum;
}

@Override
public void showInfo() {
System.out.println("이름: " + name + ", 강의장: " + classNum);
}
@Override
public String toString() {
return "toString [이름: " + name + ", 강의장: " + classNum + "]";
}
}
  1. StudentTest.java 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class StudentTest {

public static void main(String[] args) {
//기존 자바사용해서 객체 구현 및 사용
ItwillStudent kim = new ItwillStudent();
kim.setName("킴스");
kim.setClassNum(8);
kim.showInfo();

System.out.println("-----생성자 사용하여 객체생성");
//생성자를 사용한 객체생성
ItwillStudent user1 = new ItwillStudent("사용자1");
user1.showInfo();
Student user2 = new ItwillStudent("사용자2", 7);
user2.showInfo();

System.out.println("-----생성자 사용하여 DI");
//생성자를 사용한 의존주입
ItwillStudent user3 = (ItwillStudent) fac.getBean("conBean1");
user3.showInfo();
ItwillStudent user4 = (ItwillStudent) fac.getBean("conBean2");
user4.showInfo();

}
}




2 수정자 Setter메서드 이용한 예시

  1. .xml파일에 spring beans DTD 연결

  2. .xml파일의 <bean>태그로 객체생성하기 (new 키워드랑 동일한 결과)

    • bean태그에서 사용할 수 있는 속성들
    • id : “빈 객체의 고유한 이름, 외부에서 접근하는 이름”
    • name : “객체의 별칭”
    • class : “생성할 클래스위치”
    • property : “set() 사용해서 값을 주입”
      • name속성 : bean태그의 class속성 위치에 있는 클래스의 멤버변수 중에 set 원하는 것을 지정
      • value담는 방식은 두가지 1. <value>태그사용 2.value=""속성사용
    • constructor-arg : “생성자 사용해서 값 주입”
    • person.xml 코드
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      <?xml version="1.0" encoding="UTF-8"?>

      <!-- DTD -->
      <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

      <!-- bean == 객체(스프링이 제공하는 객체) -->
      <beans>
      <!-- 객체생성 (new 키워드랑 동일한 결과) -->
      <bean id="personBean" class="com.itwill.spring.SubPerson">
      <!--속성 name은 class에 있는 멤버변수 명중에 선택하는 것임
      value담는 방식은 두가지 1. 태그사용 2. 속성사용
      -->
      <property name="name"><value>아이린</value></property>
      <property name="age" value="25"></property>
      </bean>
      </beans>
  3. IPerson.java 인터페이스생성

1
2
3
4
5
6
public interface IPerson {
//사람객체를 생성하기위한 틀

//인사 구현
public void sayHello();
}
  1. SubPerson.java 파일 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class SubPerson implements IPerson {

//사람의 정보를 저장하면서 기능 구현 가능
private String name;
private int age;

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}

@Override
public void sayHello() {
System.out.println(name+"님("+age+"세), 안녕하세요!");
}

@Override
public String toString() {
return "SubPerson정보 [이름=" + name + ", 나이=" + age + "]";
}
}
  1. PersonTest.java파일에서 객체 사용하기
    • BeanFactory는 외부의 파일을 읽어와서 처리함
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class PersonTest {

public static void main(String[] arg){

//1.자바에서 객체생성하여 사용하기
Person p1 = new Person();
p1.setName("홍길동");
p1.setAge(20);

System.out.println(p1);
System.out.println(p1.toString());

//인터페이스로 만든 SubPerson사용하고싶다면?
//의존의 관계가 강한결합 -> 인터페이스를 만들었으니 xml에서 약한 결합으로 바꿔줄수있다.
SubPerson sp = new SubPerson();
sp.setName("소미");
sp.setAge(22);
sp.sayHello();

//2.스프링을 사용해서 객체 생성하고 사용하기(xml)
BeanFactory fac = new XmlBeanFactory(new FileSystemResource("person.xml")); //xml파일명 기입
//의존주입
IPerson ip = (IPerson) fac.getBean("personBean"); //xml파일의 bean의 id값을 기입
ip.sayHello();
}
}

//출력값
사용자정보 [이름=홍길동, 나이=20]
사용자정보 [이름=홍길동, 나이=20]
소미님(22세), 안녕하세요!
log4j:WARN No appenders could be found for logger (org.springframework.beans.factory.xml.XmlBeanDefinitionReader).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
아이린님(25세), 안녕하세요!




강한 결합 VS 약한 결합

JPA에서 주로 사용하는 DI 방법은?

빌더 패턴을 사용하기를 권장하고 있다.
그 이유는 생성패턴의 종류와 빌더패턴을 사용해야하는 이유포스팅에서 확인할 수 있다.

Comments