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

77

什么情况下使用动态代理?

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

有没有推荐的阅读材料?


这里有一个很好的解释:https://opencredo.com/dynamic-proxies-java/ - Bala
5个回答

41

我强烈推荐这个资源

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

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


链接失效。 - bvdb
感谢提供链接...确实非常有帮助。 - worked
1
我认为这是最好的答案。其他答案包含超长且难以理解的句子,我一辈子也无法理解。这个链接则是一步一步地帮助了我。 - SWIIWII
使用wayback机器来查看链接,哇,我从来没有想到过这一点。我以后一定会在我的回答中这样做,谢谢那位友善的用户编辑。链接中的内容也很棒,谢谢! - undefined

31
一个动态代理类是一种在运行时实现了指定接口列表的类,这样对于该类实例上的接口方法调用将被编码并通过统一接口分派到另一个对象。它可用于为接口列表创建类型安全的代理对象,而无需预先生成代理类。动态代理类对于需要提供接口API的类型安全反射调度对象的应用程序或库非常有用。动态代理类

很好的回答,一个很好的例子就是Spring Remoting,它具有HTTP、RMI、EJB和JMS的内置代理功能。 - Robin

17

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

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

因此,我编写了一个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;
}
}

8

6
一个使用案例是Hibernate - 它为您提供实现模型类接口的对象,但在getter和setter下面有与数据库相关的代码。也就是说,您可以将它们用作简单的POJO,但实际上,在表面下发生了很多事情。
例如- 您只需调用延迟加载属性的getter,但实际上该属性(可能是整个大对象结构)从数据库中获取。
您应该查看cglib库以获取更多信息。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接