[JAVA] JDBC Class.forName() 동적로딩 동작과 사용이유

유알·2022년 12월 26일
0

[JAVA]

목록 보기
4/13

.java .class

  • .java 확장자인 파일은 자바언어로 소스 코드를 작성할 때 사용
  • .class 확장자인 파일은 자바 컴파일러로 컴파일 한 파일
    클래스 로더(Class Loader)가 확장자가 .class인 클래스 파일의 위치를 찾아 JVM에 로딩한다.

동적 로딩

Java는 동적 로딩을 지원하기 때문에 모든 클래스를 로딩하지 않고 필요한 시점에 클래스를 로딩하여 사용할 수 있다.

  • 로드 타임 동적 로딩 : 하나의 클래스를 로딩할 때 필요한 클래스 로딩
  • 런타임 동적 로딩 : 코드를 실행하는 순간 필요한 클래스를 로딩

로드타임 동적로딩

로드타임 동적 로딩은 클래스를 로딩할 때 필요한 다른 클래스를 동적으로 로딩하는 것이다.
예를 들어, System.out.println("Hello World");와 같은 코드가 작성되어 있는 Hello.javaHello.class로 컴파일 하고 JVM에 로딩한다고 생각해보자.
이때 필요한 System, String 관련 .class 파일도 같이 로딩한다.
말 그대로 "로드 타임 동적로딩" 이다.

런타임 동적로딩

런타임 동적 로딩은 코드를 실행하는 순간 클래스를 로딩하는 것이다.
즉 JVM이 코드를 실행하다가 .class 파일을 로딩하는 것을 의미한다.
여기서 이번 주제인 Class.forName("로드할 클래스 이름") 이 사용된다.

JDBC API 사용시 Class.forName

아래의 예는 토비의 스프링에서 나온 JDBC API 사용하여 DB연결을 하는 예 중 일부를 가져온 것이다.

public class UserDao{
	public void add(User user) throws ClassNotFindException, SQLExcption{
    	Class.forName("com.mysql.jdbc.Driver");
        Connection c = DriverManager.getConnection(
        	"jdbc:mysql://localhost/springbook","spring","book"
        );
        //...
    }
}

add 메서드가 실행될때 동적으로 Driver를 로딩한다.

JDBC 에서 Class.forName 사용 이유

그런데 이상하다. Class.forName()의 리턴값도 받지 않는데 어떻게 커넥션을 사용할 수 있을까?
Driver 클래스의 구조이다. (자손으로 oracle.jdbc.driver.OracleDriver, com.mysql.jdbc.Driver 등을 가지고 있음)

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    static {
        try {
           java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }
    
    그외 기타 기능
}

보면 static{} 구문이 들어있다.
이는 클래스가 로드 될때 실행되는 구문으로, 여기서는 DriverManager에 Driver을 등록하는 구문이 쓰여있다.
즉 런타임 동적 로딩을 통해 클래스가 로딩되면 DriverManager에 Driver가 등록되는 구조이다.

  1. Class.forName()을 통해 실행도중 드라이버 로드
  2. 로드 되면서 static{}이 실행되면서 DriverManager에 Driver 등록
  3. DiriverManager.getConnection()을 통해 Driver에 연결
  4. 이후 쿼리 수행

그렇다면 왜 처음 클래스가 로드될때 로드하지 않고 메서드 단에서 로드를 하는 것일까?
(물론 OracleDriver oracleDriver=new OracleDriver();이런식으로 로드타임 동적 로딩을 해도 정상 작동한다.)
하지만 저렇게 new를 사용해서 로드하게 되면, 의미없는 객체 1개를 생성해서 메모리 낭비를 하고 있다. 결국 DriverManager을 통해 사용하기 때문이다.

또 다른 이유는

String dirverName=request.getParameter("driverName");
	String connectionInfo="";
	if(dirverName.equals("oracle.jdbc.driver.OracleDriver")){
		connectionInfo="jdbc:oracle:thin:@127.0.0.1:1521:xe";
	}else{
		//오라클 말고 기타 DB Driver
	}
	
	
	try{
		Class.forName(dirverName);
	}catch (ClassNotFoundException e){
		//에러처리하는 코드 
	} 
	Connection conn=null;
	Statement stmt=null;
	ResultSet rs=null;
	
	try{
		conn=DriverManager.getConnection(connectionInfo,"jsp","oracle");
		stmt=conn.createStatement(); //쿼리 수행
		rs=stmt.executeQuery("SELECT 1 FROM DUAL");
		if(rs.next())  System.out.print(rs.getInt(1));
		
	}catch (SQLException e){
		//에러처리하는 코드 
	}finally {
		if(rs!=null){try{rs.close();} catch(Exception e){} }
		if(stmt!=null){try{stmt.close();} catch(Exception e){} }
		if(conn!=null){try{conn.close();} catch(Exception e){} }
	}

이 코드를 보면, 파라미터 값에 따라 oracle, mysql같은 다양한 드라이버에 연결하게 됩니다.
만약 여기서 로드타임 동적로딩을 사용하게 되면, 로드되는 순간 그 수많은 드라이버를 한번에 로드하고 들고 있게 됩니다.
하지만 Class.getName()을 사용하면 딱 사용하는 순간에 로드할 수 있게된다.

정리하면 Class.forName()을 쓰는 이유는
1. Driver의 경우 로드만 하면 되지 객체 생성은 필요 없다.
2. 특정 조건에 따라 Driver로드 다르게 할 때는 모든 Driver을 미리 로드할 필요없다.

profile
더 좋은 구조를 고민하는 개발자 입니다

0개의 댓글