最近在寫學校案子用到hibernate,正巧以前打工的地方學習到一個方法,
在javaworld@tw也有人提到過,因此整理一下.
Hibernate + Struts 學習筆記提到過的使用dynamic proxy的做法,
來避免巢狀呼叫,也可以讓service delegate不會跟hibernate綁的死死的.
首先dynamic proxy會利用到java.lang.reflect.Proxy跟java.lang.reflect.InvocationHandler. 直接看以下程式碼
HogeIF.java
1
2
3
public interface HogeIF {
public
void
printMessage(String message);
}

Hoge.java
1
2
3
4
5
public class Hoge implements
HogeIF{
public void printMessage(String message) {

System.out.println(message);
}
}

執行時必須寫成
1
2
HogeIF hoge = new Hoge();
hoge.printMessage("hogehoge");

如果想對此方法做點前後置處理,那就寫個proxy
HogeProxy.java
1
2
3
4
5
6
7
8

9
10
11
public class HogeProxy 
implements
HogeIF{
private HogeIF hoge;
public HogeProxy(HogeIF hoge)
{
this.hoge = hoge;
}
public void printMessage(String message)
{
System.out.println("before");
hoge.printMessage(message);
System.out.println("after"
);
}
}

這樣就會變成
1
2
3
before
hogehoge
after

如果每個method都需要加工,需要加工的class都implement不同interface,或者每個class有不同method,那麼........就要寫非常多的proxy來用... 在這邊改個方法使用dynamic proxy...
DynamicHogeProxy.java
1
2
3
4
5
6
7
8

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import
java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method
;
public class DynamicHogeProxy implements InvocationHandler
{

private Object obj;
public static Object newInstance(Object obj)
{
return java.lang.reflect.Proxy.newProxyInstance(obj.getClass()
.getClassLoader(), obj.getClass().getInterfaces(),

new DynamicHogeProxy(obj));
}
private DynamicHogeProxy(Object obj)
{

this.obj = obj;
}
public Object invoke(Object proxy, Method m, Object[] args)

throws
Throwable {
Object result;
try {
System.out.println(
"before"
);
result = m.invoke(obj, args);
System.out.println("after");
return result;

} catch (InvocationTargetException e) {
throw
e.getTargetException();
}finally {
}

}

}

執行時寫成
1
2
HogeIF hoge = (HogeIF)DynamicHogeProxy.newInstance(new Hoge());
hoge.printMessage("hogehoge");

這樣就可以達到跟上一個例子一樣的效果了. 回到本篇正題,最近用hibernate希望他自動關閉session,但是又不希望在dao的每個method都開開關關,也不希望service delegate裡面去做開關,因此我把每個service delegate都使用dynamic proxy來invoke...這樣就可以做到整個呼叫過程,session的開關只有一次,也就不用擔心巢狀呼叫了. 因此我在每個dao的每個操作的method的開頭加上ThreadLocalSession.currentSession(); 然後每個service delegate都使用dynamic proxy 去invoke. dynamic proxy的內容如下

            
            1
2
3
4
5
6
7
8

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

import
java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method
;
import java.lang.reflect.Proxy;
 
public class DynamicHogeProxy
implements
InvocationHandler{
private Object obj;
private DynamicHogeProxy(Object obj)
{
this.obj = obj;
}
public static Object newInstance(Object obj)
{
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
new
DynamicHogeProxy(
obj));
}
public Object invoke(Object proxy, Method method, Object[] args)

throws Throwable {
Object result = null;
if
(obj != null) {
try {
result =
method.invoke(obj, args);
} catch (InvocationTargetException e) {

throw
e.getCause();
} finally {
ThreadLocalSession.closeSession();

}
}
return result;
}
}


這種寫法缺點是一定得撰寫interface才可以,不然會發生ClassCastException.
1
2
HogeIF hoge = (HogeIF)DynamicHogeProxy.newInstance(new Hoge()); //ok!!!
Hoge hoge = (Hoge)DynamicHogeProxy.newInstance(
new
Hoge()); //ClassCastException

而且一開始產生proxy時效能會稍微差一點. 另外,要達到上述功能現在也可以用AspectJ來做...恩...