静态代理
静态代理很简单,举个例子:
我们日常生活中,都要找人解决各种各样问题。
我们先不管到底解决的是什么问题,我们同意写成do()。
静态代理的一个重要的地方就是需要固定的接口。
大体流程是这样的:
平时我们找人做事,我们需要直接找到这个人,但是个人的经历非常有限,而且能做的事情非常少。
比如我们在这个人的接口上面定义了他能做某件事,所以这个人就只能做某件事,为什么不能干别的,因为我们没定义。因为定义了,就违反了OOP编程的概念。
我再把这个例子具体一点,你也许就能明白许多。
比如我们定义一个Person接口,它的唯一方法是do();
再定义一个实现类,歌手,然后实现这个方法,我们告诉这个歌手,你只能唱歌。
这个时候,当我们想调用歌手的时候。发现歌手很忙。而且他只能唱歌。毕竟术业有专攻。这个时候我们怎么办。
我们需要给这个歌手一个经纪人。我们这里称为代理类。
然后我们就以后不直接找这个歌手了,我们直接找这个经纪人,也就是这个代理类,歌手负责唱歌,代理类负责做所有其它的事情。
public interface Person{void do();}public class Singer implements Person{@Overridepublic void do(){sout("我能唱歌!");}}public class PersonProxy{private Person person;public PersonProxy(Person person){this.person = person}// 加这么多 o 就是想表面,名字不是一样的。但是do很恰当。public void dooooo(){// do something!person.do();// do something!}}public class main{psvm{PersonProxy personproxy = new PersonProxy(new Singer);personproxy.dooooo();}}
到现在你懂了吗? 当然在代码里面是不能用 do 这个关键字的。
当我们想用 歌手的时候就传歌手,就很自由。
静态代理对于一个 经常敲代码的人来说, 即使没有学过,也会很自然的写出来,但是静态代理的缺点就是,很难扩展。
比如:我们要在歌手唱歌之前和后,不断变换业务,那会怎么样?你是选择做超多的代理类还是?如果要复用呢?你又如何做?
动态代理
使用动态代理,我们实现一个接口。
InvocationHandler 顾名思义: 调用处理器
流程控制都由它处理。
它长这样:
public interface InvocationHandler {public Object invoke(Object proxy, Method method, Object\[\] args)throws Throwable;}
记得刚学动态代理的时候,反射和设计模式,总不能很好的互相适应,每次都感觉摸不到知识。后来通过不断的看,总算把它掌握了。
参数的名字:
- proxy:调用此方法的代理实例
- method:就是反射那个Method类,在这里就是正在调用的方法
- args:这个是在这个方法内调用方法时方法的参数
- 返回值:这个返回值,真的是,看了好多篇博文,大家真的是非常默契,点到即止,一到这里就没了。所以我特地查了API,找到了答案
原文是:
the value to return from the method invocation on the proxy instance. If the declared return type of the interface method is a primitive type, then the value returned by this method must be an instance of the corresponding primitive wrapper class; otherwise, it must be a type assignable to the declared return type. If the value returned by this method is {@code null} and the interface method’s return type is primitive, then a @code NullPointerException will be thrown by the method invocation on the proxy instance. If the value returned by this method is otherwise not compatible with the interface method’s declared return type as described above,a @code ClassCastException will be thrown by the method invocation on the proxy instance.中文意思是:从代理实例上的方法调用返回的值。如果接口方法声明的返回类型是原始类型,那么该方法返回的值必须是对应原始包装类的实例;否则,它必须是可分配给声明的返回类型的类型。如果此方法返回的值为{@code null},且接口方法的返回类型为原始类型,则方法调用将在代理实例上抛出{@code NullPointerException}。如果此方法返回的值与上述接口方法声明的返回类型不兼容,则方法调用将在代理实例上抛出{@code ClassCastException}。
懂了没?
然后我们还需要一个 Proxy 类
这个类的作用就是:动态生成代理类和对象
public static Object newProxyInstance(ClassLoader loader,Class<?> [] interfaces,InvocationHandler h)throws IllegalArgumentException
这个类 需要传入 类加载器,要操作的接口,和 你定义的调用处理器。 并返回一个 代理对象。
来,我们直接上代码:
我们把do改成doSomething,你们可以直接复制代码进行测试,我推荐这么做,因为动态代理,稍难理解。
public interface Person{void doSomething();}public class Singer implements Person{@Overridepublic void doSomething(){sout("我能唱歌!");}}// 注意这里我不采用名字 PersonProxy,用 PersonHandlerpublic class PersonHandler implements InvocationHandler{private Person person;public PersonProxy(Person person){this.person = person}// 我们会实现这个方法@Overridepublic Object invoke(Object proxy, Method method, Object\[\] args) {// 这个方法就是控制流程了,你可以在这里做一些事情。// do something//这个方法就是被代理的类内的方法。method.invoke(person, args);// do something// 因为原始的方法没有返回值,所以我们这里返回nullreturn null;}}public class Main{psvm{// 把要处理的对象搞进来Singer singer = new Singer();// 此类的处理器。多态真的太棒了。然后把要处理的对象传进去。PersonHandler personHandler = new PersonHandler(singer);// 这里我们会先做个代理对象// 第一个参数:类加载器,这里我们随便选个默认加载器// 第二个参数:处理类的接口,这里就是 Person// 第三个参数:我们定义的处理器,这里就是 personHandlerPerson proxy = (Person)Peoxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class {Person.class},personHandler)// 这里我们就可以调用包裹了Singer所有方法的代理类,// 这个代理类 可以 调用 Singer 的所有方法,// 因为我们就定义了一个 doSomething,所以我们就调用了一个方法,// 但是这个方法就是:上面所谓的流程。// 你可以在 handler 里面,进行在这个方法上面下面干你想做的事情。// 这个就是动态代理了。proxy.doSomething();}}
他们最大的区别就是,静态代理只代理一个类,
而动态代理可以代理,一个接口下的多个类。