Java 什么是动态代理类,我为什么要使用一个?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/933993/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What are Dynamic Proxy classes and why would I use one?
提问by cwash
What is a use case for using a dynamic proxy?
使用动态代理的用例是什么?
How do they relate to bytecode generation and reflection?
它们与字节码生成和反射有何关系?
Any recommended reading?
有什么推荐的读物吗?
采纳答案by xiaojieaa
I highly recommend this resource.
我强烈推荐这个资源。
First of all, you must understand what the proxy pattern use case. Remember that the main intent of a proxy is to control access to the target object, rather than to enhance the functionality of the target object. The access control includes synchronization, authentication, remote access (RPC), lazy instantiation (Hibernate, Mybatis), AOP (transaction).
首先,您必须了解代理模式的用例。请记住,代理的主要目的是控制对目标对象的访问,而不是增强目标对象的功能。访问控制包括同步、认证、远程访问(RPC)、延迟实例化(Hibernate、Mybatis)、AOP(事务)。
In contrast with static proxy, the dynamic proxy generates bytecode which requires Java reflection at runtime. With the dynamic approach you don't need to create the proxy class, which can lead to more convenience.
与静态代理相反,动态代理生成字节码,在运行时需要 Java 反射。使用动态方法,您不需要创建代理类,这会带来更多便利。
回答by Michael Borgwardt
The class java.lang.reflect.Proxy
allows you to implement interfaces dynamically by handling method calls in an InvocationHandler
. It is considered part of Java's reflection facility, but has nothing to do with bytecode generation.
该类java.lang.reflect.Proxy
允许您通过处理InvocationHandler
. 它被认为是 Java 反射功能的一部分,但与字节码生成无关。
Sun has a tutorialabout the use of the Proxy class. Google helps, too.
回答by miceuz
One use case is hibernate - it gives you objects implementing your model classes interface but under getters and setters there resides db related code. I.e. you use them as if they are just simple POJO, but actually there is much going on under cover.
一个用例是休眠 - 它为您提供实现模型类接口的对象,但在 getter 和 setter 下存在与数据库相关的代码。即您使用它们就好像它们只是简单的 POJO 一样,但实际上有很多隐藏内容。
For example - you just call a getter of lazily loaded property, but really the property (probably whole big object structure) gets fetched from the database.
例如 - 您只需调用延迟加载属性的 getter,但实际上该属性(可能是整个大对象结构)是从数据库中获取的。
You should check cgliblibrary for more info.
您应该检查cglib库以获取更多信息。
回答by miceuz
A dynamic proxy classis a class that implements a list of interfaces specified at runtime such that a method invocation through one of the interfaces on an instance of the class will be encoded and dispatched to another object through a uniform interface. It can be used to create a type-safe proxy object for a list of interfaces without requiring pre-generation of the proxy class. Dynamic proxy classes are useful to an application or library that needs to provide type-safe reflective dispatch of invocations on objects that present interface APIs.
甲动态代理类是一类,它实现通过对类的一个实例的一个接口方法调用将被编码,并通过一个统一的接口分派给另一个对象的在运行时,例如指定的接口的列表。它可用于为接口列表创建类型安全的代理对象,而无需预先生成代理类。动态代理类对于需要对呈现接口 API 的对象提供类型安全的调用反射调度的应用程序或库很有用。
回答by Jim P
I just came up with an interesting use for a dynamic proxy.
我刚刚想出了一个有趣的动态代理用途。
We were having some trouble a non-critical service that is coupled with another dependant service and wanted to explore ways of being fault-tolerant when that dependant service becomes unavailable.
我们在与另一个依赖服务耦合的非关键服务中遇到了一些麻烦,并希望探索在该依赖服务不可用时进行容错的方法。
So I wrote a LoadSheddingProxythat takes two delegates - one is the remote impl for the 'normal' service (after the JNDI lookup). The other object is a 'dummy' load-shedding impl. There is simple logic surrounding each method invoke that catches timeouts and diverts to the dummy for a certain length of time before retrying. Here's how I use it:
所以我写了一个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();
}
}
}
And here's the proxy:
这是代理:
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;
}
}