写代码的过程中看到许多 Proxy 的类,知道是代理模式,也知道代码如何走下去。
写代码的过程中看到许多 Proxy 的类,知道是代理模式,也知道代码如何走下去。但是对于代理模式的细节和原理却不太懂。
通过网上的学习得知
『代理模式是 Java 中常见的一种设计模式,根据创建代理类的时间点分为静态代理和动态代理。』
静态代理#
静态代理就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的. class 文件就已经生成。
静态代理模式最主要的就是有一个公共接口,一个具体的类,一个代理类,代理类持有具体类的实例,代为执行具体类实例方法。
静态代理的例子#
根据我的想法编了一个小例子,座位明星肯定要出席活动,Jisoo 要出席香奈儿的活动,但是由于身体原因。现在有 Jennie 代为出席。假如就和 Jisoo 出席的效果一样。
首先创建一个 Person 接口
public interface Person{
//出席活动
public void attendance();
}
然后是 Idol 的实现类:
public class Idol implements Person{
String name;
int age;
Idol(String n,int a){
this.name=n;
this.age=a;
}
//实现接口
@Override
public void attendance() {
System.out.print(this.name+"出席了Channl活动");
}
}
然后是 IdolProxy 代理类,持有 Idol 的对象。也和 Idol 对象一样实现了 Person Interface
public class IdolProxy implements Person{
Idol idol;
public IdolProxy(Person stu){
if(stu.getClass()==Idol.class) {
this.idol=(Idol)stu;
}
}
@Override
public void attendance() {
idol.attendance();
}
}
接下来就是测试类
public static void main(String[] args) {
Person Jisoo=new Idol("Jisoo",18);
Person Jennie=new IdolProxy(Jisoo);
Jennie.attendance();
}
输出结果为:
Jisoo出席了Channl活动
没有通过 Jisoo 去参加活动,而是通过 Jennie 的代理去的,这就是代理模式;
有一个公共的接口(Person),一个实现类(Idol)和一个代理类(IdolProxy)都实现了接口,
代理类还包含一个实现类的实例对象。并且代理这个对象执行具体的方法。
代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。
这里的间接性就是指不直接调用实际对象的方法,那么我们在代理过程中就可以加上一些其他用途。比如 AOP 的的一些功能
动态代理#
静态代理的时候代理类是已经实现的,就是我们写好的,而动态代理就是在程序运行时动态创建的代理类。事先没写代理类的实现。
主要的点就是在 Proxy.newProxyInstance (loader, interfaces, h); 这个方法,这个方法是 Proxy 下的静态方法,而 Proxy 是 Java.lang.reflect 下的类
这个方法就相当于对上面静态代理的代理类的一个抽象,他可以有我们代码指示去如何代理一个对象。下面通过上面的那个例子来简单实现以下:
动态代理的例子#
我们要实现的仍然是 Jennie 去代理身体不输入的 Jisoo 出席 Channal 的活动。我看了网上的大部分教程都是从上往下讲,从建立接口到实现 Invocation 接口的类
从我自己的理解来看,是从下网上理解较容易理解一些。那就直接从测试的类开始:
//创建一个实例对象,这个对象是被代理的对象
Person Jisoo = new Idol("Jisoo",18);
//创建一个与代理对象相关联的InvocationHandler,这个就相当于静态代理的代理类idolProxy了 只不过比他牛逼,他含有所有的类的对象是一个Object
InvocationHandler idolHandler = new IdolInvocationHandler<Person>(Jisoo);
//创建一个代理对象Jennie来代理Jisoo,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
Person Jennie = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, idolHandler);
//代理执行方法
Jennie.attendance();
第一步 Person Jisoo = new Idol (“Jisoo”,18); 不需多说了创建一个实现 Person 接口的实例化对象。
关键就是第二步 InvocationHandler idolHandler = new IdolInvocationHandler (Jisoo);
这里面的 InvocationHandler 是 java.lang.reflect.InvocationHandler; 是 Java 提供的实现动态代理类的接口,我们需要实现这个接口:
public class IdolInvocationHandler<T> implements InvocationHandler{
//invocationHandler持有的被代理对象
T target;
public IdolInvocationHandler(T target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("11111");
method.invoke(target, args);
System.out.println("2222");
return null;
}
}
这个接口定义了 invoke 方法,invoke 就是援引,调用的意思。就是说所有被代理类的方法都通过这个方法调用。
如何获取的被代理类的方法呢?就是通过 Java 的反射机制。invoke 方法有三个参数,第一个就是被代理的对象,第二个是方法对象,第三个就是参数
接下来就是 :
Person Jennie = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, idolHandler);
这就生成了一个代理的对象 Jennie, Proxy.newProxyInstance (loader, interfaces, h) 包含三个参数,第一个是接口的加载器 ,第二个是代理接口的列表
第三个就是上面实现 Invocation 接口的类 IdolInvocationHandler 的对象 idolHandler,这样在执行 Jennie 的 attendance 方法的时候就会输出:
11111
Jisoo出席了Channl活动
2222
这样就实现了动态代理的功能。。注意:动态代理只能对接口进行代理,不能对类进行代理。究其原因就是 Java 只支持单继承,只有通过接口实现多继承的关系。
原理:
其实大概就是把接口复制出来,通过这些接口和类加载器,拿到这个代理类 cl。然后通过反射的技术复制拿到代理类的构造函数(这部分代码在 Class 类中的 getConstructor0 方法),最后通过这个构造函数 new 个一对象出来,同时用 InvocationHandler 绑定这个对象。
PS:
Person idolProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, (proxy,method,agrs)->{
if(method.getName()=="attendance") {
System.out.println("1111");
method.invoke(Jisoo, args);
System.out.println("2222");
return null;
}
return null;
});
在创建动态代理对象的那步还可以这么写,用 java8?的新特性 Lmbada 表达式来写,就不用写 Invocation 的实现类了。