자바에서 사용하는 리플렉션은 Class와 같은 객체를 통해 정보를 분석해 내는 방법으로, 구체적인 클래스 타입을 알지 못해도 그 클래스의 메소드, 타입, 변수들을 접근할 수 있도록 해주는 자바 API다.
Reflection은, 스프링 프레임워크, 대표적 ORM 기술인 하이버네이트, jackson 라이브러리 등에 사용된다.
일반적으로 간단한 변수들의 클래스의 호출 과정은 아래와 같다.
public void Test1() {
//임의의 클래스를 가져오는 방법
Class c = "foo".getClass();
System.out.println(c); //class java.lang.String
Class str = new String("").getClass();
System.out.println(str); //class java.lang.String
Class i = new Integer(0).getClass();
System.out.println(i); //class java.lang.Integer
byte[] b = new byte[1024];
Class c1 = b.getClass();
System.out.println(c1); //class [B
Set<String> s = new HashSet<>();
Class c2 = s.getClass();
System.out.println(c2); //class java.util.HashSet
}
Class 객체가 해당 클래스의 타입을 반환한다는 것은 위의 코드를 실행하면 알 수 있다.
조금 심화해보면
public class Employee implements java.io.Serializable {
private Integer id;
private Integer age;
private Date birthday;
private String name;
public Employee() {
}
public Employee(Integer age, Date birthday, String name) {
this.age = age;
this.birthday = birthday;
this.name = name;
}
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getAge() {
return this.age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getBirthday() {
return this.birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
이와 같은 Employee 클래스가 있고, id, age, name, birthday의 변수에 관련된 Getter, Setter 메서드가 있다고 가정하고 Reflection을 사용해본다면 어떻게 될까?
public void Test3() {
try {
Class c = Employee.class;
Method m[] = c.getDeclaredMethods(); // Class에서 정의된 메소드 리스트를 반환
for (int i = 0; i < m.length; i++)
System.out.println(m[i].toString());
} catch (Throwable e) {
System.err.println(e);
}
}
Employee 클래스를 가지고 와서, getDeclaredMethods()로 클래스에서 정의한 메소드 리스트를 Method 배열에 담아서 출력해보면 아래와 같은 결과가 나온다.
public java.lang.String org.power21.hibernate.data.Employee.getName()
public java.lang.Integer org.power21.hibernate.data.Employee.getId()
public void org.power21.hibernate.data.Employee.setName(java.lang.String)
public void org.power21.hibernate.data.Employee.setAge(java.lang.Integer)
public void org.power21.hibernate.data.Employee.setBirthday(java.util.Date)
public java.lang.Integer org.power21.hibernate.data.Employee.getAge()
public java.util.Date org.power21.hibernate.data.Employee.getBirthday()
public void org.power21.hibernate.data.Employee.setId(java.lang.Integer)
Getter, Setter로 정의한 메소드들이 출력된 것을 확인할 수 있다.
더 자세하게 Class와 관련된 메소드들을 살펴보면
public void Test2() {
Class c = Employee.class;
Method[] m = c.getMethods();
Field[] f = c.getFields();
Constructor[] cs = c.getConstructors();
Class[] inter = c.getInterfaces();
Class superClass = c.getSuperclass();
System.out.println(c); // class org.power21.hibernate.data.Employee
System.out.println(m[0]); // public java.lang.String org.power21.hibernate.data.Employee.getName()
System.out.println(f); // [Ljava.lang.reflect.Field;@3fee733d
System.out.println(cs[0]); // public org.power21.hibernate.data.Employee()
System.out.println(inter[0]); // interface java.io.Serializable
System.out.println(superClass); // class java.lang.Object
}
Constructor나, Interface나, Field나, Method나, SuperClass나.
Reflection은 클래스를 구성하는 요소들을 가져와서 사용할 수 있다.
아, Boolean 변수는 원시 코드라서 Reflection이 안된다는 점 참고하길 바란다.
public void Test4() {
Class c = Employee.class;
boolean b1 = c.isInstance(new Integer(10)); // c가 Integer가 맞는지?
System.out.println("결과 :: " + b1);
boolean b2 = c.isInstance(new Employee()); // c가 Employee가 맞는지?
System.out.println("결과 :: " + b2);
}
InstansceOf 기능도 사용할 수 있다.
isInstance(Object object)에 비교할 대상을 넣으면 boolean이 반환된다.
public void Test5() {
try {
Class cls = Employee.class;
Method mList[] = cls.getDeclaredMethods(); // Employee 클래스의 메소드 리스트를 가져온다.
System.out.println("interface :: " + cls.getInterfaces()[0]); // 인터페이스
for (int i = 0; i < mList.length; i++) {
Method m = mList[i];
System.out.println("name :: " + m.getName()); // 메소드의 이름
Class pvec[] = m.getParameterTypes(); // 메소드의 파라미터 타입
for (int j = 0; j < pvec.length; j++)
System.out.println(" param :: " + j + " " + pvec[j]);
Class evec[] = m.getExceptionTypes(); // 메소드의 예외처리 타입
for (int j = 0; j < evec.length; j++)
System.out.println("exception :: " + j + " " + evec[j]);
System.out.println("return type :: " + m.getReturnType()); // 메소드의 리턴 타입
System.out.println(" ");
}
} catch (Throwable e) {
System.err.println(e);
}
}
실질적으로 사용되는 부분은 위와 같다.
Method 리스트를 받아와서 해당 메소드 이름, 파라미터 타입, 예외처리 타입, 리턴 타입등을 알아내서 용도에 맞게 사용되도록 한다.
이렇게 되면 해당 클래스의 이름이나 메소드나, 파라미터를 알지 못해도 이 흐름에 이 클래스를 사용해야만 할 때 Reflecion을 이용해서 실행할 수 있다는 장점이 있다.
'Java > 순수 Java' 카테고리의 다른 글
[Java] Eclipse 이클립스 크롬 연동하기(Web Browser 연동) (0) | 2020.03.13 |
---|---|
[Java] SWT Tree와 Map을 활용한 Directory 출력 예제 소스 (0) | 2020.03.12 |
[Java] SWT Tree 사용 예제 (0) | 2020.03.12 |
[Java] Apache log4j .properties 생성 및 오류 해결 방법 (0) | 2020.03.06 |
[Java] Property 파일(.properties) 생성 및 사용 (0) | 2020.03.05 |