JDBC - Class.forName() 동작 방식

목차

JDBC를 사용하려면 우선 내가 사용하려는 DB의 드라이버를 로드해야한다. 이때 사용하는 게 Class.forName()이다. 이 메소드는 따로 객체를 생성하지도 않았는데 어떻게 드라이버가 로드된 걸까? 이는 리플렉션과 클래스의 static 블록과 관련있다. 각각에 대해 알아보면서 Class.forName()의 동작과정을 정리해보았다.

아래는 JDBC를 통해 DB에 접근하여 쿼리를 실행하는 코드 중 일부이다.

Class.forName("com.mysql.cj.jdbc.Driver"); // 1. JDBC 드라이버 로딩

Connection connection = DriverManager.getConnection(address, userName, password); // 2. 커넥션 생성

PreparedStatement ps = con.prepareStatement(query); // 3. 쿼리문 실행
... 

여기서 Class.forName()은 리플렉션의 메소드 중 하나이다. 여기서 리플렉션이란?

리플렉션이란

🌟 컴파일 시간(Compile Time)에 특정 클래스의 정보을 알지 못하더라도, 
    실행 시간(Run Time)에 동적으로 특정 클래스의 정보를 알아낼 수 있는 프로그래밍 기법이다.

    즉, 런타임 시점에 동적으로 클래스 정보에 접근하기 위해 리플렉션을 사용한다.
  • 사용하는 이유? : Java 정적 언어의 한계

    Java는 컴파일 시점에 타입을 결정하는 정적인 언어라, 런타임 시점에 타입을 결정할 수 없다. 따라서, 컴파일 단계에서 모든 타입이 결정되어 있어야 한다. 즉, 코드에 타입에 대한 모든 내용이 작성되어 있어야 한다. (런타임때 동적으로 결정될 수 없으므로) 이는 구체화에 의존한다는 의미이고, 이로 인해 변경에 취약해진다. 이러한 정적 언어의 한계를 극복하기 위해, 런타임 시점에 동적으로 클래스 정보에 접근하기 위해 리플렉션을 사용한다.

  • 사용 예시

    메소드들을 통해서 런타임에 해당 클래스 혹은 메소드의 행위를 접근(확인/수정/실행)할 수 있다. Class.forName() 는 리플렉션 메소드 중 하나이다.

    // Class 찾기 (클래스 이름으로부터 클래스 찾기)
    Class clazz = Class.forName("test.Child");
    System.out.println(clazz.getName()); // 출력 >> test.Child

이제 Class.forName()이 리플렉션이고, 이를 통해 런타임에 JDBC 드라이버에 접근함을 알 수 있다.

근데 Class.forName("com.mysql.cj.jdbc.Driver"); 여기서는 접근만 했지, 드라이버 객체를 반환하거나 다른 처리를 하지 않았다. 어떻게 연결이 성립된걸까?

Class.forName() 동작 과정

JDBC 연결코드만 다시 보자. 첫 줄에서 리플렉션에 의해 런타임 시점에 해당 경로의 클래스(com.mysql.cj.jdbc.Driver)를 동적으로 로드한다.

Class.forName("com.mysql.cj.jdbc.Driver"); // 1. JDBC 드라이버 로딩

Connection connection = DriverManager.getConnection("..."); // 2. 커넥션 생성

여기서 로딩한 Driver 클래스 코드이다. 런타임 시점에 해당 클래스(Driver)를 JVM의 클래스로더가 로딩하게 된다.

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

이때, Driver 클래스 내부에 static 블록이 있다. static 블록은 클래스가 로딩될때, 바로 메모리의 static 영역에 올라가게 된다. **다시 말해, 인스턴스를 생성해주지 않고 클래스만 로딩해도 해당 부분은 실행되어 메모리에 올라간다.

이 때문에 Class.forName("com.mysql.cj.jdbc.Driver"); 이 코드만으로도, 즉 따로 객체를 반환하거나 다른 메소드를 호출하지 않아도 드라이버가 등록된다.

따라서 알아서 자동 등록된 드라이버로 이후 작업을 실행할 수 있다. (커넥션 생성, 쿼리 실행 등)

Class.forName("com.mysql.cj.jdbc.Driver"); // 1. JDBC 드라이버 로딩

Connection connection = DriverManager.getConnection(address, userName, password); // 2. 커넥션 생성

PreparedStatement ps = con.prepareStatement(query); // 3. 쿼리문 실행
... 

정리하면

  • Class.forName() 은 런타임 시점에 동적으로 클래스 정보에 접근하는 리플렉션 메소드이다.

  • 이때, Driver클래스의 static 블록에 드라이버를 등록하는 코드가 있다. static 블록은 클래스 로드할 때 메모리에 올라가서 실행된다(인스턴스를 만들지 않더라도).

  • 따라서, Class.forName("com.mysql.cj.jdbc.Driver"); 이 코드는 해당 드라이버 접근하면 자동으로 드라이버가 등록된다.

  • 그래서 개발자가 따로 객체 생성 등을 하지 않더라도 드라이버가 등록되고, 이후 작업을 진행할 수 있다.

Reference

Last updated