什么是 Dynamic Proxy 类? 我为什么要使用它?

使用动态代理的用例是什么?

它们与字节码生成和反射有什么关系?

有什么推荐读物吗?

59526 次浏览

java.lang.reflect.Proxy允许您通过在 InvocationHandler中处理方法调用来动态实现接口。它被认为是 Java 反射工具的一部分,但与字节码生成无关。

Sun 有关于代理类的使用的 教程

一个用例是 hibernate ——它提供了实现模型类接口的对象,但是在 getters 和 setter 之下存在与 db 相关的代码。也就是说,你使用它们就好像它们只是简单的 POJO 一样,但实际上有很多事情是在掩盖之下进行的。

例如,您只是调用一个延迟加载属性的 getter,但实际上属性(可能是整个大对象结构)是从数据库中获取的。

您应该查看 Cglib库了解更多信息。

动态代理类动态代理类是一个实现 在运行时指定的接口,以便通过 类实例上的一个接口将被编码,并且 通过一个统一的接口分配到另一个对象。它可以 用于为接口列表创建类型安全的代理对象 不需要预生成代理类 类对于需要提供 类型安全的调用反射分派 接口 API。

动态代理类

我刚想到一个动态代理的有趣用法。

我们遇到了一些麻烦,一个非关键服务与另一个依赖服务相结合,并希望探索当依赖服务变得不可用时容错的方法。

因此,我编写了一个 LoadSheddingProxy,它接受两个委托——一个是“普通”服务的远程隐含(在 JNDI 查找之后)。另一个对象是一个“虚拟的”负载削减意义。围绕每个方法调用都有一个简单的逻辑,它捕获超时并在重试之前转移到虚拟机一段时间。我是这样使用它的:

// This is part of your ServiceLocator class
public static MyServiceInterface getMyService() throws Exception
{
MyServiceInterface loadShedder = new MyServiceInterface() {
public Thingy[] getThingys(Stuff[] whatever) throws Exception {
return new Thingy[0];
}
//... etc - basically a dummy version of your service goes here
}
Context ctx = JndiUtil.getJNDIContext(MY_CLUSTER);
try {
MyServiceInterface impl = ((MyServiceHome) PortableRemoteObject.narrow(
ctx.lookup(MyServiceHome.JNDI_NAME),
MyServiceHome.class)).create();
// Here's where the proxy comes in
return (MyService) Proxy.newProxyInstance(
MyServiceHome.class.getClassLoader(),
new Class[] { MyServiceInterface.class },
new LoadSheddingProxy(MyServiceHome.JNDI_NAME, impl, loadShedder, 60000));  // 10 minute retry
} catch (RemoteException e) {    // If we can't even look up the service we can fail by shedding load too
logger.warn("Shedding load");
return loadShedder;
} finally {
if (ctx != null) {
ctx.close();
}
}
}

代理人是这样的:

public class LoadSheddingProxy implements InvocationHandler {


static final Logger logger = ApplicationLogger.getLogger(LoadSheddingProxy.class);


Object primaryImpl, loadDumpingImpl;
long retry;
String serviceName;
// map is static because we may have many instances of a proxy around repeatedly looked-up remote objects
static final Map<String, Long> servicesLastTimedOut = new HashMap<String, Long>();


public LoadSheddingProxy(String serviceName, Object primaryImpl, Object loadDumpingImpl, long retry)
{
this.serviceName = serviceName;
this.primaryImpl = primaryImpl;
this.loadDumpingImpl = loadDumpingImpl;
this.retry = retry;
}


public Object invoke(Object obj, Method m, Object[] args) throws Throwable
{
try
{
if (!servicesLastTimedOut.containsKey(serviceName) || timeToRetry()) {
Object ret = m.invoke(primaryImpl, args);
servicesLastTimedOut.remove(serviceName);
return ret;
}
return m.invoke(loadDumpingImpl, args);
}
catch (InvocationTargetException e)
{
Throwable targetException = e.getTargetException();


// DETECT TIMEOUT HERE SOMEHOW - not sure this is the way to do it???
if (targetException instanceof RemoteException) {
servicesLastTimedOut.put(serviceName, Long.valueOf(System.currentTimeMillis()));
}
throw targetException;
}
}


private boolean timeToRetry() {
long lastFailedAt = servicesLastTimedOut.get(serviceName).longValue();
return (System.currentTimeMillis() - lastFailedAt) > retry;
}
}

我强烈推荐这款 资源

首先,您必须了解代理模式的用例。请记住,代理的主要目的是控制对 而不是增强目标对象的功能 访问控制包括同步、身份验证、远程访问(RPC)、延迟实例化(Hibernate、 Mybatis)、 AOP (事务)。

与静态代理不同,动态代理生成的字节码在运行时需要 Java 反射。使用动态方法,您不需要创建代理类,这可以带来更多的便利。