19장. 스프링 의존성 주입과 제어 역전 기능
  • 객체 지향 프로그래밍 언어에서 클래스는 특정 기능을 수행하는 부품 역할을 함
  • 사용자의 요구 사항에 따라 클래스 기능을 변경하거나 다른 클래스 기능으로 대체해야 하는 경우에 더 수월하게 대체할 수 있게 도입된 기능이 의존성 주입(DI)제어 역행(IoC)

 

19.1 의존성 주입하기

  • (before) 어떤 한 클래스가 다른 클래스의 기능을 사용 => 개발자가 직접 코드에서 사용할 클래스의 생성자를 호출.
  • (before) 사용할 클래스와 사용될 클래스의 관계 => 개발자의 의해 직접 코드에서 부여

 

  • (now) 의존성 주입으로 컨테이너가 연관 관계를 직접 규정
    • 코드에서 직접적인 연관 관계가 발생하지 않으므로 각 클래스들의 변경이 자유로워짐
    • 약한 결합(한 기능을 변경해도 다른 기능의 변화는 거의 x)
      • 좋은 프로그램은 서로 관련이 있는 기능들은 강하게 결합, 관련이 없는 기능들은 약하게 결합하는 것
  • 전체 애플리케이션 > 각각의 기능을 담당하는 컨포넌트들 > 세부 기능을 수행하는 클래스들로 이루어짐
  • 한 클래스에서 다른 클래스의 기능을 사용하기 위해서 둘의 연관관계를 생성자가 아닌 스프링 프레임워크의 설정을 통해 맺어주자! 

 

- pro17에서 의존성 주입을 사용하지 않았을 때 (생성자 이용)

//BoardController.java
@WebServlet("/board/*")
public class BoardController extends HttpServlet {
...

public void init() throws ServletException {
    boardService = new BoardService();
    articleVO=new ArticleVO();
}


//BoardService.java
public class BoardService {

	BoardDAO boardDAO;
	
	public BoardService() {
		boardDAO=new BoardDAO();
	}


//BoardDAO.java
public class BoardDAO {

	private Connection con;
	private DataSource dataFactory;
	private PreparedStatement pstmt;
	
	public BoardDAO() {
		try {
			Context ctx=new InitialContext();
			Context envContext=(Context) ctx.lookup("java:/comp/env");
			dataFactory=(DataSource) envContext.lookup("jdbc/oracle");
		}catch(Exception e) {
			e.printStackTrace();
		}
	}

>> 만약 오라클이 아닌 MySQL로 변경한다면 BoardDAO 클래스의 기능과 경우에 따라 BoardService 클래스의 기능까지 변경해줘야 한다.

>> controller, service, dao를 인터페이스로 만들고 그들을 구현하는 impl 클래스를 각각 만들어 여기서 생성자를 구현한다. 따라서 mySQL로 바꾸고 싶다면 serviceImpl의 서비스 생성자에서 dao객체에 MySqlDAOImpl를 주입하는 것으로 소스코드 변경.

  >> 즉, 클래스의 객체를 사용할 때 인터페이스 타입으로 선언한 참조 변수로 접근해서 사용(인터페이스, 클래스는 참조타입)

  >> 클래스들 간 의존관계가 약해지기는 하지만 여전히 소스코드를 수정해야한다.

 

 

<의존성 주입을 적용하기>

  •  장점
    • 클래스들 간의 의존 관계를 최소화하여 코드를 단순화 함
    • 애플리케이션을 더 쉽게 유지 및 관리
    • 객체의 생성, 소멸, 객체간의 의존관계를 컨테이너가 제어
  • 구현 방법 : XML이나 애너테이션을 이용해 객체를 주입하여 의존관계를 맺어줌
  • 제어의 역전(IoC) : 기존 코드에서는 개발자가 직접 제어했지만 스프링 프레임워크에서는 스프링이 직접 제어
    • IoC의 종류도 여러가진데 스프링에서는 DI로 IoC의 기능을 구현
  • 의존하는 객체를 전달받기 위해 new 키워드를 사용하지 않고 1)생성자의 매개변수나 2)setter로 외부에서 객체를 주입 받아(컨테이너가 주입함) 사용한다! (빈에 해당하는 클래스에는 생성자나 setter가 있어야 구현 되어 있어야 함)

ex)

//방법1) 생성자의 매개변수로 객체를 주입하기 위한 생성자
public class BoardServiceImpl implements BoardService{
	private BoardDAO boardDAO;
    public BoardServiceImpl(BoardDAO boardDAO){
    	this.boardDAO=boardDAO;
    }
}


//방법2) setter을 이용해서 객체를 주입하기 위한 setter
public class BoardServiceImpl implements BoardService{
	private BoardDAO boardDAO;
    public void setBoardDAO(BoardDAO boardDAO){
    	this.boardDAO=boardDAO;
    }
}

 

 

19.2 의존성 주입 실습하기

  • 자바 프로젝트에서 DI 기능을 사용하기 위해서 스프링 관련 라이브러리 패스를 설정
    1. 자바 프로젝트 생성
    2. 프로젝트 아래 새 폴더 lib를 생성 후 스프링 DI 관련 라이브러리들을 복붙
    3. Build Path > Configure Build Path > Add JARs.. > 2에서 만든 lib폴더의 라이브러리를 모두 선택 
  • DI 설정(xml에서)
    1. XML 파일 생성(source 탭에서 코드 작성)
    2. <beans>태그로 스프링 실행 시 생성할 빈을 설정(bean : 스프링을 실행할 때 사용하는 클래스 객체) 

- <bean> 태그에 사용되는 여러가지 속성들

속성이름 설명
id 빈 객체의 고유 이름, 이 id를 이용해 빈에 접근
name 객체의 별칭
class 생성할 클래스(패키지 이름까지 입력해야 함)
constructor-arg 생성자를 이용해 값을 주입할 때 사용하는 속성
property setter를 이용해 값을 주입할 때 사용하는 속성

 

 

1) setter를 이용한 DI 기능

>> xml에서 빈 생성 후 property 태그를 이용해 name속성에 설정할 객체의 속성이름(key), value태그에 값(value)을 설정하여 값을 초기화

 

- person.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
                             "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
   <bean id="personService" class="com.spring.ex01.PersonServiceImpl">
      <property name="name">
         <value>홍길동</value>
      </property>
   </bean>
</beans>

>> 클래스에 name에 해당하는 setter가 있으면 value를 주입해줌

 

- service.java (인터페이스)

public interface PersonService {
	public void sayHello();
}

 

- serviceImpl.java

public class PersonServiceImpl implements PersonService {

	private String name;
	private int age;
	
	
	public void setName(String name) {
		this.name = name;
	}


	@Override
	public void sayHello() {
		System.out.println("이름 : "+name);
		System.out.println("나이 : "+age);
	}

}

 

- PersonTest.java ( 실행 클래스)

public class PersonTest {

	public static void main(String[] args) {
		BeanFactory factory=new XmlBeanFactory(new FileSystemResource("person.xml"));
		PersonService person=(PersonService) factory.getBean("personService");
		person.sayHello();
	}
}

 

 

2) 생성자를 이용한 DI 기능

 

- person.xml

<beans>
   <bean id="personService1" class="com.spring.ex02.PersonServiceImpl">
        <constructor-arg   value="이순신" />
   </bean>
   
   <bean id="personService2" class="com.spring.ex02.PersonServiceImpl">
       <constructor-arg   value="손흥민"  />
       <constructor-arg   value="23"  />
   </bean>

</beans>

>> value가 하나면 인자가 한개인 생성자로(나머지 속성은 디폴트값..), value가 두개 이상이면 해당 생성자 인자의 순서대로 넣어짐

 

- serviceImpl.java

public class PersonServiceImpl implements PersonService {

	private String name;
	private int age;

	public PersonServiceImpl(String name) {
		this.name = name;
	}

	public PersonServiceImpl(String name, int age) {
		this.name = name;
		this.age = age;
	}

	@Override
	public void sayHello() {
		System.out.println("이름 : "+name);
		System.out.println("나이 : "+age);
	}

}

- serviceTest.java

public static void main(String[] args) {
    BeanFactory factory=new XmlBeanFactory(new FileSystemResource("person.xml"));
    PersonService person1=(PersonService) factory.getBean("personService1");
    PersonService person2=(PersonService) factory.getBean("personService2");

    person1.sayHello();
    System.out.println();
    person2.sayHello();
}

 

 

19.3 회원 기능 이용해 의존성 주입 실습하기

- 위에선 빈의 속성 값으로 기본형 데이터를 주입했고 이번에는 의존 관계에 있는 다른 빈을 주입해보자!!

- ServiceImpl은 DAOImpl를 의존한다!

 

- member.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
                             "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
	<bean id="memberService" class="com.spring.ex03.MemberServiceImpl">
		<property name="memberDAO" ref="memberDAO"/> <!-- * -->
	</bean>
	<bean id="memberDAO" class="com.spring.ex03.MemberDAOImpl"/>
</beans>

>> 빈 생성과 의존 관계에 있는 빈을 속성에 주입해줌

>> * 주입되는 데이터가 참조형인 경우 ref 속성으로 ! (cf. 기본형일 땐 value)

 

- MemberService.java & MemberDAO.java (인터페이스)

public interface MemberService {
	public void listMembers();
}

///////////////////////////////////
public interface MemberDAO {
	public void listMembers();
}

 

- MemberServiceImpl.java & MemberDAOImpl.java

//service
public class MemberServiceImpl implements MemberService {

	private MemberDAO memberDAO;
    
	public void setMemberDAO(MemberDAO memberDAO) {
		this.memberDAO = memberDAO;
	}
    
	@Override
	public void listMembers() {
		memberDAO.listMembers();
	}

}

///////////////////////////////dao
public class MemberDAOImpl implements MemberDAO {

	@Override
	public void listMembers() {
		System.out.println("listMembers 메서드 호출");
	}
}

 

 

- MemberTest.java

public static void main(String[] args) {
    BeanFactory factory=new XmlBeanFactory(new FileSystemResource("member.xml"));
    MemberService service=(MemberService)factory.getBean("memberService");
    service.listMembers();
}

 

# 자바코드로 클래스 객체를 생성한 것이 아닌 스프링의 DI기능을 이용해서 빈을 생성후 의존성 주입!! #