一、什么是代理
代理是基本的设计模式之一,它是你为了提供额外的或不同的操作,而插入的用来代替“实际”对象的对象。这些操作通常涉及与“实际”对象的通信,因此代理通常充当着中间人的角色。代理这种设计模式是通过不直接访问被代理对象的方式,而访问被代理对象的方法。
二、什么情况下使用代理
(1)设计模式中有一个设计原则是开闭原则,是说对修改关闭对扩展开放,我们在工作中有时会接手很多前人的代码,里面代码逻辑让人摸不着头脑(sometimes the code is really like shit),这时就很难去下手修改代码,那么这时我们就可以通过代理对类进行增强。
(2)我们在使用RPC框架的时候,框架本身并不能提前知道各个业务方要调用哪些接口的哪些方法 。那么这个时候,就可用通过动态代理的方式来建立一个中间人给客户端使用,也方便框架进行搭建逻辑,某种程度上也是客户端代码和框架松耦合的一种表现。
(3)Spring的AOP机制就是采用动态代理的机制来实现切面编程。
三、动态代理的构成
- 接口类
- 实现接口类的目标类
- 实现了InvocationHandler接口的代理类
四、动态代理关键技术
1、InvocationHandler接口
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable{}
invoke方法中参数:
- proxy:指代我们所代理的那个真实对象
- method:指代的是我们所要调用真实对象的某个方法的Method对象
- args:指代的是调用真实对象某个方法时接受的参数
2、Proxy类
Proxy类的作用是动态创建一个代理对象的类,关键方法newProxyInstance:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
newProxyInstance方法中的参数:
- loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载;
- interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了;
- h:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
五、动态代理简单实现
1、接口类TargetInterface
public interface TargetInterface {
public void method1();
public String method2();
public int method3(int x);
}
2、目标类Target 实现TargetInterface 接口
public class Target implements TargetInterface{
@Override
public void method1() {
System.out.println("method1 running...");
}
@Override
public String method2() {
System.out.println("method2 running...");
return "method2";
}
@Override
public int method3(int x) {
return x;
}
}
3、代理类实现InvocationHandler接口
public class DynamicProxy implements InvocationHandler
{
// 这个就是我们要代理的真实对象
private Target target;
// 构造方法,给我们要代理的真实对象赋初值
public DynamicProxy(Object target)
{
this.target= target;
}
@Override
public Object invoke(Object object, Method method, Object[] args)
throws Throwable
{
System.out.println("目标方法前的逻辑");
//执行目标对象的方法
Object invoke = method.invoke(target, args);
System.out.println("目标方法后的逻辑");
return invoke;
}
}
4、ProxyTest类
public class ProxyTest {
public static void main(String[] args){
//需要代理的目标对象
Target target = new Target();
//我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
DynamicProxy dynamicProxy = new DynamicProxy(target);
TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
dynamicProxy);
proxy.method1();
String method2 = proxy.method2();
int method3 = proxy.method3(100);
System.out.println("------------------");
System.out.println(method2);
System.out.println(method3);
}
}
控制台输出;
目标方法前的逻辑
method1 running...
目标方法后的逻辑
目标方法前的逻辑
method2 running...
目标方法后的逻辑
目标方法前的逻辑
method3 runing...
目标方法后的逻辑
------------------
method2
100
六、匿名内部类实现动态代理
1、接口类TargetInterface和目标类Target 同上
2、ProxyTest2类
public class ProxyTest2 {
public static void main(String[] args) {
final Target target = new Target();
//动态创建代理对象
TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
//被执行几次?------- 看代理对象调用方法几次
//代理对象调用接口相应方法 都是调用invoke
/*
* proxy:是代理对象
* method:代表的是目标方法的字节码对象
* args:代表是调用目标方法时参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//反射知识点
System.out.println("目标方法前逻辑");
Object invoke = method.invoke(target, args);//目标对象的相应方法
System.out.println("目标方法后逻辑");
//retrun返回的值给代理对象
return invoke;
}
}
);
proxy.method1();//调用invoke---Method:目标对象的method1方法 args:null 返回值null
String method2 = proxy.method2();//调用invoke---Method:目标对象的method2方法 args:null 返回值method2
int method3 = proxy.method3(100);调用invoke-----Method:目标对象的method3方法 args:Object[]{100} 返回值100
System.out.println("------------------");
System.out.println(method2);
System.out.println(method3);
}
}
3、输出同上
可能我以为返回的这个代理对象会是Subject类型的对象,或者是InvocationHandler的对象,结果却不是,首先我们解释一下为什么我们这里可以将其转化为Subject类型的对象?原因就是在newProxyInstance这个方法的第二个参数上,我们给这个代理对象提供了一组什么接口,那么我这个代理对象就会实现了这组接口,这个时候我们当然可以将这个代理对象强制类型转化为这组接口中的任意一个,因为这里的接口是Subject类型,所以就可以将其转化为Subject类型了。
注意:JDK的Proxy方式实现的动态代理 目标对象必须有接口 没有接口不能实现jdk版动态代理
参考博客:java的动态代理机制详解