레이블이 Reflection인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Reflection인 게시물을 표시합니다. 모든 게시물 표시

2013년 1월 21일 월요일

Java Byte code 생성을 이용하여, Reflection 대체하기

아주 유명한 iBatis 같은 것도, 처음에는 Reflection을 통해 클래스의 동적 생성,  필드 값 설정 등을 실행했다가, 성능때문에 Java Byte Code 를 동적으로 생성하게 하여, 생성된 코드를 동적으로 실행하도록 변환했던것으로 기억합니다.

.NET 에서도 Reflection 의 성능때문에 Dynamic Method 라는 기법을 이용하여 동적으로 ILCode 를 생성하고,  그것을 실행하면 기존 Reflection 보다 4~100배 이상으로 빨라집니다.

이런 좋은 기능이 있는데, 안쓰면 바보겠죠? Java로 넘어 온 후, 실제 이런 기능의 필요성을 많이 못 느낄 정도로 iBatis 나 Hibernate 만을 사용했지만, 점점 JDBC 로우 레벨로 내려가다 보니,  꼭 필요하게 되는군요...

예를들어 객체 정보를 Map 으로 표현하고, Map으로 표현된 정보를 다른 객체 정보에 설정하려고 하는 기능 ( Mapper ) 는 상당히 많이 사용되기도 합니다. Model Mapper 라는 훌륭한 라이브러리가 있지만,  모든 것을 다 제공하는 게 아니고, 변형해서 쓰고자 하는 경우가 있어, 찾아 봤습니다.

여러가지 라이브러리가 있었지만, 작고 심플한 라이브러리를 찾다가 reflectasm 이란 놈을 발견했습니다. 
소스를 보니 제가 원하는 딱 그 것이였습니다. byte code generation 을 통해 성능을 향상시킨다^^ 캬... Good 

라이브러리를 가지고, 기존 .NET 코드와 유사하게 골격을 갖춰 봤습니다.
DynamicAccessor 라고 수형정보만 제공하면, 객체의 생성,  필드 정보 조회/수정, 메소드 실행 등을 할 수 있습니다.


동적으로 특정 수형의 속성이나 메소드 실행하는 것은 환경설정이나 사용자 매크로 등을 파싱하여 실제 클래스를 수행하게 할 때 아주 유용합니다.



다음 코드는 DynamicAccessor 를 생성해주는 Factory입니다. 굳이 factory를 만든 이유는 DynamicAccessor 생성 비용이 일반 클래스의 생성 비용에 비하여 상당한 비용이 들어가므로, Cache 를 이용하여, 재활용하자는 의미가 큽니다.  Cache는 google guava의 LoadingCache 를 사용한 이유는  Cache 자체적으로 항목들을 관리할 수 있어, 코드량 및 실수가 적어지는 것이 장점입니다.


마지막으로 DynamicAccessor 를 테스트하는 코드입니다. (reflectasm 에 성능 측정 코드가 있어 굳이 만들지 않았습니다)


2013년 1월 19일 토요일

Scala 의 reflection 을 이용한 객체 생성

리플렉션을 공부할 때, 가장 먼저 해보는 것이 수형을 이용하여 기본 생성자를 통한 객체를 생성해 보는 것입니다.
java에서는 당연히 가능하고, 아주 쉽습니다. java 의 generic이 . NET과는 달리 JVM 상에서는 타입을 지워버리는 (erasure) 특성때문에 적응하는데 좀 애를 먹었습니다. ㅋㅋ


그럼 Scala에서는? 다행히 Scala 2.10.0 부터는 scala.reflect.runtime.unverse.TypeTag 로 더 많은 기능을 제공하지만, 기존 2.9.2 버전에서도 지원하는 scala.reflect.ClassTag 를 이용하면 java와는 달리 .NET처럼 수형을 제공하지 않아도 동적으로 수형을 알아낼 수 있더군요.


와 같이 기본 생성자를 가진 클래스는 손쉽게 생성할 수 있습니다.
좀 더 나가서 기본 생성자 이외에 인자가 있는 클래스의 경우 인자를 주고 생성하는 경우는 어떨까? 제작해 보았습니다.

인자가 있는 경우는 인자로부터 수형을 추출하여, 해당 수형들을 인자로 받을 수 있는 생성자를 찾습니다.
추출한 생성자에게 인자들을 제공하여 생성하면 됩니다.


여기서 문제가 발생했습니다... java의 primitive type인 boolean, char, byte, short, int, long, float, double 이 문제였습니다. Scala가 boxing, unboxing 을 최소화하기위해 scala.Int, scala.Long 등의 수형을 정의하여 자동으로 (implicit) 하게 변환이 되도록 하였습니다. 이러한 기능으로 JVM 에서 구현될 때 scala.Int, scala.Long 등을 java 의 primitive type 으로 매칭시켜줘야 합니다.


이 변환 메소드를 써야 제대로 실행됩니다.

마지막으로, 아예 생성자의 수형까지 지정해서 생성자를 찾을 수 있도록 하면 다음과 같습니다.

자 이제 구현한 메소드를 이용하여 실제 테스트 코드를 제작하면 다음과 같습니다.



Scala가 Generic 에서는 Java 보다 .NET에 유사하여 이해하기도 쉽고, 더 쉽게 적용이 가능하다고 생각되네요.

위에서 사용한 ClassTag[T] 의 단점은 Nested Class 에 대해서는 지원하지 않습니다. 내부 Class 도 지원하려면 TypeTag[T]  로 해야 합니다.