Java 리플렉션이 쉽습니다.
자바의리플렉션API를 쉽게 사용할 수 있는 유틸리티 클래스를 소개합니다.
리플렉션이란?
리플렉션 API는,java.lang.reflect'패키지에 포함되어 있으며,
Java 클래스로부터 필드나 메소드 등의 정보를 취득하는 API입니다.
주로 다음 클래스를 사용합니다.
- java.lang.Class
- java.lang.reflect.Constructor
- java.lang.reflect.Method
- java.lang.reflect.Field
리플렉션를 사용하면 클래스 생성이나 메소드 호출을 직접 코딩하지 않고,
문자열에서 클래스의 인스턴스를 생성하거나 메서드를 실행할 수 있습니다.
클래스명이나 메소드명의 정의는 XML등의 외부 파일에 기술하는 것으로, 어플리케이션을 런타임의 환경의 변화에 동적으로 대응시킬 수가 있습니다.
또한 유연한 애플리케이션을 구축하는 데 매우 효과적인 API가 되었습니다.
리플렉션의 목적
리플렉션는 사용자가 직접 사용하는 것은별로 많지 않지만,
Struts등의 Web 어플리케이션이나 O/R 매핑등의 프레임워크내에서는 많이 사용되고 있습니다.
예를 들어 웹 화면의 입력 데이터를 JavaBeans 속성으로 자동 설정하거나
JavaBeans 프로퍼티을 바탕으로 자동적으로 갱신하는 SQL을 발행하는 경우 등에 이용되고 있습니다.
또, 어플리케이션의 기동시에 기능을 확장하는 플러그인을 동적으로 읽어 등록하는 등의 기능은리플렉션을 사용하면 쉽게 실현할 수 있습니다.
샘플 실행
그럼, 샘플을 실행해 주시고, 꼭 프레임워크적인 프로그래밍을 체감해 주세요.
여기에서는 다음 클래스를 사용합니다.리플렉션유틸리티의 간단한 사용법을 보여줍니다.
Main.java···실행하는 클래스
BeanUtil.java··· 리플렉션의 유틸리티 클래스
FreeSoft.java・・・프리소프를 나타내는 클래스로 리플렉션으로서 이용됩니다.
● 평소대로 실행한 경우
FreeSoft freeSoft = new FreeSoft();
freeSoft.setName(“Chat&Messenger 채팅도 메신저도!!”);
freeSoft.showName();
freeSoft.showPrice(0);
● 리플렉션을 사용하여 실행한 경우
// FreeSoft 클래스의 인스턴스 생성
Object invokeObject = BeanUtil.newInstance( "FreeSoft");
// name 필드에 값을 설정합니다.
BeanUtil.setProperty(invokeObject, “이름” , “Chat&Messenger 채팅도 메신저도!!”);
// FreeSoft showName() 메서드를 실행합니다.
BeanUtil.invoke(invokeObject, “showName”, null);
// FreeSoft showPrice() 메서드를 실행합니다.
// 메소드에 인수가 있는 경우는 Object형의 배열로 건네줄 필요가 있다.
BeanUtil.invoke(invokeObject, “showPrice”,new Object[]{new Integer(0)});
● 실행 결과
보통의 경우도, 리플렉션을 사용했을 경우도 실행 결과는 같습니다.
소프트 이름: Chat&Messenger 채팅도 메신저도! !
가격 : 0엔
>>> 리플렉션을 사용한 경우
소프트 이름: Chat&Messenger 채팅도 메신저도! !
가격 : 0엔
BeanUtil 메소드 상세
newInstance
public static Object newInstance(String className) throws Exception
- 캐릭터 라인 「className」로부터 인스턴스를 생성해 돌려줍니다.
-
- 매개변수:
className
– 정규화된 클래스 이름- 반환값:
- 완전한 클래스 이름의 새 인스턴스
- 예외:
예외
newInstance
public static Object newInstance(String className, Object[] argObj) throws Exception
- 캐릭터 라인 「className」로부터 인스턴스를 생성해 돌려줍니다.
-
- 매개변수:
className
– 정규화된 클래스 이름argObj
– 생성자 인수- 반환값:
- 완전한 클래스 이름의 새 인스턴스
- 예외:
예외
newInstance
public static Object newInstance(Class clazz) throws Exception
- 클래스 「clazz」로부터 인스턴스를 생성해 돌려줍니다.
-
- 매개변수:
clazz
– 클래스- 반환값:
- clazz의 새로운 인스턴스
- 예외:
예외
newInstance
public static Object newInstance(Class clazz, Object[] argObj) throws Exception
- 클래스 「clazz」로부터 인스턴스를 생성해 돌려줍니다.
-
- 매개변수:
clazz
– 클래스argObj
– 생성자 인수- 반환값:
- clazz의 새로운 인스턴스
- 예외:
예외
setProperty
public static void setProperty(Object invokeObject, String fieldName, Object value) throws Exception
- 오브젝트 「invokeObject」의 필드 「fieldName」의 setter 메소드를 호출해, 값 「value」를 포함합니다.
setter 메서드가 없으면 필드로 직접 값을 설정합니다. 다만, 이 경우 대상 프로퍼티의 액세스 한정자는 public인 것
-
- 매개변수:
invokeObject
– 실행할 객체fieldName
– 실행할 객체의 속성 이름value
– 설정할 값- 예외:
예외
– 다음 예외가 발생합니다.InvocationTargetException
– 기본 메소드가 예외를 throw하는 경우IllegalAccessException
– 이 Method 객체가 Java
언어 액세스 제어를 실시해, 기본이 되는 메소드에 액세스 할 수 없는 경우NoSuchMethodException
– 지정된 이름의 메소드를 찾을 수 없는 경우
getProperty
public static Object getProperty(Object invokeObject, String fieldName) throws Exception
- object invokeObject 의 필드 fieldName 의 getter 메소드를 호출치를 가져옵니다.
getter 메서드가 없으면 필드에서 직접 값을 가져옵니다. 다만, 이 경우 대상 프로퍼티의 액세스 한정자는 public인 것 -
- 매개변수:
invokeObject
– 실행할 객체fieldName
– 실행할 객체의 속성 이름- 반환값:
- 게터 메소드의 리턴 값
- 예외:
예외
– 다음 예외가 발생합니다.InvocationTargetException
– 기본 메소드가 예외를 throw하는 경우IllegalAccessException
– 이 Method 객체가 Java
언어 액세스 제어를 실시해, 기본이 되는 메소드에 액세스 할 수 없는 경우NoSuchFieldException
– 지정된 이름의 필드를 찾을 수 없는 경우
invoke
public static Object invoke(Object invokeObject, String callMethod, Object[] argObjects) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException
- 오브젝트 "invokeObject"의 메소드 "callMethod"를 실행합니다.
리턴값이 있는 경우는, Object형으로서 얻을 수가 있습니다. -
- 매개변수:
invokeObject
– 실행할 객체callMethod
– 실행할 메소드 이름argObjects
- 인수가 있으면 객체의 배열로 전달합니다. 인수가 없는 경우는 null 를 건네줍니다.- 반환값:
- callMethod를 실행한 리턴값
- 예외:
InvocationTargetException
– 기본 메소드가 예외를 throw하는 경우IllegalAccessException
– 이 Method 객체가 Java
언어 액세스 제어를 실시해, 기본이 되는 메소드에 액세스 할 수 없는 경우NoSuchMethodException
– 지정된 이름의 메소드를 찾을 수 없는 경우
findMethod
public static Method findMethod(Object invokeObject, String callMethod, Object[] argObjects) throws NoSuchMethodException
- 오브젝트 "invokeObject"의 메소드 "callMethod"를 검색합니다.
-
- 매개변수:
invokeObject
– 실행할 객체callMethod
– 실행할 객체의 메소드 이름argObjects
- 인수가 있으면 객체의 배열로 전달합니다. 인수가 없는 경우는 null 를 건네줍니다.- 반환값:
- 지정된 인수의 조건에 일치하는 Method 객체
- 예외:
NoSuchMethodException
– 일치하는 메소드를 찾을 수 없거나 이름이 “” 또는 “”인 경우
setField
public static void setField(Object invokeObject, String fieldName, Object value) throws IllegalAccessException, NoSuchFieldException
- 실행 대상의 오브젝트 「invokeObject」의 필드명 「fieldName」에 값 「value 」를 포함합니다.
-
- 매개변수:
invokeObject
– 실행할 객체fieldName
– 실행할 오브젝트의 필드 이름value
– 설정할 값- 예외:
IllegalAccessException
– 지정된 객체가 기본이 되는 필드 (또는 그 서브 클래스인가 구현측)
를 선언한다 클래스 또는 인터페이스의 인스턴스가 아닌 경우, 또는 랩 해제 변환이 실패했을 경우NoSuchFieldException
– 지정된 이름의 필드를 찾을 수 없는 경우
getField
public static Object getField(Object invokeObject, String fieldName) throws IllegalAccessException, NoSuchFieldException
- 실행 대상의 오브젝트 「invokeObject」의 필드명 「fieldName」의 값을 취득합니다.
-
- 매개변수:
invokeObject
– 실행할 객체fieldName
– 실행할 오브젝트의 필드 이름- 반환값:
- 반환 값
- 예외:
IllegalAccessException
– 지정된 객체가 기본이 되는 필드 (또는 그 서브 클래스인가 구현측)
를 선언한다 클래스 또는 인터페이스의 인스턴스가 아닌 경우, 또는 랩 해제 변환이 실패했을 경우NoSuchFieldException
– 지정된 이름의 필드를 찾을 수 없는 경우
hasField
public static boolean hasField(Object object, String fieldName) throws Exception
- 객체 "object"가 필드 이름 "fieldName"을 선언하는지 확인합니다.
-
- 매개변수:
객체
– 검사 대상 객체fieldName
– 검사할 필드 이름- 반환값:
- 선언하는 경우 true
- 예외:
예외
getAllFields
public static java.util.TreeSet getAllFields(Object object) throws Exception
-
- 매개변수:
객체
–- 반환값:
- 예외:
예외
getShortClassName
public static String getShortClassName(Object 객체)
- 오브젝트로부터 완전 수식되어 있지 않은 클래스명을 취득합니다.
-
- 매개변수:
객체
–- 반환값:
getShortClassName
public static String getShortClassName(String className)
- 완전 수식명으로부터 클래스명을 취득합니다.
-
- 매개변수:
className
–- 반환값:
getFieldName
public static String getFieldName(String methodName)
- 메소드 이름에서 필드 이름을 변경합니다. JavaBeans의 관례에 적합해야 합니다.
-
- 매개변수:
methodName
–- 반환값:
isClassExist
public static boolean isClassExist(String className)
- 완전 수식명 「className」이 존재하는 클래스명인가를 검증합니다.
-
- 매개변수:
className
–- 반환값:
getPropertyDescriptors
public static PropertyDescriptor[] getPropertyDescriptors(Object object) throws IntrospectionException
- 「object」의 오브젝트 정보를 보관 유지하는 PropertyDescriptor 를 돌려줍니다.
-
- 매개변수:
객체
–- 반환값:
- 예외:
java.beans.IntrospectionException
BeanUtil 소스 코드
/**
* 자바 리플렉션 API를 쉽게 사용할 수있는 유틸리티 클래스
*/
public class BeanUtil {
/** */
private static final String GET = “GET”;
/** */
private static final String SET = “SET”;
// ———————————————————— 『 newInstance 』
/**
* 캐릭터 라인 「className」로부터 인스턴스를 생성해 돌려줍니다.
* @param className 정규화된 클래스 이름
* @return 정규화된 클래스 이름의 새 인스턴스
* @throws 예외
*/
public static Object newInstance (String className) throws Exception {
try {
return Class.forName(className).newInstance();
} catch (NoClassDefFoundError e) {
System.err.println(“NoClassDefFoundError : ” + className);
throw e;
}
}
/**
* 캐릭터 라인 「className」로부터 인스턴스를 생성해 돌려줍니다.
* @param className 정규화된 클래스 이름
* @param argObj 생성자 인수
* @return 정규화된 클래스 이름의 새 인스턴스
* @throws 예외
*/
public static Object newInstance(String className, Object[] argObj)
throws Exception {
Class[] argClass = new Class[argObj.length];
for(int i = 0; i < argObj.length; i++) {
argClass[i] = argObj[i].getClass();
}
Constructor c = Class.forName(className).getConstructor(argClass);
return c.newInstance(argObj);
}
/**
* 클래스 "clazz"에서 인스턴스를 생성하고 반환합니다.
* @param clazz 클래스
* @return clazz의 새로운 인스턴스
* @throws 예외
*/
public static Object newInstance (Class clazz) throws Exception {
return clazz.newInstance();
}
/**
* 클래스 "clazz"에서 인스턴스를 생성하고 반환합니다.
* @param clazz 클래스
* @param argObj 생성자 인수
* @return clazz의 새로운 인스턴스
* @throws 예외
*/
public static Object newInstance(Class clazz, Object[] argObj)
throws Exception {
Class[] argClass = new Class[argObj.length];
for(int i = 0; i < argObj.length; i++) {
argClass[i] = argObj[i].getClass();
}
Constructor c = clazz.getConstructor(argClass);
return c.newInstance(argObj);
}
// —————————————————————— 『 Method 』
/**
* 객체 "invokeObject"의 필드 "fieldName"의 setter 메서드
* 호출, 값 "value"를 저장합니다.
* <br>
* setter 메소드가 없으면 필드로 직접 값을 설정합니다.
* 다만, 이 경우 대상 프로퍼티의 액세스 한정자는 public인 것
* @param invokeObject 실행 대상 객체
* @param fieldName 실행할 객체의 속성 이름
* @param value 설정할 값
* @throws Exception 다음 예외가 발생합니다.
* @throws InvocationTargetException 기본이 되는 메소드가 예외를 슬로우 하는 경우
* @throws IllegalAccessException 이 Method 객체가 Java
* 언어 액세스 제어를 실시해, 기본이 되는 메소드에 액세스 할 수 없는 경우
* @throws NoSuchMethodException 지정된 이름의 메소드를 찾을 수 없는 경우
*/
public static void setProperty(Object invokeObject, String fieldName,
Object value) throws Exception {
try {
Method method = searchMethod(invokeObject, fieldName, SET);
Class[] paramClasses = method.getParameterTypes();
Object[] valueArray = null;
if (paramClasses[0].isInstance(value)) {
// 설정하는 객체가 인수의 클래스의 서브 클래스라면 변환하지 않는다.
valueArray = new Object[] { value };
} else {
valueArray = new Object[] { convObject(value, paramClasses[0]
.getName()) };
}
method.invoke(invokeObject, valueArray);
} catch (NoSuchMethodException e) {
try {
// setter 메서드가 없으면 필드에 직접 설정합니다.
setField(invokeObject, fieldName, value);
} catch (NoSuchFieldException fe) {
String errorMes = “\n클래스” + getShortClassName(invokeObject)
+ “는” + “필드 “” + fieldName + “”에 대해 \n”
+ “액세스 가능한 세터 메소드가 없고.”
+ "필드 "" + fieldName
+ “”도 public이 아닙니다. "+"";
throw new IllegalAccessException (errorMes);
}
}
}
/**
* 객체 invokeObject 필드 fieldName getter 메서드
* 호출 값을 가져옵니다. <br>
* getter 메소드가 없으면 필드에서 직접 값을 가져옵니다.
* 다만, 이 경우 대상 프로퍼티의 액세스 한정자는 public인 것
* @param invokeObject 실행 대상 객체
* @param fieldName 실행할 객체의 속성 이름
* @return 게터 메소드의 리턴 값
* @throws Exception 다음 예외가 발생합니다.
* @throws InvocationTargetException 기본이 되는 메소드가 예외를 슬로우 하는 경우
* @throws IllegalAccessException 이 Method 객체가 Java
* 언어 액세스 제어를 실시해, 기본이 되는 메소드에 액세스 할 수 없는 경우
* @throws NoSuchFieldException 지정된 이름의 필드를 찾을 수 없는 경우
*/
public static Object getProperty (Object invokeObject, String fieldName)
throws Exception {
try {
Method method = searchMethod(invokeObject, fieldName, GET);
return method. invoke (invokeObject, null);
} catch (NoSuchMethodException e) {
return getField(invokeObject, fieldName);
}
}
/**
* 오브젝트 "invokeObject"의 메소드 "callMethod"를 실행합니다.
* 리턴값이 있는 경우는 Object형으로서 얻을 수 있습니다.
* @param invokeObject 실행 대상 객체
* @param callMethod 실행할 메소드 이름
* @param argObjects 인수가 있는 경우는 객체의 배열로서 건네준다.
* 인수가 없는 경우는 null 를 건네줍니다.
* @return "callMethod"를 실행한 반환 값
* @throws InvocationTargetException 기본이 되는 메소드가 예외를 슬로우 하는 경우
* @throws IllegalAccessException 이 Method 객체가 Java
* 언어 액세스 제어를 실시해, 기본이 되는 메소드에 액세스 할 수 없는 경우
* @throws NoSuchMethodException 지정된 이름의 메소드를 찾을 수 없는 경우
*/
public static Object invoke(Object invokeObject, String callMethod,
Object[] argObjects) throws InvocationTargetException,
IllegalAccessException, NoSuchMethodException {
Method method = findMethod(invokeObject, callMethod, argObjects);
return method.invoke(invokeObject, argObjects);
}
/**
* 오브젝트 "invokeObject"의 메소드 "callMethod"를 검색합니다.
* @param invokeObject 실행 대상 객체
* @param callMethod 실행 대상 객체의 메소드 이름
* @param argObjects 인수가 있는 경우는 객체의 배열로서 건네준다.
* 인수가 없는 경우는 null 를 건네줍니다.
* @return 지정된 인수의 조건에 일치하는 Method 객체
* @throws NoSuchMethodException 일치하는 메소드가 발견되지 않는 경우,
* 또는 이름이 “ "또는" ”의 경우
*/
public static Method findMethod(Object invokeObject, String callMethod,
Object[] argObjects) throws NoSuchMethodException {
Class[] paramClasses = null;
Method[] methods = invokeObject.getClass().getMethods();
top: for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(callMethod)) {
if (argObjects == null
&& methods[i].getParameterTypes().length == 0) {
return methods[i];
}
if (argObjects == null) {
continue;
}
paramClasses = methods[i].getParameterTypes();
if (paramClasses.length == argObjects.length) {
// 모든 매개 변수 목록의 형식과 인수의 형식 확인
for(int j = 0; j < paramClasses.length; j++) {
클래스 paramClass = paramClasses[j];
Object argObj = argObjects[j];
// 인수의 형태가 원시적 인 경우, 인수의 객체
// null이 아닌 프리미티브
// 역시, Number의 서브 클래스라면 OK로 한다.
if (argObj == null) {
continue;
}
if (paramClass.isPrimitive()
&& (argObj instanceof Number || argObj
.getClass().isPrimitive())) {
continue;
}
if (!paramClass.isInstance(argObj)) {
// 형에 암묵 변환의 호환성이 없는 시점에서, 다음의 메소드에
continue top;
}
}
return methods[i];
}
}
}
String paramLength = (paramClasses != null) ? Integer
.toString(paramClasses.length) : “”;
String errorMes = getShortClassName(invokeObject) + “에 메서드”
+ callMethod + “없음.” + “[ paramClasses.length ] = ”
+ paramLength + “,[ argObjects.length ] = ” + argObjects.length
+ “”;
throw new NoSuchMethodException (errorMes);
}
// ——————————————————————-『Field』
/**
* 실행 대상의 오브젝트 「invokeObject」의 필드명 「fieldName」에 값
* "value"를 저장합니다.
* @param invokeObject 실행 대상 객체
* @param fieldName 실행 대상 객체의 필드 이름
* @param value 설정할 값
* @throws IllegalAccessException 지정된 객체가 기본
* 되는 필드 (또는 그 서브 클래스인가 구현측)
*를 선언하는 클래스 또는 인터페이스의 인스턴스가 아닌 경우,
* 또는 랩 해제 변환이 실패한 경우
* @throws NoSuchFieldException 지정된 이름의 필드를 찾을 수 없는 경우
*/
public static void setField (Object invokeObject, String fieldName,
Object value) throws IllegalAccessException, NoSuchFieldException {
Field field = searchField(invokeObject, fieldName);
String className = field.getType().getName();
Object convObj = null;
if (field.getType().isInstance(value)) {
convObj = value;
} else {
convObj = convObject(value, className);
}
field.set(invokeObject, convObj);
}
/**
* 실행 대상의 오브젝트 「invokeObject」의 필드명 「fieldName」의 값을
* 가져옵니다.
* @param invokeObject 실행 대상 객체
* @param fieldName 실행 대상 객체의 필드 이름
* @return 리턴 값
* @throws IllegalAccessException 지정된 객체가 기본
* 되는 필드 (또는 그 서브 클래스인가 구현측)
*를 선언하는 클래스 또는 인터페이스의 인스턴스가 아닌 경우,
* 또는 랩 해제 변환이 실패한 경우
* @throws NoSuchFieldException 지정된 이름의 필드를 찾을 수 없는 경우
*/
public static Object getField (Object invokeObject, String fieldName)
throws IllegalAccessException, NoSuchFieldException {
Field field = searchField(invokeObject, fieldName);
return field.get(invokeObject);
}
/**
* 객체 "object"가 필드 이름 "fieldName"을 선언하는지 여부
* 확인합니다.
* @param object 검사 대상 객체
* @param fieldName 검사 할 필드 이름
* @return 선언하는 경우 true
* @throws 예외
*/
public static boolean hasField (Object object, String fieldName)
throws Exception {
PropertyDescriptor[] props = getPropertyDescriptors(object);
for(int i = 0; i < props.length; i++) {
String _fieldName = props[i].getName();
if (fieldName.equals(_fieldName)) {
return true;
}
}
return false;
}
/**
*
* @param 객체
* @return
* @throws 예외
*/
public static TreeSet getAllFields (Object object) throws Exception {
TreeSet fieldSet = new TreeSet();
// 메소드에서 속성 이름 검색
PropertyDescriptor[] props = getPropertyDescriptors(object);
for(int i = 0; i < props.length; i++) {
String fieldName = props[i].getName();
fieldSet.add(fieldName);
}
// 필드에서 속성 이름 가져 오기
Field[] fields = object.getClass().getFields();
for(int i = 0; i < fields.length; i++) {
String fieldName = fields[i].getName();
if (!fieldSet.contains(fieldName)) {
fieldSet.add(fieldName);
}
}
return fieldSet;
}
/**
*
* @param invokeObject 실행 대상 객체
* @param fieldName 실행 대상 객체의 필드 이름
* @return 지정된 인수의 조건에 일치하는 Filed 객체
* @throws NoSuchFieldException 지정된 이름의 필드를 찾을 수 없는 경우
*/
private static Field searchField (Object invokeObject, String fieldName)
throws NoSuchFieldException {
try {
return invokeObject.getClass().getField(fieldName);
} catch (NoSuchFieldException e) {
//이 범위는 테이블 열 이름에서 가져옵니다.
fieldName = checkFieldName(fieldName);
Field[] fields = invokeObject.getClass().getFields();
for(int i = 0; i < fields.length; i++) {
if (fields[i].getName().equalsIgnoreCase(fieldName)) {
return fields[i];
}
}
throw new NoSuchFieldException (fieldName);
}
}
// ——————————————————————-『기타』
/**
* 오브젝트로부터 완전 수식되어 있지 않은 클래스명을 취득합니다.
* @param 객체
* @return
*/
public static String getShortClassName (Object object) {
if (object == null) {
return “null”;
}
String name = object.getClass().getName();
return getShortClassName(name);
}
/**
* 완전 수식명으로부터 클래스명을 취득합니다.
* @param className
* @return
*/
public static String getShortClassName (String className) {
int index = className.lastIndexOf(“.”);
return className.substring(index + 1);
}
/**
* 메소드 이름에서 필드 이름을 변경합니다. JavaBeans의 관례에 적합
* 필요합니다.
* @param methodName
* @return
*/
public static String getFieldName (String methodName) {
String fieldName = null;
if (methodName.startsWith(“is”)) {
fieldName = methodName.substring(2);
} else {
fieldName = methodName.substring(3);
}
fieldName = convString(fieldName, 0, “L”);
return fieldName;
}
/**
* 완전 수식명 「className」이 존재하는 클래스명인가를 검증합니다.
* @param className
* @return
*/
public static boolean isClassExist (String className) {
try {
Class.forName(className);
return true;
} catch (Exception e) {
return false;
}
}
private final static Map beanInfoCache = new HashMap();
/**
* 「object」의 오브젝트 정보를 보관 유지하는 PropertyDescriptor를 돌려줍니다.
* @param 객체
* @return
* @throws IntrospectionException
*/
public static PropertyDescriptor[] getPropertyDescriptors(Object object)
throws IntrospectionException {
BeanInfo beanInfo = (BeanInfo) beanInfoCache.get(object.getClass());
if (beanInfo == null) {
beanInfo = Introspector.getBeanInfo(object.getClass());
beanInfoCache.put(object.getClass(), beanInfo);
}
// BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
return beanInfo.getPropertyDescriptors();
}
// ————————————————————————–
// ———————————————— 『이하 프라이빗 메소드』
// ————————————————————————–
/**
* PropertyDescriptor를 기반으로 인수의 fieldName에 대한 접근 방법을 검색합니다.
* @param invokeObject 실행 대상 객체
* @param fieldName 필드 이름
* @param type getter 메소드 ⇒ GET getter 메소드 ⇒ SET
* @return 지정된 인수의 조건에 일치하는 Method 객체
* @throws NoSuchMethodException 일치하는 메소드가 발견되지 않는 경우,
* 또는 이름이 “ "또는" ”
*의 경우
* @throws IntrospectionException
*/
private static Method searchMethod (Object invokeObject, String fieldName,
String type) throws NoSuchMethodException, IntrospectionException {
Method method = null;
fieldName = checkFieldName(fieldName);
PropertyDescriptor[] props = getPropertyDescriptors(invokeObject);
for(int i = 0; i < props.length; i++) {
String name = props[i].getName();
if (!name.equalsIgnoreCase(fieldName)) {
continue;
}
if (type.equals(GET)) {
method = props[i].getReadMethod();
} else {
method = props[i].getWriteMethod();
}
if (method == null) {
continue;
}
return method;
}
// 메소드가 존재하지 않는 경우.
throw new NoSuchMethodException(“클래스에 메소드가 없습니다.”
+ “(대소문자 구분 없음):” + type.toLowerCase()
+ convString(fieldName, 0, “U”) + “()”);
}
/**
* 인수의 fieldName이 컬럼명의 경우를 체크해, 컬럼명의 경우는
* 변환하여 반환합니다.
*
* MAIL_ADDRESS ⇒ MAILADDRESS ↓ mailaddress = mailAddress
* @param fieldName 필드 이름 또는 열 이름
* @return 필드 이름
*/
private static String checkFieldName (String fieldName) {
int index = fieldName.indexOf(“_”);
while (true) {
if (index == -1) {
return fieldName;
}
StringBuffer convcloumn = new StringBuffer(fieldName);
convcloumn.deleteCharAt (index);
fieldName = convcloumn.toString();
index = fieldName.indexOf(“_”);
}
}
/**
* 변환 대상의 객체 object 를 convClassName 의 형태로 변환합니다.
*
* @param object 변환 대상의 오브젝트
* @param convClassName 변환할 형식의 클래스 문자열
* @return 변환된 객체
*/
private static Object convObject (Object object, String convClassName) {
if (object == null) {
// 원시적 타입으로의 변환은 null로 돌려주면 에러가 된다.
// 0의 래퍼로 한다.
if (convClassName.equals(“int”)) {
return new Integer(0);
} else if (convClassName.equals(“long”)) {
return new Long(0);
} else {
return null;
}
}
if (object.getClass().getName().equals(convClassName)) {
return object;
}
// —————————————-- 'object instanceof String '
if (object instanceof String) {
if (convClassName.equals(“java.lang.String”)) {
return object;
} else if (convClassName.equals(“java.lang.Long”)
|| convClassName.equals(“long”)) {
String str = (String) object;
if (isExist(str)) {
// 일단 BigDecimal로 변환하지 않으면 상태가 나쁘다.
// 1000.00000
BigDecimal big = new BigDecimal(str);
return new Long(big.longValue());
} else {
// str이 쉘 리터럴이면 초기 값의 "0"
return new Long(0);
}
} else if (convClassName.equals(“java.sql.Date”)) {
return toSqlDate((String) object);
} else if (convClassName.equals(“java.sql.Timestamp”)) {
Date date = toSqlDate((String) object);
return new Timestamp(date.getTime());
} else if (convClassName.equals(“java.lang.Integer”)
|| convClassName.equals(“int”)) {
// str이 쉘 리터럴이면 초기 값의 "0"
String str = (String) object;
if (isExist(str)) {
BigDecimal big = new BigDecimal(str);
return new Integer(big.intValue());
} else {
return new Integer(0);
}
} else if (convClassName.equals(“boolean”)) {
return Boolean.valueOf(object.toString());
} else if (convClassName.equals("java.math.BigDecimal")) {
String temp = ((String) object).trim();
// temp.length() == 0인 경우 0이 아니라 null로 하는 것이 무난.
if (temp.length() == 0) {
return null;
} else {
return new BigDecimal(temp);
}
}
throwNoSupprt(object, convClassName);
}
// ——————————— 『 object instanceof java.sql.Date 』
else if (object instanceof java.sql.Date) {
if (convClassName.equals(“java.lang.String”)) {
return toStringDate((java.sql.Date) object, “yyyy/MM/dd”);
} else if (convClassName.equals(“java.sql.Date”)) {
return object;
} else if (convClassName.equals(“java.sql.Timestamp”)) {
return new Timestamp(((Date) object).getTime());
}
throwNoSupprt(object, convClassName);
}
// ————————————-『object instanceof Timestamp』
else if (object instanceof Timestamp) {
long time = ((Timestamp) object).getTime();
if (convClassName.equals(“java.lang.String”)) {
return toStringDate(time, “yyyy/MM/dd HH:mm:ss”);
} else if (convClassName.equals(“java.sql.Date”)) {
return new java.sql.Date(time);
} else if (convClassName.equals(“java.sql.Timestamp”)) {
return object;
}
throwNoSupprt(object, convClassName);
}
// —————————————— 『 object instanceof Integer 』
else if (object instanceof Integer) {
if (convClassName.equals(“java.lang.Integer”)
|| convClassName.equals(“int”)) {
return object;
} else if (convClassName.equals(“java.lang.String”)) {
return object.toString();
} else if (convClassName.equals(“java.lang.Long”)
|| convClassName.equals(“long”)) {
return new Long (((Integer) object). longValue ());
} else if (convClassName.equals("java.math.BigDecimal")) {
return new BigDecimal(((Integer) object).intValue());
}
throwNoSupprt(object, convClassName);
}
// —————————————— 『 object instanceof Long 』
else if (object instanceof Long) {
if (convClassName.equals(“java.lang.Long”)
|| convClassName.equals(“long”)) {
return object;
} else if (convClassName.equals(“java.lang.String”)) {
return object.toString();
} else if (convClassName.equals(“java.lang.Integer”)
|| convClassName.equals(“int”)) {
return new Integer (((Long) object). intValue ());
} else if (convClassName.equals("java.math.BigDecimal")) {
return new BigDecimal(((Long) object).longValue());
}
throwNoSupprt(object, convClassName);
}
// —————————————— 『 object instanceof Double 』
else if (object instanceof Double) {
if (convClassName.equals(“java.lang.String”)) {
// COLUMN NUMBER(8,0)
// windows oracle > BigDecimal
// UNIX oracle > Double
BigDecimal big = new BigDecimal(((Double) object).doubleValue());
int scale = big.scale();
if (scale == 0) {
return big.toString();
} else {
// 반올림이 필요한 경우에는 지원하지 않습니다.
throwNoSupprt(object, convClassName);
}
}
if (convClassName.equals(“java.lang.Integer”)
|| convClassName.equals(“int”)) {
return new Integer(((Double) object).intValue());
} else if (convClassName.equals(“java.lang.Long”)
|| convClassName.equals(“long”)) {
return new Long (((Double) object). longValue ());
} else if (convClassName.equals("java.math.BigDecimal")) {
return new BigDecimal (((Double) object). doubleValue ());
}
throwNoSupprt(object, convClassName);
}
// ———————————— 『 object instanceof BigDecimal 』
else if (object instanceof BigDecimal) {
if (convClassName.equals(“java.lang.String”)) {
return object.toString();
} else if (convClassName.equals(“java.lang.Long”)
|| convClassName.equals(“long”)) {
return new Long (((BigDecimal) object). longValue ());
} else if (convClassName.equals(“java.lang.Integer”)
|| convClassName.equals(“int”)) {
return new Integer (((BigDecimal) object). intValue ());
}
throwNoSupprt(object, convClassName);
}
// —————————————-『object instanceof byte[]』
else if (object instanceof byte[]) {
if (convClassName.equals(“java.sql.Blob”)) {
return object;
}
throwNoSupprt(object, convClassName);
}
// ———————————————— 『 object 가 Boolean 』
else if (object instanceof Boolean) {
if (convClassName.equals(“boolean”)) {
return object;
}
throwNoSupprt(object, convClassName);
}
// ———————————————-『object 가 boolean[] 』
else if (object instanceof boolean[]) {
if (convClassName.equals(“java.lang.String”)) {
boolean[] bs = (boolean[]) object;
StringBuffer buff = new StringBuffer(“[“);
for(int i = 0; i < bs.length; i++) {
buff.append(bs[i] + “,”);
}
buff.deleteCharAt(buff.length() – 1);
buff.append(“]”);
return buff.toString();
}
throwNoSupprt(object, convClassName);
}
throwNoSupprt(object, convClassName);
return null;
}
/**
* 변환이 서포트되어 있지 않은 경우에 슬로우 합니다.
*
* @param object 변환 대상의 오브젝트
* @param convClassName 변환할 형식
*/
private static void throwNoSupprt (Object object, String convClassName) {
String className = (object != null) ? object.getClass().getName()
: “null”;
String errorMess = “\n이 Object의 형식 변환 처리는 아직 지원되지 않습니다.\n”
+ ” [ Object ] = ” + object + “,[ Object 형식 ] = ” + className
+ “,[ convertClass ] = ” + convClassName + “”;
throw new UnsupportedOperationException (errorMess);
}
/**
* 문자열 [str]에 대해 [index] 위치의 문자를 대문자 또는 소문자로 변환합니다.
* <p>
* @param str 평가 대상 문자열
* @param index 지정한 위치
* @param toCase 대문자로 변환 ⇒ U | u 소문자로 변환 ⇒ L | l
* @return 변환 후 문자열
*/
private static String convString (String str, int index, String toCase) {
if (str == null || str.trim().length() == 0) {
return str;
} else {
String temp = str.substring(index, index + 1);
if (toCase.equalsIgnoreCase(“u”)) {
temp = temp.toUpperCase();
} else {
temp = temp.toLowerCase();
}
StringBuffer tempBuffer = new StringBuffer(str);
tempBuffer.replace(index, index + 1, temp);
return tempBuffer.toString();
}
}
/**
* [value]가 유효한 값인지 확인합니다.
*
* @param value 평가 대상 문자열
* @return [true]: null이 아니라 ""가 아닌 경우
*/
private static boolean isExist (String value) {
if (value != null && value.length() != 0) {
return true;
}
return false;
}
/**
* java.util.Date 클래스 또는 그 서브 클래스를 지정된 형식으로
* 문자열로 변환합니다.
* @param date 변환 대상의 java.util.Date 클래스
* @param pattern 지정된 형식
* @return 형식화된 날짜 문자열
*/
private static String toStringDate (Date date, String pattern) {
SimpleDateFormat sdFormat = new SimpleDateFormat(pattern);
return sdFormat.format(date);
}
private static java.sql. Date toSqlDate (String strDate) {
Calendar cal = toCalendar(strDate);
return toSqlDate(cal);
}
private static java.sql.Date toSqlDate(Calendar cal) {
long l = cal.getTime().getTime();
return new java.sql.Date(l);
}
/**
* 시간의 긴 값을 지정된 형식으로 문자열로 변환합니다.
* @param time 현재 시간의 밀리 초를 나타내는 긴 값
* @param pattern 지정된 형식
* @return 형식화된 날짜 문자열
*/
private static String toStringDate (long time, String pattern) {
return toStringDate(new Date(time), pattern);
}
/**
* String ⇒ java.sql.Date
*
* 다음 날짜 문자열을 java.sql.Date로 변환
* yyyy/MM/dd HH:mm:ss.SSS yyyy-MM-dd HH:mm:ss.SSS
*
* “20030407” “2003/04/07” “2003-04-07” “2003/04/07 15:20:16” “2003-04-07
* 15:20:16”
* @param strDate
* @return
*/
private static Calendar toCalendar (String strDate) {
strDate = format(strDate);
Calendar cal = Calendar.getInstance();
int yyyy = Integer.parseInt(strDate.substring(0, 4));
int MM = Integer.parseInt(strDate.substring(5, 7));
int dd = Integer.parseInt(strDate.substring(8, 10));
int HH = cal.get(Calendar.HOUR_OF_DAY);
int mm = cal.get(Calendar.MINUTE);
int ss = cal.get(Calendar.SECOND);
int SSS = cal.get(Calendar.MILLISECOND);
cal.clear();
cal.set(yyyy, MM – 1, dd);
int len = strDate.length();
switch (len) {
case 10:
break;
case 16: // yyyy/MM/dd HH:mm
HH = Integer.parseInt(strDate.substring(11, 13));
mm = Integer.parseInt(strDate.substring(14, 16));
cal.set (Calendar.HOUR_OF_DAY, HH);
cal.set(Calendar.MINUTE, mm);
break;
case 19: // yyyy/MM/dd HH:mm:ss
HH = Integer.parseInt(strDate.substring(11, 13));
mm = Integer.parseInt(strDate.substring(14, 16));
ss = Integer.parseInt(strDate.substring(17, 19));
cal.set (Calendar.HOUR_OF_DAY, HH);
cal.set(Calendar.MINUTE, mm);
cal.set(Calendar.SECOND, ss);
break;
case 23: // yyyy/MM/dd HH:mm:ss.SSS
HH = Integer.parseInt(strDate.substring(11, 13));
mm = Integer.parseInt(strDate.substring(14, 16));
ss = Integer.parseInt(strDate.substring(17, 19));
SSS = Integer.parseInt(strDate.substring(20, 23));
cal.set (Calendar.HOUR_OF_DAY, HH);
cal.set(Calendar.MINUTE, mm);
cal.set(Calendar.SECOND, ss);
cal.set (Calendar.MILLISECOND, SSS);
break;
default:
throw new IllegalStateException (
"이 문자열 문자열은 날짜 문자열로 변환 할 수 없습니다 :"
+ strDate);
}
return cal;
}
/**
* 모든 날짜 문자열을 “yyyy/MM/dd” or “yyyy/MM/dd HH:mm:ss”
* 형식으로 변환을 시도합니다.
* 예: 03/1/3 ⇒ 2003/01/03
* @param strDate
* @return
*/
private static String format (String strDate) {
strDate = strDate.trim();
String yyyy = null;
String MM = null;
String dd = null;
String HH = null;
String mm = null;
String ss = null;
String SSS = null;
// “-” or “/” 가 없는 경우
if (strDate.indexOf(“/”) == -1 && strDate.indexOf(“-“) == -1) {
if (strDate.length() == 8) {
yyyy = strDate.substring(0, 4);
MM = strDate.substring(4, 6);
dd = strDate.substring(6, 8);
return yyyy + “/” + MM + “/” + dd;
} else {
yyyy = strDate.substring(0, 4);
MM = strDate.substring(4, 6);
dd = strDate.substring(6, 8);
HH = strDate.substring(9, 11);
mm = strDate.substring(12, 14);
ss = strDate.substring(15, 17);
return yyyy + “/” + MM + “/” + dd + ” ” + HH + “:” + mm + “:”
+ss;
}
}
StringTokenizer token = new StringTokenizer(strDate, “_/-:. “);
StringBuffer result = new StringBuffer();
for (int i = 0; token.hasMoreTokens(); i++) {
String temp = token.nextToken();
switch (i) {
case 0:// 년의 부분
yyyy = fillString(strDate, temp, “f”, “20”, 4);
result.append(yyyy);
break;
case 1:// 달 부분
MM = fillString(strDate, temp, “f”, “0”, 2);
result.append(“/” + MM);
break;
case 2:// 일 부분
dd = fillString(strDate, temp, “f”, “0”, 2);
result.append(“/” + dd);
break;
case 3:// 시간 부분
HH = fillString(strDate, temp, “f”, “0”, 2);
result.append(” ” + HH);
break;
case 4:// 분 부분
mm = fillString(strDate, temp, “f”, “0”, 2);
result.append(“:” + mm);
break;
case 5:// 초 부분
ss = fillString(strDate, temp, “f”, “0”, 2);
result.append(“:” + ss);
break;
case 6:// 밀리초 부분
SSS = fillString(strDate, temp, “b”, “0”, 3);
result.append(“.” + SSS);
break;
}
}
return result.toString();
}
private static String fillString (String strDate, String str,
String position, String addStr, int len) {
if (str.length() > len) {
String mes = strDate + “이 String 문자열은 날짜 문자열로 변환할 수 없습니다”;
throw new IllegalStateException (mes);
}
return fillString(str, position, addStr, len);
}
/**
* 문자열 [str]에 대해 보충하는 문자열 [addStr]을 [position]의 위치로 [len]로
* 만족할 때까지 삽입합니다.
* <p>
* 예: String ss = StringUtil.fillString(“aaa”,”b”,”0″,7); ss ⇒ “aaa0000”
*
* ※fillString()은 len에 만족할 때까지 삽입합니다만, addString()은 len분 삽입합니다.
*
* @param str 대상 문자열
* @param position 앞에 삽입 ⇒ F/f 뒤에 삽입 ⇒ B/b
* @param addStr 삽입 할 문자열
* @param len 보충하기 전의 자릿수
* @return 변환 후 문자열. [str]이 null 또는 빈 리터럴도 [addStr]을 [len]
* 만족할 때까지 삽입된 결과를 반환합니다.
*/
private static String fillString(String str, String position,
String addStr, int len) {
StringBuffer tempBuffer = null;
if (!isExist(str)) {
tempBuffer = new StringBuffer();
for(int i = 0; i < len; i++) {
tempBuffer.append(addStr);
}
return tempBuffer.toString();
} else if (str.length() != len) {
tempBuffer = new StringBuffer(str);
while (len > tempBuffer.length()) {
if (position.equalsIgnoreCase("f")) {
tempBuffer.insert(0, addStr);
} else {
tempBuffer.append(addStr);
}
}
return tempBuffer.toString();
}
return str;
}
}