什么是动态代理类,为什么我会使用它?

时间:2009-06-01 08:39:08

标签: java design-patterns dynamic-proxy

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

它们如何与字节码生成和反射相关?

任何推荐阅读?

5 个答案:

答案 0 :(得分:28)

  

动态代理类是一个实现列表的类   在运行时指定的接口,以便通过方法调用   该类实例上的一个接口将被编码和   通过统一接口发送到另一个对象。有可能   用于为接口列表创建类型安全的代理对象   无需预生成代理类。动态代理   类对需要提供的应用程序或库很有用   对出现的对象进行类型安全的反射调度   接口API。

Dynamic Proxy Classes

答案 1 :(得分:23)

我强烈推荐这个resource

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

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

答案 2 :(得分:16)

我想出了一个动态代理的有趣用法。

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

所以我编写了一个 LoadSheddingProxy ,它接受两个委托 - 一个是'普通'服务的远程impl(在JNDI查找之后)。另一个对象是'虚拟'卸载impl。每个方法调用都有简单的逻辑,可以捕获超时并在重试之前转移到虚拟对象一段时间。这是我如何使用它:

// 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;
}
}

答案 3 :(得分:8)

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

Sun有a tutorial关于使用Proxy类的问题。 Google helps, too.

答案 4 :(得分:5)

一个用例是hibernate - 它为您提供了实现模型类接口的对象,但在getter和setter下面存在与db相关的代码。即你使用它们好像它们只是简单的POJO,但实际上有很多东西在掩护之下。

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

您应该查看cglib库以获取更多信息。