最近更新light-dao遇到的。
需要在一個接口中增加default方法辰晕,同時需要對非default方法重寫蛤迎。
大概類似這樣:
public interface BaseDao {
default String getName() {
return "name";
}
@Select("select * from ad")
List<Object> getAll();
}
我們重寫的Handler類似這樣:
public class DaoInvocationHandler implements InvocationHandler {
private Map<Method, SqlExecutor> sqlExecutorMap = new ConcurrentHashMap<Method, SqlExecutor>();
private DataSourceHolder dataSourceHolder;
public DaoInvocationHandler(DataSourceHolder dataSourceHolder) {
this.dataSourceHolder = dataSourceHolder;
}
//對應(yīng)于Dao接口里的函數(shù)
//將每一個函數(shù)映射為一個SqlExecutor
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (!sqlExecutorMap.containsKey(method)) {
synchronized (method) {
if (!sqlExecutorMap.containsKey(method)) {
SqlExecutor sqlExecutor = new SqlExecutor.Builder(method, dataSourceHolder.getLightTemplate()).build();
sqlExecutorMap.put(method, sqlExecutor);
}
}
}
return sqlExecutorMap.get(method).execute(args);
}
}
我們需要對default方法做特殊處理,搜索了一下stackoverflow
http://stackoverflow.com/questions/22614746/how-do-i-invoke-java-8-default-methods-refletively
里面提供了一種解決方式:
Object result = MethodHandles.lookup()
.in(method.getDeclaringClass())
.unreflectSpecial(method,method.getDeclaringClass())
.bindTo(target)
.invokeWithArguments();
但是在使用時含友,報錯如下:
Caused by: java.lang.IllegalAccessException: no private access for invokespecial
又嘗試了幾次替裆,發(fā)現(xiàn)這種方式的改進(jìn)版:
Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
.getDeclaredConstructor(Class.class, int.class);
constructor.setAccessible(true);
Class<?> declaringClass = method.getDeclaringClass();
int allModes = MethodHandles.Lookup.PUBLIC | MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED | MethodHandles.Lookup.PACKAGE;
return constructor.newInstance(declaringClass, allModes)
.unreflectSpecial(method, declaringClass)
.bindTo(proxy)
.invokeWithArguments(args);
通過設(shè)置constructor.setAccessible(true);來解決access權(quán)限問題校辩。目前來看,問題基本解決辆童。最終的invocationHandler代碼如下:
public class DaoInvocationHandler implements InvocationHandler {
private Map<Method, SqlExecutor> sqlExecutorMap = new ConcurrentHashMap<Method, SqlExecutor>();
private DataSourceHolder dataSourceHolder;
public DaoInvocationHandler(DataSourceHolder dataSourceHolder) {
this.dataSourceHolder = dataSourceHolder;
}
//對應(yīng)于Dao接口里的函數(shù)
//將每一個函數(shù)映射為一個SqlExecutor
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//default方法不重寫
if (method.isDefault()) {
Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
.getDeclaredConstructor(Class.class, int.class);
constructor.setAccessible(true);
Class<?> declaringClass = method.getDeclaringClass();
int allModes = MethodHandles.Lookup.PUBLIC | MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED | MethodHandles.Lookup.PACKAGE;
return constructor.newInstance(declaringClass, allModes)
.unreflectSpecial(method, declaringClass)
.bindTo(proxy)
.invokeWithArguments(args);
} else if (!sqlExecutorMap.containsKey(method)) {
synchronized (method) {
if (!sqlExecutorMap.containsKey(method)) {
SqlExecutor sqlExecutor = new SqlExecutor.Builder(method, dataSourceHolder.getLightTemplate()).build();
sqlExecutorMap.put(method, sqlExecutor);
}
}
}
return sqlExecutorMap.get(method).execute(args);
}
}
雖然解決了問題宜咒,但還是覺得java 8應(yīng)該在method里添加針對interface default方法的直接調(diào)用方式,這樣繞一大圈的解決方式顯然不夠優(yōu)雅把鉴。
只能期待reflect api下個版本的修改了故黑。