스프링 프레임워크 Spring 의존성 주입 과 제어 역전 기능 2
19.2 의존성 주입 실습하기
이 절에서는 의존성 주입의 두 방법인 setter을 이용한 방식과 생성자를 이용한 방식을 각각 실습을 통해 확인해 봅니다. 먼저 setter를 이용해 DI를 실습하는 방식부터 알아봅니다.
19.2.1 setter를 이용한 DI 기능
아래는 setter를 이용해 실습할 클래스들의 계층 구조이다.
TestService
↑
TestServiceImpl
자바 프로젝트에스 DI 기능을 실습하려면 우선 스프링 관련 라이브러리의 패스를 설정해 주어야 한다.
1. 이클립스 상단에서 New > Project...를 선택후 Java Project 를 선택하고 Next
2. 프로젝트 이름으로 pro19를 입력 후 finish 클릭
3. 이클립스에서 자바 프로젝트를 생성한다. 자바 프로젝트 생성 시 이클립스의 Prespective 를 자바모드로 변경할지 무르면 Remember my desision 체크박스에 체크하고 Open Perspetive를 클릭
4. Project Explorer 에서 자바 프로젝트가 생성된것을 확인
5. 프로젝트 pro19 아래 새 폴더 lib 를 만든 후 이 책에서 제공하는 예제 소스에서 스프링 DI 관련 라이브러리를 복사해 붙여 넣는다.
6. pro19를 선택하고 마우스 오른쪽 버튼을 클릭한 후 Build Path > Cpnfigure Build Path..를 선택.
7. Libraries 탭에서 Classpath 를 선택하고 Add JARs...를 클릭
8. 앞에서 미리 만든 lib 폴더의 라이브러리들을 모두 선택한 후 OK를 클릭
9. Apply and Close를 클릭하여 이 내용을 적용.
10. Project Explorer 의 Referenced Libraries 에서 jar 파일들을 확인할 수 있다.
11. 이제 DI 설정을 할 차례이다. 스프링에서 Di 설정은 XML 파일에서 한다. 따라서 빈을 설정하는 person.xml 파일을 생성해야 한다. pro19위에서 마우스 오른쪽 버튼을 클릭한 후 New > Other...를 선택하고 선택 창에서 XML> XML File을 선택하고 Next를 클릭.
12. 파일 이름으로 person.xml을 입력하고 Finish를 클릭.
13. 프로젝트 이름 하위에 person.xml이 생성된 것을 확인 할 수 있다.
다음은 XML 파일에서 빈을 생성하는 데 사용되는 <bean> 태그의 속성들을 살펴보겠다.
아래 표에 <bean> 태그에 사용되는 여러 속성들을 정리했다.
<bean> 태그에 사용되는 여러가지 속성들
속성 이름 | 설명 |
id | 빈 객체의 교유 이름으로, 빈 id 를 이용해 빈에 접근한다. |
name | 객체의 별칭이다. |
class | 생성할 클래스이다. 패키지 이름까지 입력해야 한다. |
constructor-arg | 생성자를 이용해 값을 주입할 때 사용한다. |
property | setter를 이용해 값을 주입할 떄 사용한다. |
14. 다음과 같이 person.xml 을 작성한다. <beans> 태그로 스프링 실행 시 생성할 빈을 설정한다. 빈은 앞에서도 언급했듯이 스프링을 실행할 떄 사용하는 클래스 객체(인스턴스)이다.
코드 19-9 pro19/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"> //<bean>태그를 이용해PersonServiceImpl 객체(빈)를 생성한 후 빈 id를 personService로 지정한다.
<property name="name"> // PersonServiceImpl객체의 속성 name 값을 <value>태그를 이용해'홍길동'으로 초기화한다.
<value>홍길동</value>
</property>
</bean>
</beans>
코드 19-9 처럼 실행 클래스 실행 시 <bean>태그를 이용해 com.spring.ex01.PersonServiceImpl클래스에 대한 빈을 생성한다. 그리고 이 빈에 대해 접근할 수 있는 빈 id를 personService로 지정한 후 <property>태그를 이용해 PersonServiceImple 클래스 객체의 name 속성에 <value>태그의 값으로 초기화한다. 소스 코드에서 new 를 이용해 직접 객체를 생성하던 것을 person.xml 에서 설정한 것이다.
15. 이번에는 실습 관련 클래스를 구현할 차례이다. com.spring.ex01 패키지를 만들고 클래스 파일을 생성.
16. PersonService 인터페이스를 다음과 같이 작성한다. 인터페이스 PersonSerivce 에 추상메서드 sayHello()를 선언한다.
코드 19-10 pro19/com.spring/ex01/PersonService.java
package com.spring.ex01;
public interface PersonService{ //인터페이스 PersonService 에 추상메서드 sayHello()를 선언.
public void sayHello();
}
17. PersonServiceImpl 클래스를 다음과 같이 작성한다. 구현 클래스 PersonServiceImpl에서 인터페이스 PersonService를 구현하고 setter를 이용해 person.xml 에서 <value>태그로 설정한 값을 name 속성에 주입한다. 단 , age 속성은 setter 가 없으므로 빈이 생성되더라도 값이 초기화 되지 않는다.
코드 19-11 pro19/com.spring/ex01/PersonServiceImpl.java
package com.spring.ex01;
public class PersonServiceImpl implements PersonService{
private String name;
private int age;
public void setName(String name) { //<value> 태그의 값을 setter 를 이용해 설정한다.
this.name = name;
}
@Override
public void sayHello() {
System.out.println("이름 : " + name);
System.out.println("나이 : " + age );
}
}
18. 다음과 같이 실행 클래스인 PersonTest 클래스를 작성한다. 라이브러리에서 제공하는 클래스를 이용해 XML 파일을 읽어와서 빈을 생성해 사용한다. 실행 클래스를 실행하면 스프링의 XmlBeanFactory 클래스를 이용해 person.xml 의 설정대로 PersonServiceImpl 빈을 메모리에 생성한다. 이 빈에 대한 id 인 personService 로 접근하여 sayHello() 메서드를 호출한다.
코드 19-12 pro19/com.spring/ex01/PersonTest.java
package com.spring.ex01;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;
public class PersonTest{
public static void main(String[] args) {
//실행 시 person.xml을 읽어들여 빈을 생성한다.
BeanFactory factory = new XmlBeanFactory(new FileSystemResource("person.xml"));
PersonService person = (PersonService) factory.getBean("personService");//id가 personService인 빈을 가져온다.
// PersonService person = new PersonServiceImpl(); //더이상 자바 코드에서 객체를 직접 생성 하지 않아도 되므로 주석처리.
person.sayHello();// 생성된 빈을 이용해 name 값을 출력한다.
}
}
19. main() 메서드가 있는 실팽 클래스(PersonTest.java) 가 보이는 상태에서 이클립스 상단의 녹색 아이콘을 클릭해 자바 프로젝트를 실행한다.
20. 콘솔에 name 속성 값은 person.xml 에서 <value> 태그로 설정한 값이 출력되지만 age 속성값은 0이 출력된다.
19.2.2 생성자를 이용한 DI 기능
이번에는 생성자 주입 방식으로 DI를 실습해 보겠다.
1. com.spring.ex02 패키지를 만들고 다음과 같이 클래스를 추가한다.
2. person.xml 를 다음과 같이 작성한다. <bean> 태그를 이용해 id 가 personService1인 빈을 생성하고 <constructor-arg> 태그를 이용해 생성자 호출 시 생성자 매개변수로 <value> 태그의 값을 전달하여 속성을 초기화한다. 이처럼 PersonServiceImpl 클래스의 인자가 한개인 생성자를 이용하여 name 속성에 <value>태그의 값이 주입된다. 두 번째 <bean> 태그에서는 빈 생성 시 인자가 두개인 생성자를 호출하면서 두개의 값을 전달하여 각각의 속성을 초기화한다. 즉 , PersonServiceImpl 클래스의 인자가 두 개인 생성자를 이용해 name 과 age 속성에 <value> 태그의 값이 차례대로 주입된다.
코드 19-13 pro19/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>
<!--인자가 한 개인 생성자로 id가 personService1인 빈을 생성한다.
생성자로 value 인 이순신을 잔달하며 속성 name을 초기화 한다. -->
<bean id="personService1" class="com.spring.ex02.PersonServiceImpl">
<constructor-arg value="이순신"/>
</bean>
<!--인자가 두 개인 생성자로 id 가 personService2 인 빈을 생성한다.
생성자로 두개의 값을 전달하여 name 과 age를 초기화 한다. -->
<bean id="personService2" class="com.spring.ex02.PersonServiceImpl">
<constructor-arg value="손흥민"/>
<constructor-arg value="33"/>
</bean>
</beans>
3. PersonServiceImpl 클래스에서는 인자가 한 개인 생성자와 두 개인 생성자를 구현한다.
코드 19-14 pro19/com.spring/ex02/PersonServiceImpl.java
package com.spring.ex02;
public class PersonServiceImpl implements PersonService{
private String name;
private int age;
public PersonServiceImpl(String name) { // person.xml 에서 인자가 한개인 생성자 설정 시 사용된다.
this.name = name;
}
public PersonServiceImpl(String name, int age) { // person.xml 에서 인자가 두개인 생성자 설정 시 사용된다.
this.name = name;
this.age = age;
}
@Override
public void sayHello() {
System.out.println("이름 : " + name);
System.out.println("나이 : " + age + "살");
}
}
4. 실행 클래스인 PersonTest2를 다음과 같이 작성한다. id 가 personService1 인 빈에 접근 하여 sayHello() 메서드를 호출하면 age 는 0 으로 출력된다. 반면에 id 가 personService2 인 빈에 접근한 후 sayHello() 메서드를 호출하면 두 속성값이 모두 출력된다.
코드 19-15 pro19/com.spring/ex02/PersonText2.java
package com.spring.ex02;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;
public class PersonTest2{
public static void main(String[] args) {
BeanFactory factory = new XmlBeanFactory(new FileSystemResource("person.xml"));
PersonService person1 = (PersonService) factory.getBean("personService1");//id가 personService1인 빈을 가져온다.
person1.sayHello();// 생성된 빈을 이용해 name 값을 출력한다.
System.out.println();
PersonService person2 = (PersonService) factory.getBean("personService2");//id 가 personService2인 빈을 가져온다.
person2.sayHello();
}
}
5. 다음은 main() 메서드가 있는 실행 클래스 (PersonTest2.java)가 보이는 상태에서 실행버튼을 클릭하여 실행한 결과.
19.3 회원 기능 이용해 DI 실습하기
앞 절에서는 설정 파일에서 기본형 데이터를 빈의 속성 값으로 직접 주입해서 사용했다. 이번에는 빈이 주입되는 값이 의존 관계에 있는 다른 빈을 주입하는 경우를 살펴 보겠다.
아래에 회원 기능 관련 Service 클래스와 DAO 클래스의 계층 구조를 나타내었다. Service 클래스는 데이터베이스와의 연동을 위해 DAO 클래스에 의존하는 관계임을 알수 있다.
회원 관리 기능 클래스 계층 구조
이 책의 17장에서 다룬 게시판 실습예제에서는 Service 클래스는 소스코드에서 직접 dAO 객체를 생성한 후 DAO 에서 제공하는 메서드를 이용하여 데이터베이스와 연동했다. 그러면 앞의 예제의 경우 DI 를 이용하는 경우가 어떻게 다른지 실습을 통해 살펴보자.
1. 같은 프로젝트에 member.xml을 생성한다.
2. member.xml 에서는 두개의 빈을 동시에 생성한 후 id가 memberService 인 빈이 id 가 memberDAO 인 빈을 자신의 속성 memberDAO 에 바로 주입한다.
코드 19-16 pro19/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>
<!-- MemberServiceImpl 클래스를 이용해 id 가 memberService 인 빈을 만든다.
빈을 만들면서 setter 주입 방식으로 id 가 memberDAO인 빈을 자신의 속성에 주입한다. -->
<bean id="memberService" class="com.spring.ex03.MemberServiceImpl">
<property name="memberDAO" ref="memberDAO"/><!-- 주입되는 데이터가 기변형이 아닌 참조형인 경우 ref 속성으로 설정한다. -->
</bean>
<bean id="memberDAO" class="com.spring.ex03.MemberDAOImpl" /><!--id가 memberDAO인 빈을 MEmberDAOImpl을 이용해 만든다. -->
</beans>
의존하는 빈을 주입할 때는 주입되는 타입이 기본형 데이터가 아닌 참조형 데이터일 경우 ref 속성을 이용해 주입해야 한다는 것을 꼭 기억해야 한다. member.xml 에서 빈을 주입하는 과정은 다음과 같다.
member.xml 의 빈 주입 과정
3. MemberServiceImpl 클래스는 다음과 같이 setter 로 주입되는 빈을 받을 MemberDAO 타입의 속성과 setter를 이용해 구현한다.
코드 19-17 pro19/com/spring/ex03/MemberServiceImpl.java
package com.spring.ex03;
public class MemberServiceImpl implements MemberService{
private MemberDAO memberDAO; //주입되는 빈을 저장할 MemberDAO 타입의 속성을 선언한다.
public void setMemberDAO(MemberDAO memberDAO) { // 설정 파일에서 memberDAO 빈을 생성한 후 setter로 속성 memberDAO 에 주입한다.
this.memberDAO = memberDAO;
}
@Override
public void listMembers() {
memberDAO.listMembers(); // 주입된 빈을 이용해 listMembers() 메서드를 호출한다.
}
}
4. 다음은 주입되는 빈에 해당하는 MEmberDAOImpl 클래스이다.
코드 19-18 pro19/com/spring/ex03/MemberDAOImpl.java
package com.spring.ex03;
public class MemberDAOImpl implements MemberDAO {
@Override
public void listMembers() {
System.out.println("listMembers 메서드 호출");
System.out.println("회원정보를 조회합니다.");
}
}
5. 실행 클래스인 MEmberTest1 에서는 member.xml 을 읽어 들인 후 빈을 생성한다. 그리고 setter 주입 방식으로 주입한 후 빈 id 인 memberService 로 접근하여 listMembers() 메서드를 호출한다.
코드 19-19 pro19/com/spring/ex03/MemberTest01.java
package com.spring.ex03;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;
public class MemberTest01{
public static void main(String[] args) {
BeanFactory factory = new XmlBeanFactory(new FileSystemResource("member.xml"));//실행 시 member.xml 에 설정한 대로 빈을 생성한 후 주입한다.
MemberService service = (MemberService) factory.getBean("memberService");//id 가 memberService 인 빈을 가져온다.
service.listMembers();
}
}
6. main() 메서드가 있는 실행 클래스(MemberTest01.java)가 보이는 상태에서 시행 버튼을 클릭해 실행. 이클립스 콘솔에서 MemberDAO 의 listMembers() 메서드를 호출한다는 결과를 확인할 수 있다.
이 예제에서는 자바 코드로 어떤 클래스 객체도 생성하지 않았다. 오로지 스프링의 DI 기능을 이용해 빈을 생성했고, 의존관계에 있는 빈을 속성에 주입한 후 빈의 기능을 사용했다.
이후 진행할 스크링 관련 실습에서도 지금 실습한 방식으로 빈을 생성후 사용한다. 자바 코드로 직접 빈을 생성해서 사용하는 방식과 DI 를 이용해서 실습한 방식이 어떻게 다른지 잘 구분 할 수 있어야 한다.
코드 19 pro19/com/spring/ex03/MemberDAO.java
package com.spring.ex03;
public interface MemberDAO {
public void listMembers();
}
코드 19 pro19/com/spring/ex03/MemberService.java
package com.spring.ex03;
public interface MemberDAO {
public void listMembers();
}