我想创建一个动态代理,它可以将其方法委托给不同的实现(每个方法调用选择一个可能不同的对象)。我希望实现多态效果,就像某些代理方法调用另一个代理方法时,对象选择机制再次应用。
好吧,有足够的困惑,这是一个例子:
interface IService {
void a();
void b();
}
class HappyService implements IService {
public void a() {
System.out.println("Happy a");
b();
}
public void b() {
System.out.println("Happy b");
}
}
class SadService implements IService {
public void a() {
System.out.println("Sad a");
b();
}
public void b() {
System.out.println("Sad b");
}
}
现在,我想为IService
创建一个代理,它总是选择HappyService
来调用方法a()
和SadService
来调用方法b()
。以下是我首先想到的:
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke( final Object proxy, final Method method, final Object[] args ) throws Throwable {
Object impl;
if (method.getName().equals("a")) {
impl = new HappyService();
} else if (method.getName().equals("b")) {
impl = new SadService();
} else {
throw new IllegalArgumentException("Unsupported method: " + method.getName());
}
return method.invoke(impl, args);
}
};
IService service = (IService)Proxy.newProxyInstance( IService.class.getClassLoader(), new Class[]{ IService.class }, h );
service.a();
打印:
Happy a
Happy b
是的,那是因为b()
内a()
的调用对动态代理一无所知。
那么,我如何才能最好地实现目标?我想要的输出是:
Happy a
Sad b
我可能用另一个代理替换调用处理程序中的new HappyService()
,该代理只将方法a()
传递给HappyService
,并将所有其他方法重定向回原始代理。但也许有更好/更容易的解决方案?
答案 0 :(得分:3)
当调用者具有对代理的引用而不是“真实”实现时,代理只能用于拦截对象间调用。在此,a()
的实施都直接调用b()
,因此他们当然会在this
上调用它。代理人无法实现您想要做的事情。
但是,您可以使用AOP执行此操作,例如使用AspectJ和compile-time weaving(也可以使用aspectj-maven-plugin)或load-time weaving。粗略地说,您在a()
实现中的b()
和IService
方法的aspect上创建了带有切入点的calling sites,并建议执行,是用另一个替换原来的电话。未经测试的代码,但看起来像这样:
public aspect IServiceAspect {
private IService happy = new HappyService(); // Assuming these are stateless services
private IService sad = new SadService(); // that can be shared.
// The pointcut is inside an IService implementation, and is a call to the a() method
pointcut aCall(): within(IService+) && call(* IService.a());
pointcut bCall(): within(IService+) && call(* IService.b());
around(): aCall() && !within(HappyService) { // To avoid infinite recursion
return happy.a();
}
around(): bCall() && !within(SadService) {
return sad.b();
}
}
然后,您可以直接提供HappyService
或SadService
实施IService
,两者都已修改。您还可以创建与IService
的所有方法匹配的单个切入点,并根据方法名称动态路由(在建议中使用thisJoinPoint
),就像在您的示例中一样。
也可以使用annotations声明方面。