Java 프레임워크 만들기 - JSP

자바 클래스 동적 로딩 - Java Class.forName newInstance, 이클립스(Eclipse)

carrotweb 2021. 4. 16. 17:04
728x90
반응형

"Class.forName()"메서드는 로딩하고자 하는 클래스 명으로 클래스를 찾아 로드합니다.

Class.forName(String name, boolean initialize, ClassLoader loader)

"name"은 완전 정규화된 명확한 이름(FQN - fully qualified name)으로 된 클래스 명입니다.

"initialize"는 클래스 안에 "static"들을 초기할지 여부를 설정합니다.

"loader"는 클래스 로더를 지정합니다.

Class.forName(String name)

"name"은 완전 정규화된 명확한 이름(FQN - fully qualified name)으로 된 클래스 명입니다.

클래스 안에 "static"들을 초기화("initialize = true") 하고 "Class.forName"메서드를 실행하는 클래스의 클래스 로더("loader = this.getClass().getClassLoader()") 를 사용합니다.

테스트를 위해 "TestClass.java"를 생성합니다.

package com.home.project.test2.service;

public class TestClass {

	/**
	 * 생성자
	 */
	public TestClass() {
		System.out.println("TestClass 생성자 실행");
	}

	/**
	 * Static Block
	 */
	static {
        System.out.println("TestClass안에 있는 Static Block를 실행");
    }

	/**
	 * 프로세스를 실행입니다.
	 */
	public void process() {
		System.out.println("TestClass의 process 실행");
	}  
}

 

"Class.forName(String name)"메서드로 "TestClass"를 찾아 로드하겠습니다.

"TestDispatcherServlet.java"에서 "Class.forName(String name)"메서드를 추가합니다.

try {
	Class<?> finedClass = Class.forName("com.home.project.test2.service.TestClass");
	System.out.println("Class : " + finedClass.getName());
} catch (ClassNotFoundException e) {
	e.printStackTrace();
}

 

실행 결과, "TestClass"안에 있는 "static"블록이 실행되었습니다.

그 이유는 "initialize"가 "true"로 자동으로 설정되기 때문입니다.

TestClass안에 있는 Static Block를 실행
Class.forName : com.home.project.test2.service.TestClass

 

만약, 클래스 명으로 찾지 못할 경우 "ClassNotFoundException"이 발생합니다.

클래스명을 "TestClass"에서 "TestClass1"로 변경하고 실행하였습니다.

Class<?> finedClass = Class.forName("com.home.project.test2.service.TestClass1");

 

또한, "Class<?>"에서 "<?>"(타입을 명시하지 않음)가 없을 경우에는 "Class is a raw type. References to generic type Class<T> should be parameterized"("클래스가 원시 유형입니다. 제네릭 타입 Class<T>에 대한 참조는 변수화해야합니다.")이라는 문제(Java Probleam)가 발생합니다.

Class finedClass = Class.forName("com.home.project.test2.service.TestClass1");

반드시 "Class<?>"으로 입력해야 합니다.

 

"Class.forName(String name, boolean initialize, ClassLoader loader)"메서드로 "TestClass"를 찾아보겠습니다. "initialize"는 "false"으로 초기화하지 않도록 하겠습니다.

try {
	ClassLoader classLoader = this.getClass().getClassLoader();
	Class<?> finedClass = Class.forName("com.home.project.test2.service.TestClass", false, classLoader);
	System.out.println("Class.forName : " + finedClass.getName());
} catch (ClassNotFoundException e) {
	e.printStackTrace();
}

실행 결과, "TestClass"안에 있는 "static"블록이 실행되지 않았습니다.

Class.forName : com.home.project.test2.service.TestClass

 

로드된 클래스에 "newInstance()"메서드를 이용하여 새로운 인스턴스로 생성합니다.

"newInstance()"메서드는 클래스의 새로운 인스턴스를 생성하고 클래스의 생성자를 호출한 후 생성된 새로운 인스턴스를 리턴합니다.

"TestDispatcherServlet.java"에서 "newInstance()"메서드를 추가합니다.

Object classInstance = finedClass.newInstance();
System.out.println("Class Instance : " + classInstance);

"Exception"처리를 위해 "Add catch clasue to surrounding try"를 선택합니다. 아래에서 설명드리겠습니다.

실행 결과, 클래스 생성자가 실행되면서 인스턴스가 생성되었습니다.

TestClass안에 있는 Static Block를 실행
Class.forName : com.home.project.test2.service.TestClass
TestClass 생성자 실행
Class Instance : com.home.project.test2.service.TestClass@547ad232

 

새로운 인스턴스를 "TestClass"으로 형 변환하여 내부 메서드("process")를 실행합니다.

TestClass testClass = (TestClass)classInstance;
testClass.process();

"TestClass"를 import합니다.

실행 결과, "TestClass"의 "process"메서드가 호출되었습니다.

TestClass안에 있는 Static Block를 실행
Class.forName : com.home.project.test2.service.TestClass
TestClass 생성자 실행
Class Instance : com.home.project.test2.service.TestClass@547ad232
TestClass의 process 실행

 

동적으로 조건에 따라 여러 개의 클래스가 생성되고 동일한 프로세스로 동작하게 만들기 위해서는 interface(인터페이스)를 만들고 구현하면 됩니다.

인터페이스는 클래스를 만들 때 기본이 되는 설계도와 같습니다. 인터페이스로 구현되는 클래스는 반드시 엔터페이스에 있는 메서드(클래스 함수)를 구현해야 합니다.

이전에 "TestClass.java"에 "process"메서드를 사용했으니 interface(인터페이스)에는 "doProcess"메서드를 추가하여 "ITestInerface.java"를 생성합니다. 테스트를 위해 "process"메서드를 사용하지 않고"doProcess"메서드를 만들었습니다.

package com.home.project.test2.service;

public interface ITestInerface {

	/**
	 * 프로세스를 실행합니다.
	 */
	public abstract void doProcess();
}

인터페이스의 변수는 "public static final"으로 선언되어야 합니다. (생략할 수 있습니다.)

인터페이스의 메서드는 "public abstract"으로 선언되어야 합니다. (생략할 수 있습니다.)

 

 

"TestClass.java"에 "ITestInerface.java" 인터페이스가 구현(implements)되도록 코드를 수정합니다.

public class TestClass implements ITestInerface {

"TestClass.java"에 "ITestInerface.java"의 "doProcess"메소드를 구현합니다.

/**
 * 프로세스를 실행입니다.
 */
@Override
public void doProcess() {
	System.out.println("TestClass의 doProcess 실행");
}

오버라이드(Override) 처리하는 방법은 아래에서 설명드리겠습니다.

"TestDispatcherServlet.java"에서 import한 "TestClass"를 삭제하고 "TestClass"로 형 변환한 부분을 인터페이스("ITestInerface")로 형 변환으로 수정하고 "process"메서드를 인터페이스의 "doProcess"메서드로 변경합니다.

ITestInerface testClass = (ITestInerface)classInstance;
testClass.doProcess();

실행 결과, "TestClass"의 "doProcess"메서드가 호출되었습니다.

TestClass안에 있는 Static Block를 실행
Class.forName : com.home.project.test2.service.TestClass
TestClass 생성자 실행
Class Instance : com.home.project.test2.service.TestClass@6778028a
TestClass의 doProcess 실행

 

이처럼, 동적으로 클래스를 생성하여 조건에 따라 실행할 수 있습니다.

이클립스(Eclipse)에서 구현(implements) 또는 계승(extends) 받은 클래스에서 "오버라이드(Override)"를 처리 방법을 설명하겠습니다.

마우스를 문제(Java Probleam)가 발생한 "TestClass" 위로 이동합니다. 그러면 컨텍스트 메뉴가 나타납니다.

"The type TestClass must implement the inherited abstract method ITestInerface.doprocess()"

"상속되는 추상 메서드인 ITestInerface.doprocess()를 구현해야 합니다."라고 에러 메시지가 나타납니다.

"Add unimplemented methods"(구현되지 않은 메서드(클래스 함수) 추가)를 클릭합니다.

@Override
public void doprocess() {
	// TODO Auto-generated method stub
}

"doprocess()"메서드가 추가되고 "@Override"어노테이션이 추가되어 "오버라이드(Override)"된 메서드임을 표시합니다.

만약, "Make type 'TestClass' abstract"를 클릭하면 "TestClass"를 추상 클래스가 되도록 "abstract" 키워드가 추가됩니다. 그러면 "TestClass"는 직접 실행할 수 없는 클래스가 됩니다.

public abstract class TestClass implements ITestInerface {

 

 

이클립스(Eclipse)에서 "try/catch"구문 처리에 대한 UI의 사용 방법을 설명하겠습니다.

코딩 중에서 "try/catch"구문을 처리하는 방법입니다. 마우스를 "Unhandled exception"이 된 "finedClass.newInstance()" 위로 이동합니다. 그러면 컨텍스트 메뉴가 나타납니다.

여기서 다양한 처리 방식이 있습니다.

 

"Add catch clasue to surrounding try"를 선택하시면 "catch"구문이 여러 개 등록됩니다.

try {
	Class finedClass = Class.forName("com.home.project.test2.service.TestClass");
	System.out.println("Class.forName : " + finedClass.getName());
	Object classInstance = finedClass.newInstance();
	System.out.println("Class Instance : " + classInstance);
} catch (ClassNotFoundException e) {
	e.printStackTrace();
} catch (InstantiationException e) {
	e.printStackTrace();
} catch (IllegalAccessException e) {
	e.printStackTrace();
}

 

"Add exceptions to existing catch clasue"를 선택하시면 "catch"구문에 여러 개의 "exception"을 "|"으로 구분하여 등록됩니다.

try {
	Class finedClass = Class.forName("com.home.project.test2.service.TestClass");
	System.out.println("Class.forName : " + finedClass.getName());
	Object classInstance = finedClass.newInstance();
	System.out.println("Class Instance : " + classInstance);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
	e.printStackTrace();
}

 

"Surround width try/multi-catch"를 선택하시면 "try/catch"구문이 내부에 생성되고 "catch"구문에 여러 개의 "exception"을 "|"으로 구분하여 등록됩니다.

try {
	Class finedClass = Class.forName("com.home.project.test2.service.TestClass");
	System.out.println("Class.forName : " + finedClass.getName());
	Object classInstance;
	try {
			classInstance = finedClass.newInstance();
	} catch (InstantiationException | IllegalAccessException e) {
		e.printStackTrace();
	}
	System.out.println("Class Instance : " + classInstance);
} catch (ClassNotFoundException e) {
	e.printStackTrace();
}

 

"Surround width try/catch"를 선택하시면 "try/catch"구문이 내부에 생성되고 "catch"구문이 여러개 등록됩니다.

try {
	Class finedClass = Class.forName("com.home.project.test2.service.TestClass");
	System.out.println("Class.forName : " + finedClass.getName());
	Object classInstance;
	try {
		classInstance = finedClass.newInstance();
	} catch (InstantiationException e) {
		e.printStackTrace();
	} catch (IllegalAccessException e) {
		e.printStackTrace();
	}
	System.out.println("Class Instance : " + classInstance);
} catch (ClassNotFoundException e) {
	e.printStackTrace();
}

 

 

728x90
반응형