- 객체 지향 프로그래밍 언어에서 클래스는 특정 기능을 수행하는 부품 역할을 함
- 사용자의 요구 사항에 따라 클래스 기능을 변경하거나 다른 클래스 기능으로 대체해야 하는 경우에 더 수월하게 대체할 수 있게 도입된 기능이 의존성 주입(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 기능을 사용하기 위해서 스프링 관련 라이브러리 패스를 설정
- 자바 프로젝트 생성
- 프로젝트 아래 새 폴더 lib를 생성 후 스프링 DI 관련 라이브러리들을 복붙
- Build Path > Configure Build Path > Add JARs.. > 2에서 만든 lib폴더의 라이브러리를 모두 선택
- DI 설정(xml에서)
- XML 파일 생성(source 탭에서 코드 작성)
- <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기능을 이용해서 빈을 생성후 의존성 주입!! #
'BackEnd > Spring' 카테고리의 다른 글
21장. 스프링 MVC 기능 (0) | 2022.03.01 |
---|---|
20장. 스프링 AOP 기능 (0) | 2022.02.28 |
18장. 스프링 프레임워크 시작하기 (0) | 2022.02.26 |
17장. 모델2 방식으로 효율적으로 개발하기 - ③ 답변형 게시판(답글쓰기, 페이징 구현) (2) | 2022.02.25 |
17장. 모델2 방식으로 효율적으로 개발하기 - ② 답변형 게시판(CRUD 구현) (0) | 2022.02.25 |
Comment