JDBC Class.forName()동작과 사용이유

2024. 7. 12. 11:37자바

자바 파일(Java file), 클래스 파일(Class file)

자바 파일은 확장자가 .java 인 파일로서 자바 언어로 소스 코드를 작성하는 파일입니다.

그리고 이 자바 파일을 자바 컴파일러로 컴파일한 파일이

바로 .class 확장자를 가진 클래스 파일입니다.

우리가 흔히 이클립스와 같은 IDE 혹은 커맨드 라인에서

javac 명령어를 통해 컴파일했을 때 나오는 파일이죠.

이 클래스 파일을 가지고 자바의 클래스 로더(Class Loader)가

JVM으로 클래스 파일을 로딩합니다.

Java에서 클래스의 로딩 과정은 클래스 로더(Class Loader)가 확장자가

.class 클래스 파일의 위치를 찾아

그것을 JVM위에 올려놓는 과정을 뜻합니다. 

 

 

동적 로딩

JVM이 클래스파일을 로드할 때 기본적으로 동적으로 로드합니다. 

즉 실행하면서 메모리에 로드합니다. 

이 때 메모리에 로드 하는 방법이 크게 2가지가 있는데

바로 로드타임 동적로딩,  런타임 동적로딩입니다. 

static{} 영역은 이렇게 로드될때 실행되는 코드입니다.

 

메소드 구조. 메소드 영역에 클래스 정보가 로드됩니다.

 

 

 

 

 

로드타임 동적 로딩

public class Main {

    public static void main(String[] args) {
        List<String> list= new ArrayList<>();
        
    }
}

 

위와 같은 코드를 실행하기 위해서 JVM는 코드를 한줄 씩 읽어가면서 

그 떄 마다 필요한 클래스들을 메소드 영역에 저장합니다.

이 때 한번에 모든 클래스들을 메소드 영역에 올리는게 아니라 실행하면서  한줄 씩 로드합니다.  

먼저 String, (메인메소드 파라미터),  List,  ArrayList 순으로 로드가 될 것입니다. 

이를 로드 타임 동적로딩이라고 합니다. 

즉, 코드가 실행되면서 필요한  클래스에 대해 JVM이 자동으로 메모리에 올리는 것입니다.

 

 

런타임 동적 로딩

 

Class.forName("클래스이름") 이 바로 런타임 동적 로딩입니다.

클래스 이름에 맞는 클래스를 메모리에 로드합니다. 

 

 

 

자바에서 DB연결까지 과정

일반적으로 자바에서 DB(oracle)까지 연결하는 코드는 다음과 같습니다.

	try{
		Class.forName("oracle.jdbc.driver.OracleDriver"); 
	}catch (ClassNotFoundException e){
		//에러처리하는 코드 
	}
	Connection conn=null;
	Statement stmt=null;
	ResultSet rs=null;
	
	try{
		conn=DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:xe","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){} }
	}

 

 

JAVA에서 JDBC API를 이용해 jdbc Driver(나는 오라클드라이버)를 이용하려면

해당 드라이버가 로드되어야합니다.

Class.forName("oracle.jdbc.driver.OracleDriver")은

런타임 동적로딩을 통해 OracleDriver를 메모리에 로드합니다. 

 

자 그럼 Class.forName()을 통해 oracle드라이버는 실행도중 로드가 됐습니다.

근데 로드만 했을 뿐 변수를 선언해  oracle드라이버객체를 받지도 않았는데 어떻게 바로 conn=DriverManger.getConnection()을 써서 DB에 연결을 할 수 있었을 까요?

 

정답은 바로 Driver클래스의 static{}구문에 있습니다.

다음은 Driver클래스의 구조인데요. (OracleDriver도 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!");
        }
    }
    
    그외 기타 기능
}

 

 

 

다음은 OracleDriver static구문 

static 
    {
        defaultDriver = null;
        Timestamp timestamp = Timestamp.valueOf("2000-01-01 00:00:00.0");
        try
        {
            if(defaultDriver == null)
            {
                defaultDriver = new OracleDriver();
                DriverManager.registerDriver(defaultDriver);
            }
        }
        catch(RuntimeException runtimeexception) { }
        catch(SQLException sqlexception) { }
    }

 

static{}구문은 클래스가 로드될 때 실행되는 구문입니다.

즉 로드될 때  Driver를 DriverManger라는 곳에 등록했기 때문에

바로 DB에 연결하는 Connection conn=DriverManger.getConnection("url","id","pw")구문을

사용해  DB에 연결할 수 있게 된겁니다. 

 이와 같이 등록한 JDBC Driver는 데이터베이스 Connection을 생성하는 시점에 사용되게 됩니다.

 

※getConnection메소드는 DriverManger의 메소드다.

url,id,pw를 매개변수로 받고  DriverManger가 가지고 있는 드라이버를 통해 DB에 연결한다.

 

 

지금까지과정을 정리하면 다음과 같습니다.

  • 1. Class.forName()을 통해 실행도중 오라클 드라이버로드
  • 2. 로드되면서 OracleDriver의 static{}구문 -> DriverManger에  OracleDriver 등록
  • 3. DriverManger.getConnection()을 통해 Oracle에 연결
  • 4. 이후는 쿼리 수행 

 

 

Class.forName사용 이유

static{}구문이 로딩될 때 사용되는 거라면 OracleDriver oracle=new OracleDriver() 코드를 통해

DirverManger에 등록되서 DriverManger.getConnection()하면 되지 않냐?? 

	OracleDriver oracleDriver=new OracleDriver();
	Connection conn=null;
	Statement stmt=null;
	ResultSet rs=null;
	
	try{
		conn=DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:xe","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){} }
    }

 

맞습니다. 위처럼 코딩해도 문제없이 실행됩니다. 

근데 위의 코드에서 oracleDriver객체는 사용되지않습니다.

즉 쓸데없이 객체1개를 생성해서 메모리 낭비를 하고 있습니다.

 

 

또  Class.forName()의 매개변수 문자열 값을 직접 입력하는게 아니라

조건에 따라 oracle이냐 mysql이냐 하는 경우겠죠.

지금은 메민메소드에서 Class.forName()를 사용하지만  driver를 로드할 때 다음과 같은 메소드를 만들면

매개변수값에 따라 로드 시킬 DB드라이버를 선택할 수 있습니다.

public void loadDriver(String driverName){
  Class.forName(driverName);
  //기타작업 
}

 

 

즉  Class.forName()을 쓰면 

1.Driver의 경우 로드만하면되지 객체 생성 필요없습니다.

2. 특정조건에따라 원하는 driver를 로드하기 편합니다.

이런이유로 Class.forName()을 사용합니다.

 

 

 

 

※ JDBC 4.0이후부터는 Class.forName()이든 OracleDriver든

개발자의 코드상에서 로드할 필요가 없이 
클래스패스에 오라클드라이버 라이브러리가 추가되어있다면

자동으로 DriverManger에 해당 오라클라이브러리가 추가됩니다.