6.3 DI를 이용한 빈 의존성 관리
1. 의존 객체
- 의미 : 특정 작업을 수행할 때 사용하는 객체
2. 의존 객체 관리
1) 사용할 때 마다 의존 객체 생성하기
- 문제점 : 많은 가비지(garbage) 생성, 실행시간 지연
2) 의존 객체를 미리 생성해 두었다가 필요할 때 꺼내쓰기
3) 필요한 의존 객체를 사용 전에 미리 주입해 두기 ★★★
- 의존성 주입(Dependency Injection; DI)
- 의존 객체 주입을 위한 코드 준비
- MemberDao 인스턴스 변수와 셋터 메서드 추가
public class ContextLoaderListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
…
MemberDao memberDao = new MemberDao();
…
sc.setAttribute("/member/list.do",
new MemberListController().setMemberDao(memberDao));
public class MemberListController implements Controller {
MemberDao memberDao;
public MemberListController setMemberDao(MemberDao memberDao) {
this.memberDao = memberDao;
return this;
}
public String execute(Map<String, Object> model) throws Exception {
model.put("members", memberDao.selectList());
return "/member/MemberList.jsp";
}
}
3. 의존 객체와 느슨한 연대
- 인터페이스를 이용하여 대체가 용이하도록 만들자
1) 기존 방식: MemberDao가 클래스이다.
public class MemberDao {
DataSource ds;
public void setDataSource(DataSource ds) {
this.ds = ds;
}
public List<Member> selectList() throws Exception { … }
…
}
- SQL문이 MySQL DBMS에 맞춰져 있다.
- DBMS 마다 DAO를 따로 만든다면, MemberDao 자리를 다른 DAO 클래스로 대체할 수 있는가? No!
- 서브 클래스가 아니면, 대체할 수 없다!
- 해결책? MemberDao를 인터페이스로 선언
- 기존의 MemberDao는 MySQL 전용 DAO로 만든다.
- 모든 DAO 클래스는 MemberDao 인터페이스를구현한다.
4. DI 실습 ( _26_MVC_DI_DAO )
1) ContextLoaderListener.java
package spms.listener;
import javax.naming.InitialContext;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import spms.controller.LoginController;
import spms.controller.LogoutController;
import spms.controller.MemberAddController;
import spms.controller.MemberDeleteController;
import spms.controller.MemberListController;
import spms.controller.MemberUpdateController;
import spms.dao.MemberDAO;
public class ContextLoaderListener implements ServletContextListener {
//datasource의 장점 커넥션풀을 톰캣에서 지원해주기 때문에
//개발자가 직접 connection pool 객체를 만들어 줄 필요가 없다
//BasicDataSource ds;
@Override
public void contextInitialized(ServletContextEvent sce) {
//웹 어플리케이션이 실행되면 자동으로 DB커넥션 생성 및 MemeberDAO객체 생성
try {
System.out.println("contextInitialized");
ServletContext sc = sce.getServletContext();
//톰캣 서버에서 자원(커넥션)을 찾기 위한 InitailContext 객체 생성
InitialContext initialcontext = new InitialContext();
/*
* JNDI 사용
* WAS의 자원에 대한 고유 이름 정의
* 어플리케이션에서 서버 리소스를 접근할 때 사용하는 명명 규칙(톰캣)
* 서버마다 명명 규칙은 다름 (제우스는 또다른 규칙이 있음)
*
* 1) java:comp/env - 응용 프로그램 환경 항목
* 2) java:comp/env/jdbc - JDBC 데이터 소스
* 3) java:comp/ejb - EJB 컴포넌트
* 4) java:comp/UserTransaction - UserTransaction 객첵
* 5) java:comp/env/mail - JavaMail 연결 객체
* 6) java:comp/env/url - URL 정보
* 7) java:comp/env/jms - JMS 연결 객체
*
* */
DataSource ds = (DataSource)initialcontext.lookup("java:comp/env/jdbc/studydb");
/*
* ds = new BasicDataSource();
* ds.setDriverClassName(sc.getInitParameter("driver"));
* ds.setUrl(sc.getInitParameter("url"));
* ds.setUsername(sc.getInitParameter("username"));
* ds.setPassword(sc.getInitParameter("password"));
*/
MemberDAO memberDAO = new MemberDAO();
memberDAO.setDataSource(ds);
//생성된 MemberDAO객체를 ServletContext 데이터 보관소를 통해 서블릿끼리 공유
//sc.setAttribute("memberDAO", memberDAO);
//Controller 객체에 memberDAO 의존성 주입
//Controller 객체들을 서블릿 컨텍스트 영역에 저장 (키 값 : 서블릿 요청 url, value : 해당 컨트롤러 )
sc.setAttribute("/member/list.do", new MemberListController().setMemberDAO(memberDAO));
sc.setAttribute("/auth/login.do", new LoginController().setMemberDAO(memberDAO));
sc.setAttribute("/auth/login.do", new LogoutController());
sc.setAttribute("/member/add.do", new MemberAddController().setMemberDAO(memberDAO));
sc.setAttribute("/memberUpdate.do", new MemberUpdateController().setMemberDAO(memberDAO));
sc.setAttribute("/member/delete.do", new MemberDeleteController().setMemberDAO(memberDAO));
} catch(Exception e) {
e.printStackTrace();
}
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
try {
System.out.println("contextDestroyed");
} catch(Exception e) {
e.printStackTrace();
}
}
}
2) MemberListController.java
package spms.controller;
import java.util.Map;
import spms.dao.MemberDAO;
public class MemberListController implements Controller {
/*
* DI(Dependency Injection)
* 1) 클래스간 결합도 낮춤
* 2) MemberDAO 인터페이스를 선언하고 상속구현하므로써 다른 DBMS로의 전환 용이
* 3) 나중에 변경할 자동화 작업에 사용
*/
MemberDAO memberDAO = null;
public MemberListController setMemberDAO(MemberDAO memberDAO) {
this.memberDAO = memberDAO;
return this;
}
@Override
public String execute(Map<String, Object> model) throws Exception {
//리턴될 조회 결과 맵에 담음
model.put("memberList", memberDAO.selectlist());
return "/member/MemberList.jsp";
}
}
3) MemberAddController, MemberDeleteController, MemberUpdateController 코드 수정
- 전부 memberDAO를 전역변수로 선언 (밖으로 빼줌)
public class MemberUpdateController implements Controller {
MemberDAO memberDAO = null;
public MemberUpdateController setMemberDAO(MemberDAO memberDAO) {
this.memberDAO = memberDAO;
return this;
}
}
5. 실습 : DBMS 교체를 위한 인터페이스 생성 ( _27_MVC_Interface_DAO )
MySqlMemberDAO.java
package spms.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import spms.vo.Member;
public class MySqlMemberDAO implements MemberDAO {
/*
* Connection connection;
*
* //DAO객체는 servlet이 아니기 때문에 servletcontext에 있는 커넥션 직접 접근 불가능
* //memberlistServlet에서 커넥션을 객체를 DAO에 주입해줄 것 public void
* setConnection(Connection connection) { this.connection = connection; }
*/
DataSource ds;
public void setDataSource(DataSource ds) {
this.ds = ds;
}
@Override
public List<Member> selectlist() throws Exception {
Connection connection = null;
Statement stmt = null;
ResultSet rs = null;
String sqlSelect = "SELECT * FROM MEMBERS ORDER BY MNO ASC";
try {
connection = ds.getConnection();
stmt = connection.createStatement();
rs = stmt.executeQuery(sqlSelect);
ArrayList<Member> members = new ArrayList<Member>();
while(rs.next()) {
members.add(new Member()
.setNo(rs.getInt("MNO"))
.setName(rs.getString("MNAME"))
.setEmail(rs.getString("EMAIL"))
.setCreateDate(rs.getDate("CRE_DATE")));
}
return members;
} catch(Exception e) {
throw e;
} finally {
try {
if(rs != null) {
rs.close();
}
} catch(Exception e) {
e.printStackTrace();
}
try {
if(stmt != null) {
stmt.close();
}
} catch(Exception e) {
e.printStackTrace();
}
try {
if(connection != null) {
connection.close();
}
} catch(Exception e) {
e.printStackTrace();
}
}
}
//MemberAddServlet에서 입력 폼으로 입력받은 데이터를 member객체로 담아서
//DAO로 전달할 예정
@Override
public int insert(Member member) throws Exception {
int result = 0;
Connection connection = null;
PreparedStatement stmt = null;
final String sqlInsert = "INSERT INTO MEMBERS(EMAIL, PWD, MNAME, CRE_DATE, MOD_DATE)" +
"VALUES(?, ?, ?, NOW(), NOW())";
try {
connection = ds.getConnection();
stmt = connection.prepareStatement(sqlInsert);
stmt.setString(1, member.getEmail());
stmt.setString(2, member.getPassword());
stmt.setString(3, member.getName());
//insert 성공하면 1 int 값 리턴
result = stmt.executeUpdate();
} catch(Exception e) {
throw e;
} finally {
try {
if(stmt != null) {
stmt.close();
}
} catch(Exception e) {
e.printStackTrace();
}
try {
if(connection != null) {
connection.close();
}
} catch(Exception e) {
e.printStackTrace();
}
}
return result;
}
@Override
public int delete(int no) throws Exception {
int result = 0;
Connection connection = null;
Statement stmt = null;
final String sqlDelete = "DELETE FROM MEMBERS WHERE MNO=" + no;
try {
connection = ds.getConnection();
stmt = connection.createStatement();
result = stmt.executeUpdate(sqlDelete);
} catch(Exception e) {
throw e;
} finally {
try {
if(stmt != null) {
stmt.close();
}
} catch(Exception e) {
e.printStackTrace();
}
try {
if(connection != null) {
connection.close();
}
} catch(Exception e) {
e.printStackTrace();
}
}
return result;
}
//해당 멤버 데이터 조회
@Override
public Member selectOne(int no) throws Exception {
Member member = null;
Connection connection = null;
Statement stmt = null;
ResultSet rs = null;
final String sqlSelectOne = "SELECT * FROM MEMBERS WHERE MNO=" + no;
try {
connection = ds.getConnection();
stmt = connection.createStatement();
rs = stmt.executeQuery(sqlSelectOne);
if(rs.next()) {
member = new Member()
.setNo(rs.getInt("MNO"))
.setEmail(rs.getString("EMAIL"))
.setName(rs.getString("MNAME"))
.setCreateDate(rs.getDate("CRE_DATE"));
} else {
throw new Exception("해당 번호의 회원을 찾을 수 없습니다.");
}
} catch(Exception e) {
throw e;
} finally {
try {
if(stmt != null) {
stmt.close();
}
} catch(Exception e) {
e.printStackTrace();
}
try {
if(connection != null) {
connection.close();
}
} catch(Exception e) {
e.printStackTrace();
}
}
return member;
}
//해당 멤버 데이터 수정
@Override
public int update(Member member) throws Exception {
int result = 0;
Connection connection = null;
PreparedStatement stmt = null;
final String sqlUpdate = "UPDATE MEMBERS SET EMAIL=?, MNAME=?, MOD_DATE=NOW() WHERE MNO=?";
try {
connection = ds.getConnection();
stmt = connection.prepareStatement(sqlUpdate);
stmt.setString(1, member.getEmail());
stmt.setString(2, member.getName());
stmt.setInt(3, member.getNo());
result = stmt.executeUpdate();
} catch(Exception e) {
throw e;
} finally {
try {
if(stmt != null) {
stmt.close();
}
} catch(Exception e) {
e.printStackTrace();
}
try {
if(connection != null) {
connection.close();
}
} catch(Exception e) {
e.printStackTrace();
}
}
return result;
}
@Override
public Member exist(String email, String password) throws Exception {
System.out.println(email);
System.out.println(password);
Member member = null;
Connection connection = null;
PreparedStatement stmt = null;
ResultSet rs = null;
final String sqlExist = "SELECT * FROM MEMBERS WHERE EMAIL=? AND PWD=?";
try {
connection = ds.getConnection();
stmt = connection.prepareStatement(sqlExist);
stmt.setString(1, email);
stmt.setString(2, password);
rs = stmt.executeQuery();
if(rs.next()) {
member = new Member()
.setName(rs.getString("MNAME"))
.setEmail(rs.getString("EMAIL"));
} else {
return null;
}
} catch(Exception e) {
throw e;
} finally {
try {
if(stmt != null) {
stmt.close();
}
} catch(Exception e) {
e.printStackTrace();
}
try {
if(connection != null) {
connection.close();
}
} catch(Exception e) {
e.printStackTrace();
}
}
return member;
}
}
MemberDAO.java
package spms.dao;
import java.util.List;
import spms.vo.Member;
/*
* 인터페이스 생성 이유
*
* Oracle, MySql, MSSql 등 DBMS 변경시
* 이 인터페이스를 상속받는 DAO 클래스를 정의하되
* 각 DBMS 특성에 맞게 구현하여 DBMS 교체를 쉽게 함
* 메서드의 역할과 리턴 값은 동일하게 처리
*
* */
public interface MemberDAO {
public List<Member> selectlist() throws Exception;
public int insert(Member member) throws Exception;
public int delete(int no) throws Exception;
public Member selectOne(int no) throws Exception;
public int update(Member member) throws Exception;
public Member exist(String email, String password) throws Exception;
}
ContextLoaderListener.java
package spms.listener;
import javax.naming.InitialContext;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.sql.DataSource;
import spms.controller.LoginController;
import spms.controller.LogoutController;
import spms.controller.MemberAddController;
import spms.controller.MemberDeleteController;
import spms.controller.MemberListController;
import spms.controller.MemberUpdateController;
import spms.dao.MySqlMemberDAO;
public class ContextLoaderListener implements ServletContextListener {
//datasource의 장점 커넥션풀을 톰캣에서 지원해주기 때문에
//개발자가 직접 connection pool 객체를 만들어 줄 필요가 없다
//BasicDataSource ds;
@Override
public void contextInitialized(ServletContextEvent sce) {
//웹 어플리케이션이 실행되면 자동으로 DB커넥션 생성 및 MemeberDAO객체 생성
try {
System.out.println("contextInitialized");
ServletContext sc = sce.getServletContext();
//톰캣 서버에서 자원(커넥션)을 찾기 위한 InitailContext 객체 생성
InitialContext initialcontext = new InitialContext();
/*
* JNDI 사용
* WAS의 자원에 대한 고유 이름 정의
* 어플리케이션에서 서버 리소스를 접근할 때 사용하는 명명 규칙(톰캣)
*
* 1) java:comp/env - 응용 프로그램 환경 항목
* 2) java:comp/env/jdbc - JDBC 데이터 소스
* 3) java:comp/ejb - EJB 컴포넌트
* 4) java:comp/UserTransaction - UserTransaction 객첵
* 5) java:comp/env/mail - JavaMail 연결 객체
* 6) java:comp/env/url - URL 정보
* 7) java:comp/env/jms - JMS 연결 객체
*
* */
DataSource ds = (DataSource)initialcontext.lookup("java:comp/env/jdbc/studydb");
/*
* ds = new BasicDataSource();
* ds.setDriverClassName(sc.getInitParameter("driver"));
* ds.setUrl(sc.getInitParameter("url"));
* ds.setUsername(sc.getInitParameter("username"));
* ds.setPassword(sc.getInitParameter("password"));
*/
MySqlMemberDAO memberDAO = new MySqlMemberDAO();
memberDAO.setDataSource(ds);
//생성된 MemberDAO객체를 ServletContext 데이터 보관소를 통해 서블릿끼리 공유
/* sc.setAttribute("memberDAO", memberDAO); */
//Controller 객체에 memberDAO 의존성 주입
//Controller 객체들을 서블릿 컨텍스트 영역에 저장(키 값 : 서블릿 요청 url, value : 해당 컨트롤러(DAO 의존성 주입상태))
sc.setAttribute("/member/list.do", new MemberListController().setMemberDAO(memberDAO));
sc.setAttribute("/auth/login.do", new LoginController().setMemberDAO(memberDAO));
sc.setAttribute("/auth/logout.do", new LogoutController());
sc.setAttribute("/member/add.do", new MemberAddController().setMemberDAO(memberDAO));
sc.setAttribute("/member/update.do", new MemberUpdateController().setMemberDAO(memberDAO));
sc.setAttribute("/member/delete.do", new MemberDeleteController().setMemberDAO(memberDAO));
} catch(Exception e) {
e.printStackTrace();
}
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
try {
System.out.println("contextDestroyed");
} catch(Exception e) {
e.printStackTrace();
}
}
}
'👨💻 2. 웹개발_Back end > 2-4 JSP & Servlet' 카테고리의 다른 글
[JSP & Servlet] 6장 미니 MVC 프레임워크 만들기 (5) 프로퍼티를 이용한 객체관리 (0) | 2021.10.18 |
---|---|
[JSP & Servlet] 6장 미니 MVC 프레임워크 만들기 (4) 리플랙션 API를 이용하여 프런트 컨트롤러 개선하기 (0) | 2021.10.18 |
[JSP & Servlet] 6장 미니 MVC 프레임워크 만들기 (2) 페이지 컨트롤러의 진화 (0) | 2021.10.18 |
[JSP & Servlet] 6장 미니 MVC 프레임워크 만들기 (1) 프런트 컨트롤러 (1) | 2021.10.18 |
[JSP & Servlet] 5장 MVC 아키텍처 (7) - DB 커넥션풀 (0) | 2021.10.17 |
댓글