什么情况下使用动态代理?
它们与字节码生成和反射有什么关系?
有没有推荐的阅读材料?
什么情况下使用动态代理?
它们与字节码生成和反射有什么关系?
有没有推荐的阅读材料?
我强烈推荐这个资源。
首先,你必须理解代理模式的用途。请记住,代理的主要目的是控制对目标对象的访问,而不是增强目标对象的功能。访问控制包括同步、身份验证、远程访问(RPC)、懒加载(Hibernate、Mybatis)和AOP(事务)。
与静态代理相比,动态代理生成的字节码需要在运行时使用Java反射。使用动态方法,您无需创建代理类,这可能更加方便。
我刚想到一个动态代理的有趣用途。
我们遇到了一些问题,一个非关键服务与另一个依赖服务相耦合,希望探索在该依赖服务不可用时实现容错的方法。
因此,我编写了一个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;
}
}
java.lang.reflect.Proxy
允许您通过在InvocationHandler
中处理方法调用来动态实现接口。它被认为是Java反射机制的一部分,但与字节码生成无关。