반응형
1. 사용법
reflection의 사전적의미는 "반사", "영상"이라는 뜻이다.
즉, java에서 정의된 class객체에 대한 하나의 상을 담을수 있는 그릇을 만든다고 생각하면 이해가 쉽다.
여기서 한가지 더 알아야 할것이 java의 Class객체이다. jdk에 기술된 내용을 보면

------------------------------

Instances of the class Class represent classes and interfaces in a running Java application. Every array also belongs to a class that is reflected as a Class object that is shared by all arrays with the same element type and number of dimensions. The primitive Java types (boolean, byte, char, short, int, long,float, and double), and the keyword void are also represented as Class objects.

Class has no public constructor. Instead Class objects are constructed automatically by the Java Virtual Machine as classes are loaded and by calls to the defineClass method in the class loader.

-------------------------------
간단하게 살펴보면 Class 객체는 java에서 사용되는 모든 class와 interface, array등을 표현할 수 있으며 Class객체는 생성자를 포함하고 있지 않으므로 다음과 같은 방법으로 사용된다.
void printClassName(Object obj) { System.out.println("The class of " + obj + " is " + obj.getClass().getName()); } 
(여기서 getClass는 object객체에서 제공된는 메소드이며 Class형태로 return 된 값을 getName()이란 Class객체에서 제공하는 메소드로 반환하고 있다.)
이처럼 Class객체에서는 class의 내부정보(class명, 생성자, 메소드, 멤버변수)등을 표시하는 메소드들을 제공하는데 이를 담기위한 그릇의 형태로 Reflection API가 사용된다.
여기서 getClass() 메소드 대신 .class라는 class literal이 사용 가능하다. .class에 대한 정의는 다음과 같다.
---------------------
A class literal is an expression consisting of the name of a class, interface, array, or primitive type followed by a `.' and the token class. The type of a class literal is Class. It evaluates to the Class object for the named type (or for void) as defined by the defining class loader of the class of the current instance.
---------------------
즉, 객체.class를 사용하면 .getClass()와 같은 효과를 얻을 수 있다. 

Class에서 제공되는 메소드를 이용하여 Reflection API에 값을 담아 표시하는데 주요 사용되는 Class메소드 - Reflection API는 다음과 같다. 

------------------------
Class cls = 객체.getClass();

* Reflection API  = Class 메소드 
                       cls.getName(); //클래스 이름
                       cls.getSuperclass();//상위클래스
//modifier : class의 구분자여부를 확인
int mods = cls.getModifiers();  
java.lang.reflect.isPublic(mods);//class가 public 인지
java.lang.reflect.Modifier.isProtected(mods)
java.lang.reflect.Modifier.isPrivate(mods));
java.lang.reflect.Modifier.isFinal(mods)); 
java.lang.reflect.Modifier.isStatic(mods)); 
java.lang.reflect.Modifier.isAbstract(mods)); 
java.lang.reflect.Modifier.isInterface(mods)); 

//Fields : class의 멤버변수
java.lang.reflect.Field[] fields = cls.getDeclaredFields(); //멤버변수들을 가져옴
java.lang.reflect.Modifier.toString(java.lang.reflect.field.getModifiers());//필드의 구분자를 가져옴.
java.lang.reflect.field.getType(); //멤버변수의 타입을 Class형태로 return 되므로 .getName()으로 값을 가져옴
java.lang.reflect.field.getName(); // 멤버변수의 이름을 return

// Constructors  : 생성자
java.lang.reflect.Constructor[] constructors = cls.getDeclaredConstructors(); //생성자들을 배열로 가져옴
java.lang.reflect.Constructor.getName();//생성자 이름
Class[] paramTypes = java.lang.reflect.Constructor.getParameterTypes();//생성자의 파라메터를 배열값으로 가져옴

//Methods : 메소드
java.lang.reflect.Method[] methods = cls.getDeclaredMethods();  
java.lang.reflect.Method.getReturnType(); //return type을 Class 형태로 반환
java.lang.reflect.Method.getName();
Class[] paramTypes = java.lang.reflect.Method.getParameterTypes();  //메소드의 파라메터를 배열값으로 가져옴


------------------------------------

getFields vs getDeclaredFields : Declared가 붙는 경우는 private, public 값을 다 가져 오지만 없는 경우는 public 값만 가져온다.

-----------------------------------

2. 활용법
자 이제 reflection의 개념을 알았으니 실제 활용법을 알아보자.
동적으로 메소드를 호출하는
 법에 대해 알아보겠다. 
동적 메소드 호출이라고 하면 메소드를 파라메터로 받아 호출하는 것으로 ejb대신 servlet+동적메소드 호출 방식으로 사용이 가능하다.

소스를 살펴보자

------------------------
//dynamic method invocation
파라메터 : String ctrName, Connection conn, Data data

1: Class ctrClass = Class.forName(ctrName);
2: Constructor constructor = ctrClass.getConstructor(new Class[]{Connection.class});
3: Object object = constructor.newInstance(new Object[]{conn});
4: Method method = ctrClass.getMethod(methodName, new Class[] { Data.class });
5: Object resultObj = method.invoke(object, new Object[] { data });

--------------------------------
class의 이름을 동적으로 받아 내부 메소드를 동적으로 호출하는 예제이다.

1번째 줄에서는 class의 이름을 받아(
ctrName) Class 객체를 찾는다.
2번째 줄에서는 찾은 Class 객체에서 생성자를 호출하는 부분으로, new Class[]{Connection.class} 를 파라메터값으로 Class객체의 생성자를 찾아와 Reflection API인 Constructor 객체에 담는다. - 이 소스에서는 Connection 정보가 생성자로 들어간다.
3번째 줄에서는 생성자를 이용하여 새로운 객체를 생성한다. 이때  new Object[]{conn}값이 생성자의 파라메터로 들어가며 conn 값은 소스의 윗부분에서 connection 정보를 객체로 생성해 놓은 값이다.

즉, 1~3번째 소스를 통해 class이름을 받아 class의 생성자를 값으로 얻어 이 class의 instance를 생성했다.
AAA aa = new AAA("초기값"); 이란 부분을 동적으로 생성할 수 있게 된 것이다.
이제 aa.process();를 동적으로 호출해 보자

4번째 줄에서는 reflection API인 Method 객체에 Class객체에서 메소드 값을 찾아 넣어준다. 이때 메소드의 파라메터는 Data객체의 형태로 들어오는것을 찾는다.
5번째 줄에서는 찾은 메소드에 대해 invoke를 통해 해당 메소드를 호출하는데 파라메터로는 3번째줄에서 찾은 Class의 객체와 실제 호출될때 파라메터로 들어가는 값이 된다.
실행된 메소드의 return 값은 resultObj에 들어가게 된다.

 

반응형

+ Recent posts