티스토리 뷰
의존성 주입
- 의존성 주입이라는 것을 알기 전에는 어떤 한 클래스가 다른 클래스의 기능을 사용하려면 당연히 개발자가
직접 코드에서 사용할 클래스의 생성자를 호출해서 사용했다.
- 즉, 사용할 클래스와 사용될 클래스의 관계는 개발자에 의해 직접 코드에서 부여된다.
- 의존성 주입은 이런 연관 관계를 개발자가 직접 코딩을 통해 컴포넌트(클래스)에 부여하는 것이 아니라
컨테이너가 연관 관계를 직접 규정하는 것이다.
- 코드에서 직접적인 연관 관계가 발생하지 않으므로 각 클래스들의 변경이 자유로워 진다.(약간 결합)
- 전체 애플리케이션은 각각의 기능을 담당하는 컴포넌트들로 이루어진다. 그리고 각 컴포넌트들은 다시
세부 기능을 수행하는 클래스들로 이루어진다.
- 다른 클래스의 기능을 사용하려면 어떻게 해야할까?
- 소스 코드에서 다른 클래스의 생성자를 호출해서 사용할 경우 기능을 구현하는 과정에서 다른 변경 사항이
발생하면 빠르게 대처하기가 어렵다. 관련이 있는 모든 클래스들의 소스 코드를 수정해 주어야 하기 때문이다.
- 따라서 스프링 프레임워크에서는 각 클래스들의 연관 관계를 클래스들 사이에서 맺는 것이 아니라 스프링
프레임워크에서 설정을 통해 맺어줌으로써 클래스들이 연관 관계를 갖지 않게 구현했다.
의존성 주입 전의 게시판 기능
- 각 클래스들의 기능을 보면 다른 클래스의 기능을 사용하기 위해 소스 코드에서 직접 다른 클래스 객체를
생성한 후 메서드를 호출하여 연동한다.
* BoardController.java
@WebServlet("/board/*")
public class BoardController extends HttpServlet {
private static final String ARTICLE_IMAGE_REPO = "C:\\board\\article_image"; // 파일을 저장할 위치를 선언한다.
BoardService service;
ArticleVO vo;
public void init() throws ServletException {
service = new BoardService(); // BoardService 객체를 코드에서 직접 생성해 사용한다.
vo = new ArticleVO();
}
}
* BoardService.java
public class BoardService {
BoardDAO dao;
public BoardService() {
dao = new BoardDAO(); // BoardDAO 객체를 코드에서 직접 생성해 DB와 연동한다.
}
}
* BoardDAO.java
public class BoardDAO {
private Connection con;
private PreparedStatement pt;
private DataSource ds;
public BoardDAO (){
try {
Context ctx = new InitialContext();
Context envCtx = (Context)ctx.lookup("java:/comp/env");
ds = (DataSource)envCtx.lookup("jdbc/oracle");
}catch(Exception e) {
e.printStackTrace();
}
}
- BoardDAO 클래스에서는 오라클과 연동해 게시판 기능을 구현하고 있다. 그런데 만약 중간에 오라클에서 MySQL로 DB를 변경한다고 가정하면, 지금과 같은 경우에는 BoardDAO 클래스의 기능을 일일이 변경해 주어야 한다.
- 그뿐만 아니라 경우에 따라서는 BoardDAO 클래스를 사용하는 BoardService 클래스의 기능도 변경해야 할 수도 있다.
- 지금까지는 클래스를 사용하려면 자바에서 배웠듯이 자바 코드에서 클래스 생성자를 호출해 객체를 생성했다.
- 하지만 프로젝트 규모가 점점 커지는 상황에서 자바 코드에 직접 객체를 생성해서 사용하는 것은 복잡한 문제를 일으킬 수도 있다.
- 다른 클래스의 변경 사항이 연속적으로 다른 부분에 영향을 미친다면 이 방법은 좋은 방법이 아니다.
인터페이스를 적용한 게시판 기능
- 앞에서 다룬 게시판 기능 구현 시의 문제점을 인터페이스를 사용해서 해결한다.
-각각의 클래스가 인터페이스를 구현하도록 한다.
* BoardServiceImpl.java
public class BoardServiceImpl implements BoardService{
BoardDAO boardDAO;
public BoardService() {
boardDAO = new BoardOracleDAOImpl(); // 인터페이스를 이용해 하위 클래스 객체를 생성한 후 오라클 DB와 연동한다.
}
}
* BoardOracleDAOImpl.java
public class BoardOracleDAOImpl implements BoardDAO{
private DataSource ds;
Connection con;
PreparedStatement pt;
public BoardDAO() {
try{
Context ctx = new InitialContext();
Context envCtx = (Context)ctx.lookup("java:/comp/env");
ds = (DataSource) envCtx.lookup("jdbc/oracle");
}catch(Exception e){
e.printStackTrace();
}
...
}
}
- 개발 중에 MySQL과 연동하는 기능이 생겼다고 가정한다. 지금처럼 인터페이스로 구현한 경우에는
기존의 BoardOracleDAOImpl 클래스를 변경할 필요가 없다.
- BoardDAO 인터페이스를 구현한 또 다른 BoardMySqlDAOImpl 클래스를 구현한 후 BoardServiceImpl에서 사용하면 된다.
* BoardServiceImpl.java
public class BoardServiceImpl implements BoardService{
BoardDAO boardDAO;
public BoardService() {
// boardDAO = new BoardOracleDAOImpl();
boardDAO = new BoardMySqlDAOImpl(); // 인터페이스를 이용해 하위 객체를 생성한 후 MySql DB와 연동한다.
}
- 인터페이스를 이용해 각 클래스를 구현한 후 각 클래스의 객체를 사용할 때는 인터페이스 타입으로 선언한 참조 변수로 접근해서 사용하면 된다. 그러면 완전하지는 않아도 앞의 경우보다 훨씬 클래스들 간 의존관계가 약해진다.
- 그러나 인터페이스를 사용해도 여전히 BoardServiceImpl 클래스 자체에는 소스 코드를 직접 수정해야 한다.
의존성 주입을 적용한 게시판 기능
- 앞의 게시판 예제를 통해 클래스들의 의존 관계가 강하게 결합되어 있으면 여러 가지 문제가 발생할 수 있음을 알았다.
- 이번에는 스프링의 의존성 주입 기능을 이용해 각 클래스들 사이의 의존 관계를 완전히 분리하는 작업을 알아본다.
- 의존성 주입 적용 시의 장점
- 클래스들 간의 의존 관계를 최소화하여 코드를 단순화할 수 있다.
- 애플리케이션을 더 쉽게 유지 및 관리할 수 있다.
- 기존 구현 방법은 개발자가 직접 코드 안에서 객체의 생성과 소멸을 제어했지만, 의존성 주입은 객체의 생성,
소멸과 객체 간의 의존 관계를 컨테이너가 제어한다.
- 의존성 역전을 구현하려면 XML이나 애너테이션을 이용해 객체를 주입하여 객체들의 의존 관계를 맺어주면 된다.
- 즉, DI를 사용하여 각 객체들 간의 의존 관계를 최소화함으로써 코드를 단순화하고 유지보수를 쉽게 할 수 있다.
- DI는 객체의 생성, 소멸, 의존 관계를 개발자가 직접 설정하는 것이 아니라 XML이나 애너테이션 설정을 통해
경량 컨테이너에 해당하는 스프링 프레임워크가 제어한다.
- 따라서, 기존 코드에서는 개발자가 직접 객체를 제어했지만, 스프링 프레임워크에서는 객체의 제어를 스프링이 직접
담당하므로 제어의 역전(Inversion of Control, IoC)이라고 한다.
- IoC의 종류도 여러 가지이며, 일반적으로 스프링에서는 DI로 IoC의기능을 구현하므로 IoC보다는 DI라는 용어를 더 많이
사용한다.
DI를 적용한 게시판 기능
public class BoardServiceImpl implements BoardService{
BoardDAO boardDAO;
public BoardService() {
boardDAO = new BoardOracleDAOImpl(BoardDAO boardDAO){
this.boardDAO = boardDAO;
}
- BoardServiceImpl 클래스는 의존하는 BoardDAOImpl 객체를 전달받기 위해 new 키워드를 사용해 객체를
생성하지 않고 생성자를 호출할 때 외부에서 객체를 주입 받아 사용한다.
- new를 사용해 객체를 생성하는 것이 아니라 BoardServiceImpl 생성자를 호출할 때 컨테이너에 의해 주입되는 객체로
boardDAO 변수를 초기화한 것이다.
- 스프링에서는 의존(dependency)하는 객체를 컨테이너 실행 시 주입(Injection)하기 때문에 DI라고 부른다.
- 각 클래스를 bean(빈)이라고 부르는데, 이는 의존 관계를 설정하는 외부 XML 파일에서 각각의 객체를
<bean> 태그로 표시하기 때문이다.
Setter를 이용한 DI
- 프로젝트 하위에 XML 파일을 먼저 생성한다.
* 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.PersonServiceImpl"> <!-- <bean> 태그를 이용해 PersonServiceImpl 객체를 생성한 후 빈 id를 personService로 지정한다. -->
<property name="name"> <!-- PersonServiceImpl 객체의 속성 name 값을 <value> 태그를 이용해 '홍길동'으로 초기화한다. -->
<value>홍길동</value>
</property>
</bean>
</beans>
- 실행 클래스 실행 시 <bean> 태그를 이용해 com.spring.PersonServiceImpl 클래스에 대한 빈을 생성한다.
그리고 이 빈에 대해 접근할 수 있는 빈 id를 personService로 지정한 후 <property> 태그를 이용해
PersonServiceImpl 클래스 객체의 name 속성에 <value> 태그의 값으로 초기화한다.
- 소스 코드에서 new를 이용해 직접 객체를 생성하던 것을 person.xml에 설정한 것이다.
* PersonService.java
package com.spring;
public interface PersonService {
public void sayHello();
}
* PersonServiceImpl.java
package com.spring;
public class PersonServiceImpl implements PersonService{
private String name;
private int age;
public void setName(String name) { // XML 파일에서 설정한 <value> 태그의 값을 setter를 이용해 설정한다.
this.name = name;
}
@Override
public void sayHello() {
System.out.println("이름 : " + name);
System.out.println("나이 : " + age);
}
}
* PersonTest.java
- 실행 클래스이다. 라이브러리에서 제공하는 클래스를 이용해 XML 파일을 읽어와서 빈을 생성한다.
- 실행 클래스를 실행하면 스프링의 XmlBeanFactory 클래스를 이용해 person.xml의 설정대로 PersonServiceImpl 빈을
메모리에 생성한다.
- 이 빈에 대한 id인 personService로 접근하여 sayHello() 메서드를 호출한다.
package com.spring;
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");
// PersonService person = new PersonServiceImpl(); // 더 이상 자바코드에서 객체를 직접 생성하지 않아도 된다.
// 생성된 빈을 이용해 name 값을 출력한다.
person.sayHello();
}
}
- 해당 프로그램을 실행시켜 본다. 프로그램 실행 후 IDE의 콘솔을 보면 다음과 같이 출력된 것을 볼 수 있다.
생성자를 이용한 DI
* 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>
<!-- 인자가 한 개인 생성자로 id가 personService1인 빈을 생성한다. 생성자로 value인 이순신을 전달하여 속성 name을 초기화한다. -->
<bean id="personService1" class="com.spring.PersonServiceImpl">
<constructor-arg value="이순신" />
</bean>
<!-- 인자가 두 개인 생성자로 id가 personService2인 빈을 생성한다. 생성자로 두 개의 값을 전달하여 name과 age를 초기화한다. -->
<bean id="personService2" class="com.spring.PersonServiceImpl">
<constructor-arg value="손흥민" />
<constructor-arg value="23" />
</bean>
</beans>
* PersonServiceImpl.java
package com.spring;
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 + "살");
}
}
* PersonTest2.java
package com.spring;
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) {
// 실행 시 person.xml을 읽어 들려 빈을 생성한다.
BeanFactory factory = new XmlBeanFactory(new FileSystemResource("person.xml"));
// id가 personService1인 빈을 가져온다.
PersonService person1 = (PersonService) factory.getBean("personService1");
person1.sayHello();
System.out.println();
// id가 personService2인 빈을 가져온다.
PersonService person2 = (PersonService) factory.getBean("personService2");
person2.sayHello();
}
}
- 코드 작성 후 프로그램을 실행시켜 본다. 프로그램 실행 후 IDE의 콘솔을 보면 다음과 같이 출력된 것을 볼 수 있다.
회원 기능을 이용한 DI
- 앞에서는 설정 파일에서 기본형 데이터를 빈의 속성 값으로 직접 주입해서 사용했다.
- 이번에는 빈에 주입되는 값이 의존 관계에 있는 다른 빈을 주입하는 경우를 살펴본다.
* member.xml
- 프로젝트 디렉터리 아래에 생성한다.
- 의존하는 빈을 주입할 때는 주입되는 타입이 기본형 데이터가 아닌 참조형 데이터일 경우 "ref" 속성을 이용해
주입해야 한다.
<?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.MemberServiceImpl">
<property name="memberDAO" ref="memberDAO" /> <!-- 주입되는 데이터가 기본형이 아닌 참조형일 경우 'ref' 속성으로 설정한다. -->
</bean>
<bean id = "memberDAO" class="com.spring.MemberDAOImpl" />
</beans>
* MemberService.java
package com.spring;
public interface MemberService {
public void listMembers();
}
* MemberServiceImpl.java
package com.spring;
public class MemberServiceImpl implements MemberService{
private MemberDAO memberDAO;
public void setMemberDAO(MemberDAO memberDAO) { // setter를 이용한 DI
this.memberDAO = memberDAO;
}
@Override
public void listMembers() {
memberDAO.listMembers();
}
}
* MemberDAO.java
package com.spring;
public interface MemberDAO {
public void listMembers();
}
* MemberDAOImpl.java
package com.spring;
public class MemberDAOImpl implements MemberDAO{
@Override
public void listMembers() {
System.out.println("listMembers 메서드 호출");
System.out.println("회원 정보를 조회합니다.");
}
}
* MemberTest1.java
package com.spring;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;
public class MemberTest1 {
public static void main(String[] args) {
BeanFactory factory = new XmlBeanFactory(new FileSystemResource("member.xml"));
MemberService service = (MemberService) factory.getBean("memberService");
service.listMembers();
}
}
- 코드 작성 후 프로그램을 실행시켜 본다. 프로그램 실행 후 IDE의 콘솔을 보면 다음과 같이 출력된 것을 볼 수 있다.
- 이 예제에서 자바 코드로 어떤 클래스 객체도 생성하지 않았다. 오로지 스프링의 DI 기능을 이용해 빈을 생성했고,
의존 관계에 있는 빈을 속성에 주입한 후 빈의 기능을 사용했다.
'Spring' 카테고리의 다른 글
[Spring]스프링 애너테이션 (0) | 2024.08.17 |
---|---|
[Spring]MyBatis 연동하기 (0) | 2024.08.17 |
[Spring]JDBC 연동 (0) | 2024.08.17 |
[Spring]스프링 MVC (0) | 2024.08.17 |
[Spring]스프링이란? (0) | 2024.08.16 |
- Total
- Today
- Yesterday
- html css
- javaserverpage
- react
- a 태그
- Java Server Page
- 리액트
- 스프링
- 네트워크
- Network
- 세션
- 서브넷팅
- Session
- JSP
- HTML
- 서블릿
- httpServletRequest
- FMT
- Redux
- CSS
- nodejs
- el
- 제이쿼리
- 미들웨어
- Servlet
- script element
- Binding
- Cookie
- Spring
- 내장객체
- CSS 속성
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |