【问题标题】:What are Dynamic Proxy classes and why would I use one?什么是动态代理类,我为什么要使用一个?
【发布时间】:2010-10-30 08:36:12
【问题描述】:

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

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

有什么推荐的读物吗?

【问题讨论】:

标签: java design-patterns dynamic-proxy


【解决方案1】:

我强烈推荐这个resource

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

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

【讨论】:

  • 链接坏了
  • 感谢您的链接...确实很有帮助。
  • 我认为这是最好的答案。其他答案包含我一生中永远无法理解的超长难题。该链接只是一步一步地帮助了我。
【解决方案2】:

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

Sun 有a tutorial 关于代理类的使用。 Google helps, too.

【讨论】:

    【解决方案3】:

    一个动态代理类是一个实现一个列表的类 在运行时指定的接口,使得方法调用通过 类实例上的一个接口将被编码,并且 通过统一的接口分派给另一个对象。有可能 用于为接口列表创建类型安全的代理对象 无需预先生成代理类。动态代理 类对需要提供的应用程序或库很有用 对当前对象的调用进行类型安全的反射调度 接口 API。

    Dynamic Proxy Classes

    【讨论】:

    • 很好的答案,Spring Remoting 就是一个很好的例子,它内置了 HTTP、RMI、EJB 和 JMS 的代理功能。
    【解决方案4】:

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

    我们在一个非关键服务与另一个依赖服务耦合时遇到了一些麻烦,我们想探索在该依赖服务不可用时容错的方法。

    所以我编写了一个 LoadSheddingProxy,它需要两个委托 - 一个是“普通”服务的远程实现(在 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;
    }
    }
    

    【讨论】:

      【解决方案5】:

      一个用例是休眠 - 它为您提供实现模型类接口的对象,但在 getter 和 setter 下存在与 db 相关的代码。 IE。你可以把它们当作简单的 POJO 来使用,但实际上有很多事情在隐藏。

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-02-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-01-10
        • 2011-02-21
        相关资源
        最近更新 更多