一、为什么需要动态代理
1.为了解决静态代理一次只能服务一种类型的目标对象的问题
2.通过使用动态代理,可以在运行时动态获取一个持有目标对象,同时实现代理接口的代理对象实例,最后统一调用代理对象的 invoke() 方法,在此可以统一为所有代理接口方法注入相同的拓展逻辑
二、相关角色
接口对象:目标对象的抽象接口
目标对象:真实目标对象
代理对象:动态生成目标对象的代理对象
三、使用案例
接口对象:
/**
* 目标对象的抽象接口
*/
public interface IDinner {
/**
* 定义目标对象的接口方法
*/
void dinner();
}
目标对象:
/**
* 目标对象,被代理的对象,实现了目标对象的抽象接口
*/
public class DinnerConsumer implements IDinner {
@Override
public void dinner() {
System.out.println("只管吃大餐");
}
}
调用处理器类:
/**
* 调用处理器类,生成动态代理对象
*/
public class DinnerHandler implements InvocationHandler {
/**
* 声明目标对象
* 作用:绑定关系,关联到哪个接口(与具体的实现类绑定)的哪个方法将(具体的实现类的方法)被调用,执行 invoke() 方法
*/
private Object target;
// 构造代理对象时传入目标对象
public DinnerHandler(Object target) {
this.target = target;
}
public Object newProxyInstance(Object target) {
/**
* 参数一:指定为和目标对象同一个类加载器
* 参数二:指定为目标对象实现的接口,代理对象默认实现了该接口,这样就能调用接口中的方法
* 参数三:指定 InvocationHandler 对象,即动态代理在调用方法时会关联到哪个 InvocationHandler 对象
* 最终生成动态代理类实例
*/
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 调用目标方法前的处理
System.out.println("动态代理,有人请客");
// 调用目标对象的方法
method.invoke(target, args);
// 调用目标方法后的处理
System.out.println("动态代理,有人结账");
return null;
}
}
测试类:
public class DinnerTest {
public static void main(String[] args) {
//创建目标对象
DinnerConsumer dinnerProvider = new DinnerConsumer();
//创建代理对象
InvocationHandler dinnerHandler = new DinnerHandler(dinnerProvider);
//动态获取接口对象对应的代理对象实例
IDinner iDinner = (IDinner) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class<?>[] { IDinner.class }, dinnerHandler);
//实际执行了 invoke() 方法,invoke() 方法通过反射机制调用真实目标对象的方法
iDinner.dinner();
}
}