写点什么

java8 通过反射执行接口的 default 方法

作者:java易二三
  • 2023-08-12
    湖南
  • 本文字数:1945 字

    阅读完需:约 6 分钟

最近更新 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;}
//对应于Dao接口里的函数//将每一个函数映射为一个SqlExecutorpublic 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 又尝试了几次,发现这种方式的改进版:


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);
复制代码


通过设置 constructor.setAccessible(true);来解决 access 权限问题。目前来看,问题基本解决。最终的 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;}
//对应于Dao接口里的函数//将每一个函数映射为一个SqlExecutorpublic 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 应该在 method 里添加针对 interface default 方法的直接调用方式,这样绕一大圈的解决方式显然不够优雅。只能期待 reflect api 下个版本的修改了。

用户头像

java易二三

关注

还未添加个人签名 2021-11-23 加入

还未添加个人简介

评论

发布
暂无评论
java8 通过反射执行接口的default方法_Java_java易二三_InfoQ写作社区