본문 바로가기

Dev.BackEnd/Servlet&JSP

5. Model 1 에서 Model 2 까지 변화과정 3편




5. Model 1 에서 Model 2 까지 변화과정 3편

controller역할 뿐만 아니라 view 역할과 model 역할까지 모두 하는 서블릿부터 만든 다음에,

그 서블릿으로부터 기능들이 하나씩 분리되는 과정을 보이려고 한다.

5. ServletContextListener
리스너란 사건이 발생했을 때, 알림을 받는 객체를 말한다.
서블릿을 보면, 매 요청마다
MemberDao memberDao = new MemberDao( );  를 통해 DAO 인스턴스를 생성한다.
요청할 때마다 객체를 만들게 되면 많은 가비지가 생성되고, 실행 시간이 길어진다.
그래서 DAO를 ServletContext에 저장한다.
AppInitServlet에 DAO 객체를 준비해도 되지만, 웹 애플리케이션 이벤트를 이용하는 것이 더 좋다.

ContextLoaderListener.java 를 생성한다.
이 클래스에 Appinit.java 클래스에 있는 역할도 옮겨 온다.
이 클래스는 ServletContextListener 인터페이스를 구현한다.
( contextInitialized( )메서드와 contextDestroyed( ) 메서드를 구현하면 된다. )

이렇게 하면 서블릿에서는 MemberDao 객체를 생성하는 대신,
ServletContext에 저장된 DAO 객체를 꺼내쓰면 된다.

그리고 Connection 객체를 생성하고 MemberDao에 주입하는 것도
contextInitialized( ) 메소드에 포함되었으므로,
서블릿에서는 더이상 ServletContext에서 Connection 객체를 꺼내쓰지 않아도 된다.
1
2
3
4
5
6
7
8
9
10
11
12
ServletContext sc = event.getServletContext();
 
Class.forName(sc.getInitParameter("driver"));
    conn = DriverManager.getConnection(
          sc.getInitParameter("url"),
          sc.getInitParameter("username"),
          sc.getInitParameter("password"));
 
MemberDao memberDao = new MemberDao();
memberDao.setConnection(conn);
 
sc.setAttribute("memberDao", memberDao);
cs
여태까지 구현했던 코드 중 중복되는 코드가 ContextLoaderListener.java 파일에 모아졌다.
ServletContext에서 driver 클래스를 꺼내오고 Connection 객체를 얻는다.
그리고 Dao 객체를 생성하고 Dao 객체에 connection을 주입한다.
그런 다음 ServletContext에 memberDao를 주입한다.



6. DB 커넥션 풀
DB 커넥션 객체를 여러 개 생성하여 Pool에 담아놓고 필요할 때 꺼내 쓰는 방식을 말한다.
(객체를 미리 만들어두고, 필요할 때마다 빌리고, 사용한 다음 반납하는 방식을 풀링이라고 한다.)

DBConnectionPool.java 클래스를 새로 생성하고 이 클래스에는
url, username, password 그리고 ArrayList(생성한 connectionpool을 관리할 배열)를 Heap영역에 선언한다음
생성자를 정의한다.
1
2
3
4
5
6
7
8
  public DBConnectionPool(String driver, String url, 
      String username, String password) throws Exception {
    this.url = url;
    this.username = username;
    this.password = password;
    
    Class.forName(driver);
  }
cs

그리고 getConnection( ) 메소드를 통해 Connection 객체에 접근할 수 있도록 한다.

1
2
3
4
5
6
7
8
9
  public Connection getConnection() throws Exception {
    if (connList.size() > 0) {
      Connection conn = connList.remove(0); 
      if (conn.isValid(10)) {
        return conn;
      }
    }
    return DriverManager.getConnection(url, username, password);
  }
cs

returnConnection( ) 메소드를 통해 Connection을 반환받아 ArrayList에 추가한다.
1
2
3
  public void returnConnection(Connection conn) throws Exception {
    connList.add(conn);
  }
cs

closeAll( ) 메서드를 통해 ArrayList에 있는 connectionpool들을 모두 close 한다.
1
2
3
4
5
  public void closeAll() {
    for(Connection conn : connList) {
      try{conn.close();} catch (Exception e) {}
    }
  }
cs

이렇게 하면 ContextLoadListener에서 Connection객체와 DAO를 생성하지 않는다.
대신 DB ConnectionPool을 생성한다.
생성자를 정의한대로 ServletContext에서 driver, url, username, password를 꺼내 생성자 매개변수로 넘겨준다.
그리고 Dao에 connectiontpool을 주입한다.
1
connection = connPool.getConnection();
cs
그리고 마지막에 connPool.closeAll( )을 실행시킬 destoryed 메소드를 정의한다.
이렇게 하면 DAO에서는 connection 객체를 connPool.getConnection( ) 메소드를 통해 받는다.
그리고 해당 task를 마무리 하면 finally를 통해서 returnConnection해줘야 한다.



7. DataSource
첫째, DataSource는 서버에서 관리하기 때문에, DB or JDBC 드라이버가 변경되더라도
애플리케이션을 변경할 필요가 없다.
둘째, Connection과 Statement 객체를 풀링할 수 있으며 분산 트랜잭션을 다룰 수 있다
장점이 지금 당장 무슨뜻인지는 와닿지 않지만,
DriverManager를 통해 했던 작업들을 DataSource라는 것을 통해 한다는 말이다.

BasicDataSource 라는 것을 사용한다.
DBCP 라이브러리에서 DataSource 인터페이스를 구현한 클래스가 BasicDataSource 이다.
ds라고 인스턴스를 생성한다.
그리고 memberDao에 DataSource ds를 주입한다.
1
connection = ds.getConnection();
cs
finally 에서는 connection을 반납하는 코드 대신 connection.close( )를 호출한다.
contextDestroyed 메소드에서 Exception을 SQLException으로 처리해야 한다.

MemberDao.java에도 DataSource 클래스를 정의하고
주입받는 setDataSource 메소드를 작성한다, 



이 포스팅은 '자바 웹 개발 워크북'이라는 교재를 바탕으로 작성되었습니다. 문제가 될 시 삭제하겠습니다.

-..-