Jdk动态代理如何处理java8接口的default方法:
MethodHandlesUtil
工具类参考我的另一篇博客: java8中实现调用对象的父类方法
我们在使用Jdk的动态代理时,一定会写一个java.lang.reflect.InvocationHandler
实现类,用来处理编写后续的处理逻辑,或者根据方法名写相应的逻辑代码。该接口方法invoke(Object proxy, Method method, Object[] args)
有三个参数,
proxy
是JDK动态生成的接口实现类Class实例化的对象。
method
是当前正在被调用的接口方法,
args
则是方法参数。
对于JDK的动态代理来说,我们是不能在InvocationHandler中直接反射调用proxy对象方法的.因为其方法实现就是通过InvocationHandler实现,会造成无限死循环调用InvocationHandler中的invoke方法. 那么如果接口中含有java8 中default关键字定义的默认方法,是否存在同样的问题. 下面看下实验代码:
接口类如下:
getAddress是一个普通的接口方法, getUserName是个default方法, 实现类即使不重写该方法也是能直接被调用的.
public interface UserMapper {
public static final Logger logger = LoggerFactory.getLogger(UserMapper.class);
String getAddress(String userId);
default String getUserName(String userName, Boolean yes, int age) {
logger.info("bruce");
return "bruce " + userName + " " + yes + " " + age;
}
}
实现动态代理以及测试类
public class MainTest {
static class MyInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(proxy, args);
}
}
public static void main(String[] args) {
UserMapper userMapper = (UserMapper) Proxy.newProxyInstance(UserServiceInvoke.class.getClassLoader(),
new Class[]{
UserMapper.class}, new MyInvocationHandler());
//String address = userMapper.getAddress("12345");
//System.out.println(address);
//调用含有default关键字的方法
String userName = userMapper.getUserName("aa", true, 18);
System.out.println(userName);
}
}
运行结果: 实际上跟普通接口方法一样,都是不能在invoke方法中直接调用的.
那么JDK的动态代理,如何能够调用接口中定义的default方法呢?
那就是使用java7开始提供的方法句柄MethodHandle,调用对象的父类方法,参考我的另一篇博客: java8中实现调用对象的父类方法
实现代码如下:
public class MyInvocationHandler implements InvocationHandler {
private static final Logger logger = LoggerFactory.getLogger(MyInvocationHandler.class);
private ConcurrentHashMap<Method, MethodHandle> methodHandleMap = new ConcurrentHashMap<>();
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.isDefault()) {
MethodHandle defaultMethodHandle = methodHandleMap.computeIfAbsent(method, key -> {
MethodHandle methodHandle = MethodHandlesUtil.getSpecialMethodHandle(method);
return methodHandle.bindTo(proxy);
});
logger.info("default 接口方法:{}", method.getName());
return defaultMethodHandle.invokeWithArguments(args);
}
logger.info("普通接口方法:{}", method.getName());
//做其他逻辑处理,
//模拟数据
return "这是一个普通接口方法:" + method.getName();
}
}
public class MainTest {
public static void main(String[] args) {
UserMapper userMapper = (UserMapper) Proxy.newProxyInstance(UserServiceInvoke.class.getClassLoader(),
new Class[]{
UserMapper.class}, new MyInvocationHandler());
String address = userMapper.getAddress("12345");
System.out.println(address);
System.out.println();
String userName = userMapper.getUserName("aa", true, 18);
System.out.println(userName);
}
}