使用动态代理的用例是什么?
它们如何与字节码生成和反射相关?
有推荐的阅读吗?
甲动态代理类是实现在运行时指定的接口列表的类,使得通过对类的一个实例的一个接口方法调用将被编码,并通过一个统一的接口分派给另一个对象.它可用于为接口列表创建类型安全的代理对象,而无需预生成代理类.动态代理类对于需要在呈现接口API的对象上提供类型安全反射调度调用的应用程序或库非常有用.
动态代理类
我强烈推荐这个资源.
首先,您必须了解代理模式用例.请记住,代理的主要目的是控制对目标对象的访问,而不是增强目标对象的功能.访问控制包括同步,身份验证,远程访问(RPC),延迟实例化(Hibernate,Mybatis),AOP(事务).
与静态代理相比,动态代理生成需要在运行时进行Java反射的字节码.使用动态方法,您无需创建代理类,这可以带来更多便利.
我想出了一个动态代理的有趣用法.
我们遇到了一些非关键服务的问题,该服务与另一个依赖服务相结合,并希望在该相关服务变得不可用时探索容错的方法.
所以我编写了一个带有两个委托的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 MapservicesLastTimedOut = new HashMap (); 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; } }
该类java.lang.reflect.Proxy
允许您通过处理方法调用来动态实现接口InvocationHandler
.它被认为是Java反射工具的一部分,但与字节码生成无关.
Sun有一个关于使用Proxy类的教程.谷歌也有帮助.
一个用例是hibernate - 它为您提供了实现模型类接口的对象,但在getter和setter下面存在与db相关的代码.也就是说,你使用它们好像它们只是简单的POJO,但实际上有很多东西在掩护之下.
例如 - 你只是调用一个延迟加载属性的getter,但实际上属性(可能是整个大对象结构)从数据库中获取.
您应该检查cglib库以获取更多信息.