본문 바로가기

Java

Jrebel Ibatis Plugin을 만들어 봅시다.. -2-

일단 Jrebel 플러그인의 메인이 되는 클래스는 org.zeroturnaround.javarebel.Plugin 인터페이스를 구현해야 합니다.


여기서 중요한 메서드가 preinit() 메서드 입니다. 앞서 얘기했듯이 Jrebel이 다양한 프레임워크에서 플러그인을 통해
클래스 실시간 로딩을 지원하는 방법은 컴파일 된 바이트코드를 핸들링하는 것 입니다.
이렇게 핸들링 할 클래스를 합쳐주는 메서드가 바로 preinit() 메서드입니다.

그리고 , void org.zeroturnaround.javarebel.Integration.addIntegrationProcessor(ClassLoader arg0, String arg1, ClassBytecodeProcessor arg2) 를 통해 등록되는 형식을 보면 arg1은 핸들링 할 클래스의 fullname이 그리고
arg2에는 그 클래스의 바이트코드를 핸들링 하는 클래스가 들어갑니다.

직접 CBP 클래스들을 보겠습니다.



ClassInfoCBP.java 입니다.

IbatisPlugin 클래스에서 addIntegrationProcessor에서 넘긴 ClassLoader와 핸들링 대상 클래스가 이 CBP 클래스의
ClassLoader와 CtClass 파라메터로 넘어옵니다.

보면, CtMethod m4 = ct.getMethod("getInstance","(Ljava/lang/Class;)Lcom/ibatis/common/beans/ClassInfo;");
ct즉, CtClass 파라메터로 넘어온 ClassInfo 클래스에서 getInstance() 메서드를 얻어오고 그 메서드가 실행되기 전에
IbatisBORemover.getInstance().checkAndRealodBO(Class clazz, CLASS_INFO_MAP); 을 실행하라는 의미입니다.

$1은 ClassInfo의 getInstance()메서드의 파라메터인 Class clazz를 뜻합니다.

 CLASS_INFO_MAP은 ClassInfo 클래스가 private static으로 가지고 있는 캐시 역할을 하는 Map 인스턴스 입니다.
SqlMap.xml 에서 사용되는 resultMap 클래스들은 최초 로딩시에 이 Map에 캐싱되어 있습니다. 따라서, jrebel이 resultMap으로 사용되는 클래스를 다시 클래스로더에 로딩을 해도, Ibatis는 그것을 인식하지 못 합니다. 왜냐면, Ibatis의 캐싱 방식이 클래스
자체를 캐싱해 놓는 방식이 아니라, 클래스에 선언되어 있는 메서드, 필드등을 뽑아내어 저장해 두었다가 사용하는 방식이기
때문입니다.

즉, 클래스가 변경되면 그 클래스를 캐시에서 없애 버려서 다시 loading해서 메서드와 필드정보를 뽑아내도록 해줘야
Ibatis에서 새롭게 변경된 필드등을 인식 할 수 있게 됩니다.

다만, 그전에 그 클래스가 reload대상인지 확인하고 reload대상이면 ibatis에서 제거하기전 reload를 시키고 그것을
CLASS_INFO_MAP에서 제거해야합니다. 그래야 나중에 xml 파일이 변경 되어서 sqlMapClient를 다시 로드 하더라도
문제가 발생하지 않습니다.

Jrebel은 내부적으로 변경 된 클래스가 new를 통해 생성 될 때 reload하게 되어 있는 것 같습니다.

따라서, xml파일이 변경되어 sqlMapClient를 다시 초기화 할 때 resultMap클래스로 되어 있는 Domain 클래스가 Jrebel에 의해
리로드 되기 전 상태 class를 다시 로딩 해 갈 가능성이 있기 때문입니다.

이 Map 인스턴스가 private이기 때문에 외부에서 접근을 할 수 없기 때문에, 이렇게 내부적으로 바이트코드를 직접 write하여 AOP와 비슷하게  중간에 도메인클래스 캐싱을 삭제 할 메서드를 호출 하도록 해주는 방식입니다.
(http://www.csg.is.titech.ac.jp/~chiba/javassist/tutorial/tutorial.html)

그럼, 저 IbatisBORemover는 어떻게 로딩 된 클래스를 알고, 그것을 캐시에서 지우는가...



Jrebel에서 제공하는 ClassEventListener 인터페이스를 사용해서 구현 할 수 있습니다.
Jrebel은 위 인터페이스를 구현하고 자기 자신을 Reloader에 add하고 있습니다.이렇게 되면 Jrebel에서 재 로딩 한 클래스를
받아 볼 수 있게 됩니다. 그 클래스를 , Ibatis 캐시에서 강제로 지워버려서 Ibatis가 새로운 클래스 정보를 다시 읽어 오도록
하는 것이 이 CBP 클래스의 컨셉입니다.

그리고 reloader.checkAndReload(clazz) 메서드를 사용해서
Ibatis에서 호출하는 클래스가 리로드 대상이면 먼저 리로드를 시키고 그 다음에 Ibatis 캐시 맵에서 지워버리는 로직을
구현했습니다. 그러면, Ibatis에서는 자동적으로 Jrebel에 의해 리로드 된 클래스를 다시 읽어 갈 것 입니다.