动态代理的思想来源于 代理模式
本片文章不讲述设计模式,需要学习设计模型知识请自行查阅资料.
被代理类
JDK动态代理要求被代理类只能是接口或者实现某接口的类。
此处定义被代理接口
public interface BeProxyInterface {
/**
* 声明被代理方法
* @param msg 参数
* @return
*/
Object call(String msg);
}
代理类
我更喜欢把代理类叫做 触发控制类,因为代理类必须要实现InvocationHandler
public class Advised implements InvocationHandler {
/**
* 触发被代理的方法
* @param proxy 代理对象
* @param method 当前触发的方法
* @param objects 当前触发方法的入参
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable {
System.out.println("-------触发了方法-------");
return null;
}
}
创建代理对象
Object proxy = Proxy.newProxyInstance(BeProxyInterface.class.getClassLoader(), new Class[] {BeProxyInterface.class}, new Advised());
BeProxyInterface proxyObj = (BeProxyInterface)proxy;
proxyObj.call("哈哈");
以上,给BeProxyInteface接口生成了一个代理对象。基本就是动态代理的使用.
不过你可能会说,以上示例只是打印了一行输出,没有实际用处.
没错,以上示例确实没什么实际用处,不过还是能扩展,使其能得到真正应用。
说到真正应用之前,先看看网上其他文章可能给出的一般用法。
一般用法是触发控制类持有被代理对象真实示例.并触发被代理对象的方法. 如下
public class Advised implements InvocationHandler {
private BeProxyInterface beProxyInterface;
@Override
public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable {
method.invoke(beProxyInterface, objects);
}
}
对应的创建代理类的修改, 需要指定。
以上示例是比较常见的示例。 实际上像spring的动态代理也是这种方式,BeProxyInterface对应的就是spring的bean(targetSource); InvocationHanderl关联的BeProxyInterface改为关联beanName。
模板配置使用
这里展示一种之前说的真实用法。就是把InvocationHander当成模板,被代理对象当成可变配置使用。
可能的使用场景
消息队列发送消息
一般的,往消息队列方式消息,需要指定消息对象,以及接收的队列就可以。发送方法是可以公用的。即连接队列发送消息,连接对象可以公用,需要提供消息体和队列名词即可。
- 定义接口,指定消息队列,并指定消息对象
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface RabbitMqProducer {
String exchange();
String routing();
}
public interface BeProxyInterface {
/**
* 声明被代理方法
* @param msg 参数
* @return
*/
@RabbitMqProducer(exchange = "exchange.test.topic", routing = "routing.test")
Object call(String msg);
}
- 实现发送消息模板
public class Advised implements InvocationHandler {
/**
* 触发被代理的方法
* @param proxy 代理对象
* @param method 代理对上当前触发的方法
* @param objects 当前触发方法的入参
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable {
System.out.println("-------触发了方法-------");
RabbitMqProducer rabbitMqProducer = method.getAnnotation(RabbitMqProducer.class);
if (rabbitMqProducer != null) {
String exchange = rabbitMqProducer.exchange();
String routing = rabbitMqProducer.routing();
System.out.println("根据exchange:" + exchange + ", routing:" + routing + ", 发送消息 :" + objects[0]);
}
return null;
}
}
- 发送消息
public static void main(String[] args) {
Object proxy = Proxy.newProxyInstance(BeProxyInterface.class.getClassLoader(), new Class[] {BeProxyInterface.class}, new Advised());
BeProxyInterface proxyObj = (BeProxyInterface)proxy;
proxyObj.call("哈哈");
}
输出结果
-------触发了方法-------
根据exchange:exchange.test.topic, routing:routing.test, 发送消息 :哈哈
以上,把触发控制类当模板来用。 要把消息发送到不通队列,则只需要写不同的BeProxyInterface接口即可。
总结
JDK动态代理,提供了运行时创建代理的能力。根据代理特性,可以扩展其用法,不仅仅是代理的任务上。因为代理的本身是生成对象。