代理
为什么么要学习代理模式,因为aop的底层机制就是动态代理;
代理模式:
- 静态代理
- 动态代理
静态代理
代理模式的角色分析:
抽象角色:一般会使用抽象类或者接口实现
真实角色:被代理的角色
代理角色:代理真实角色,代理真实角色后,一般会做一些附属的操作;
客户:使用代理角色进行一些操作
关于静态代理的详细讲解在我之前的博客《静态代理》中有较为详细的讲解,这里不再重复,
在这篇博客中我们将租客、房东和中介的关系使用代理来进行描述,这个关系和之前我的博客中的结婚和婚庆公司的关系一致,可以实现一一对应,这里不再过多描述:
代码实现:
公共实现的接口:
package com.org.westos.DongProxy;
//租房的接口:抽象
public interface Rent {
//租房
void rent();
}
真实对象:
package com.org.westos.DongProxy;
//这个房子要出租
public class Host implements Rent {
//出租
public void rent(){
System.out.println("房东要出租房子");
}
}
代理对象:
package com.org.westos.DongProxy;
//房屋中介--代理
public class Proxy implements Rent {
//房东
private Host host;
public void setHost(Host host) {
this.host = host;
}
public void rent() {
lookHouse();
host.rent();
fare();
}
private void lookHouse(){
System.out.println("中介带你看房");
}
private void fare(){
System.out.println("收取中介费");
}
}
测试:
package com.org.westos.DongProxy;
public class You {
public static void main(String[] args) {
Host host = new Host();
Proxy proxy = new Proxy();
proxy.setHost(host);
proxy.rent();
}
}
通过上述的代码就实现了简单的静态代理
静态代理模式的好处
- 可以使真实角色更加纯粹,不用去关注一些公共的事情;
- 公共的业务由代理来完成,实现业务的分工;
- 公共业务的要扩展的话,可以更加集中和方便;
缺点:
- 假如我们的真实角色变得非常多,代理类也会随之增多,工作量变大,开发效率变低!
然后我们想需要一种能够有静态代理的全部好处,但是又不存在这种缺点的东西。
动态代理
动态代理和静态代理的角色都是一样;
静态代理模式的代理类是我们提前写好的,动态代理的类是动态生成的;
动态代理大概分两类:
基于接口实现:JDK
基于类实现:cglib
当今用的比较多的是 JAVAssist来生成动态代理
了解动态代理之前,需要掌握两个类:
InvocationHandler
Proxy
在JDK帮助文档中查阅这两个类:
-
InvocationHandler
是由代理实例的调用处理程序实现的接口 。
上面这句话听起来很拗口,可以理解为动态产生代理的类需要实现的接口 -
在实现上面的接口后需要重写
invoke(Object proxy, 方法 method, Object[] args)
处理代理实例上的方法调用并返回结果。 -
Proxy
提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。 -
通过帮助文档查找到
Proxy
类的静态方法为newProxyInstance(ClassLoader loader, 类<?>[] interfaces,InvocationHandler h)
返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。
上述静态方法的参数为类加载器、通过反射查找到类的接口和其他参数
之前的几个类都是在将静态代理的时候讲解的
我们需要创建两个新的类来分别作为动态产生代理的类和测试类,这里房东和Rent
接口不再重新写,直接使用之前写静态代理时候写好的
1.动态产生代理的类需要实现InvocationHandler
接口
public class InvocationHandlerProxy implements InvocationHandler {
}
2.在实现上面的接口后需要重写invoke
方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
3.重写上面的方法时第二个参数method
时IDE会自动导入java.lang.reflect.Method
包,导入这个包后我们需要调用这个包下的invoke
方法
Object result = method.invoke(rent, args);
return result;
invoke
方法的第一个参数是一个对象,这里需要我们代理类和真实类共同实现的接口也就是Rent
,第二个是对象的参数,这里直接使用传入的args
,在执行这个方法完成后我们可以返回result
对象
4.当然我们在重写InvocationHandler
接口中的invoke
方法时也可以在里面添加一些代理的特有的一些方法,比如:
private void lookHouse(){
System.out.println("中介带看房子");
}
private void zhongJieFei(){
System.out.println("收中介费");
}
上面的两个方法就是代理类特有的两个方法,我们可以将他们添加到重写的接口中的invoke
方法中
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
lookHouse();
Object result = method.invoke(rent, args);
zhongJieFei();
return result;
}
5.在写method.invoke
方法时我们需要一个对象,这个对象就是真实对象和代理类一起实现的共同的接口,在我们的例子中就是Rent
接口,因为是动态设置,所以不同的接口就会产生不同的代理类,所以我们需要写一个方法来从外界设置我们使用的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
6.在执行完上面的操作后还有一点,在我们重写InvocationHandler
接口中的invoke
方法时其中的一个参数就是proxy
,所以我们还需要一个方法来产生proxy
,这里我们需要使用到Proxy
类的静态方法newProxyInstance
,这里要注意newProxyInstance
方法的几个参数:类加载器、通过反射查找到类的接口和其他参数
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
}
到这里动态代理的代码才算全部完成,之后可以编写一个测试列来进行测试
测试类代码为:
package com.org.westos.DongProxy;
public class Test {
public static void main(String[] args) {
Host host = new Host();
InvocationHandlerProxy ihp = new InvocationHandlerProxy();
ihp.setRent(host);
Rent proxy = (Rent) ihp.getProxy(); //dong
proxy.rent();
}
}
可以看到运行成功,动态代理完成
后续我还会添加一些自己对代理的理解,敬请期待