Spring学习之路——代理模式
一、 代理模式简介
代理模式是什么鬼??我们来学Spring???我们为什么要学习代理模式呢???
因为SpringAOP的底层机制就是动态代理!!
代理模式又分为动态代理和静态代理,这里我们将会来分析这两种代理模式。
二、静态代理
静态代理模式又分为如下的几个角色。
- 抽象角色 : 一般使用接口或者抽象类来实现
- 真实角色 : 被代理的角色
- 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
- 客户 : 使用代理角色来进行一些操作
这里我们通过一个房屋中介的例子来给大家演示一下静态代理模式。
我们首先定义一个rent接口和一个Host房东类
public interface Rent {
public void rent();
}
public class Host implements Rent {
public void rent() {
System.out.println("我是房东,我要租房");
}
}
然后写一个租客的类。我们一般采用new一个房东实例然后调用其rent方法这种方式来实现租房功能。就像这样
public class Renter {
public static void main(String[] args) {
Host host = new Host();
host.rent();
}
}
但是大家有没有想过,如果我还需要添加一些其他的方法进去,我就必然要改变其中的Host类,可能咱们现在这里代码量很少,大家可能觉得不就添一个方法么,没什么啊。但是如果现在的host方法本来就很复杂,再对原生的类添加新的东西很容易导致现在的类就没法用了,并且久而久之,类内部方法尤其的多,耦合性会非常的强!!!
那怎么整啊???
这时候我们就可以使用代理模式这种思想。
我们可以引入一个房屋中介类,里面创建一个Host对象,然后可以再中介类里面新增一些别的方法,这就是代理模式。
public class Proxy {
Host host = new Host();
public Proxy(){
}
public Proxy(Host host) {
this.host = host;
}
public Host getHost() {
return host;
}
public void setHost(Host host) {
this.host = host;
}
public void seeHouse(){
System.out.println("带人看房");
}
public void fee(){
System.out.println("收房租");
}
public void rent(){
seeHouse();
host.rent();
fee();
}
}
我们再改一改租客类。
public class Renter {
public static void main(String[] args) {
Host host = new Host();
Proxy proxy = new Proxy(host);
proxy.rent();
}
}
最后我们发现我们没往Host类中添方法也实现了这些新的方法。
这就是所谓的静态代理模式。
他的好处:
- 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
- 公共的业务由代理来完成 . 实现了业务的分工 .
- 公共业务发生扩展时变得更加集中和方便 .
缺点也很明显,类越多,代理类就越多,开发效率就大大降低了。所以我们可以引入动态代理!!
三、动态代理
为了更好的理解动态代理,各位可以先把反射这个编程思想看一看。
- 动态代理的角色和静态代理的一样 .
- 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
- 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
动态代理主要是有三种类型JDK动态代理(基于接口)、cglib(基于类)、javasist 。现在用的比较多的是javasist,但是这里我们主要来说一说JDK动态代理的方式,他们道理其实都是一样的.
JDK动态代理主要是需要用到这两个类,InvocationHandler 和 Proxy ,我们来看看官方文档。
InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.
大概意思就是每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。
下面我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:
Object invoke(Object proxy, 方法 method, Object[] args);
//参数
//proxy - 调用该方法的代理实例
//method -所述方法对应于调用代理实例上的接口方法的实例。 方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
//args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。 原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。
这些参数大家如果大家实在不了解也没有关系,我们现在还是通过一个例子让大家了解了解动态代理模式.
有一天,脏丽丽有事儿想把自己家狗兜兜给别人照看两天,就想到了好朋友小雪.但是兜兜这条狗不认识小雪,小雪只能到脏丽丽家里来把兜兜领走,脏丽丽家有很多人,因为小雪也不知道什么时候来,来取走兜兜的时候脏丽丽可能不在家,所以到时候把狗给小雪的人也有可能是脏丽丽的妈妈,爷爷等人.我们先把几个类写好.
public interface Dog {
public void poo();
public void pee();
}
public class DouDou implements Dog
{
public void poo() {
System.out.println("兜兜想拉粑粑");
}
public void pee() {
System.out.println("兜兜想尿尿");
}
}
这里因为脏丽丽的家算是一个代理类,所以要集成InvocationHandler.脏丽丽的家里人还要提醒下小雪要在兜兜拉完粑粑之后把他的粑粑捡起来,所以里面还有一个jianbaba方法.
里面因为在小雪来的时候不知道谁来把兜兜给小雪,所以还得有一个生成动态代理的方法,小雪来的时候好有一个人接待他,这就是getProxyInstance方法.
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
interfaces: 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
这里我们第一个参数用的是兜兜的ClassLoader,第二个用的是Dog接口,第三个参数使用的是this关键字,把这个当时接待小雪的脏丽丽家人绑定到脏丽丽家这个Handler对象上。
我们看看代码.
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Zanglili implements InvocationHandler {
private Dog doudou;
public Zanglili() {
}
public Dog getDoudou() {
return doudou;
}
public void setDoudou(Dog doudou) {
this.doudou = doudou;
}
public Zanglili(Dog doudou) {
this.doudou = doudou;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object ret = method.invoke(doudou,args);
jianbaba();
return ret;
}
public void jianbaba(){
System.out.println("记得把兜兜的粑粑捡了");
}
public Object getProxyInstance(){
return Proxy.newProxyInstance(doudou.getClass().getClassLoader(),doudou.getClass().getInterfaces(),this);
}
}
之后我们还要实现Invoke接口,这是所有代理类都要实现的对象。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object ret = method.invoke(doudou,args);
//执行兜兜的方法,args是参数
jianbaba();
//执行完记得调用捡粑粑方法,把兜兜粑粑捡起来
return ret;
}
最后我们测试一下
public class Mytest {
public static void main(String[] args) {
Dog doudou= new DouDou();
Zanglili lilijia = new Zanglili(doudou);//把兜兜传给脏丽丽家这个代理类
Dog zanglilidejiaren = (Dog) lilijia.getProxyInstance(); //生成一个代理
zanglilidejiaren.poo();//通过代理调用方法
}
}
最后执行完全没问题.
最后我们来看一看动态代理的好处。
静态代理有的它都有,静态代理没有的,它也有!
- 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
- 公共的业务由代理来完成 . 实现了业务的分工 ,
- 公共业务发生扩展时变得更加集中和方便 .
- 一个动态代理 , 一般代理某一类业务
- 一个动态代理可以代理多个类,代理的是接口!