更新时间:2022-05-13 09:31:35 来源:极悦 浏览589次
这篇文章是关于Java 的动态代理——它是我们在语言中可用的主要代理机制之一。
简单地说,代理是通过自己的设施(通常是真实方法)传递函数调用的前端或包装器——可能会添加一些功能。
动态代理允许使用一种方法的单个类为具有任意数量方法的任意类的多个方法调用提供服务。动态代理可以被认为是一种外观,但它可以伪装成任何接口的实现。在幕后,它将所有方法调用路由到单个处理程序——invoke ()方法。
虽然它不是用于日常编程任务的工具,但动态代理对于框架编写者来说非常有用。它也可以用于那些直到运行时才知道具体类实现的情况。
此功能内置在标准 JDK 中,因此不需要额外的依赖项。
让我们构建一个简单的代理,它实际上不做任何事情,除了打印请求调用的方法并返回一个硬编码的数字。
首先,我们需要创建java.lang.reflect.InvocationHandler的子类型:
public class DynamicInvocationHandler implements InvocationHandler {
private static Logger LOGGER = LoggerFactory.getLogger(
DynamicInvocationHandler.class);
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
LOGGER.info("Invoked method: {}", method.getName());
return 42;
}
}
在这里,我们定义了一个简单的代理,它记录调用了哪个方法并返回 42。
我们刚刚定义的调用处理程序所服务的代理实例是通过对java.lang.reflect.Proxy类的工厂方法模式调用创建的:
Map proxyInstance = (Map) Proxy.newProxyInstance(
DynamicProxyTest.class.getClassLoader(),
new Class[] { Map.class },
new DynamicInvocationHandler());
一旦我们有了代理实例,我们就可以正常调用它的接口方法:
proxyInstance.put("hello", "world");
正如预期的那样,在日志文件中会打印出有关调用put()方法的消息。
由于InvocationHandler是一个函数式接口,因此可以使用 lambda 表达式内联定义处理程序:
Map proxyInstance = (Map) Proxy.newProxyInstance(
DynamicProxyTest.class.getClassLoader(),
new Class[] { Map.class },
(proxy, method, methodArgs) -> {
if (method.getName().equals("get")) {
return 42;
} else {
throw new UnsupportedOperationException(
"Unsupported method: " + method.getName());
}
});
在这里,我们定义了一个处理程序,它为所有 get 操作返回 42,并为其他所有操作抛出UnsupportedOperationException。
它以完全相同的方式调用:
(int) proxyInstance.get("hello"); // 42
proxyInstance.put("hello", "world"); // exception
让我们来看看动态代理的一种潜在的真实世界场景。
假设我们想记录我们的函数执行需要多长时间。为此,我们首先定义一个能够包装“真实”对象、跟踪时间信息和反射调用的处理程序:
public class TimingDynamicInvocationHandler implements InvocationHandler {
private static Logger LOGGER = LoggerFactory.getLogger(
TimingDynamicInvocationHandler.class);
private final Map<String, Method> methods = new HashMap<>();
private Object target;
public TimingDynamicInvocationHandler(Object target) {
this.target = target;
for(Method method: target.getClass().getDeclaredMethods()) {
this.methods.put(method.getName(), method);
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
long start = System.nanoTime();
Object result = methods.get(method.getName()).invoke(target, args);
long elapsed = System.nanoTime() - start;
LOGGER.info("Executing {} finished in {} ns", method.getName(),
elapsed);
return result;
}
}
随后,此代理可用于各种对象类型:
Map mapProxyInstance = (Map) Proxy.newProxyInstance(
DynamicProxyTest.class.getClassLoader(), new Class[] { Map.class },
new TimingDynamicInvocationHandler(new HashMap<>()));
mapProxyInstance.put("hello", "world");
CharSequence csProxyInstance = (CharSequence) Proxy.newProxyInstance(
DynamicProxyTest.class.getClassLoader(),
new Class[] { CharSequence.class },
new TimingDynamicInvocationHandler("Hello World"));
csProxyInstance.length()
在这里,我们代理了一个地图和一个字符序列(字符串)。
代理方法的调用将委托给被包装的对象并产生日志语句:
Executing put finished in 19153 ns
Executing get finished in 8891 ns
Executing charAt finished in 11152 ns
Executing length finished in 10087 ns
以上就是关于“什么是动态代理”的介绍,大家如果对此比较感兴趣,想了解更多相关知识,可以关注一下极悦的Java极悦在线学习,里面的课程内容细致全面,很适合没有基础的小伙伴学习,希望对大家能够有所帮助哦。
0基础 0学费 15天面授
Java就业班有基础 直达就业
业余时间 高薪转行
Java在职加薪班工作1~3年,加薪神器
工作3~5年,晋升架构
提交申请后,顾问老师会电话与您沟通安排学习