首先,什么是代理?代理的作用是什么?
这里我们先讨论什么是代理,他是干嘛的。代理在实际生活中有这大量的应用,我们来举一个例子。
假如,我开了一家咖啡店,我现在是咖啡店的前台,我们店还有专门做咖啡的师傅。你来买咖啡,你肯定是在跟我谈(要什么咖啡,放不放糖,吃什么点心),但是咖啡却是师傅做的,我没有参与咖啡的制作。但是我却可以按照你的需求,要求师傅做你想要的咖啡。
在这里,服务员就是一个代理,他代理了做咖啡的师傅。他控制了整个业务流程,他在师傅做咖啡之前,做了事情(获取了你的需求)。在完成了咖啡后,为您打包。甚至他可以不卖给你,让师傅不工作。
在这个例子中出现了三个对象。客户——事务过程的发起者(相当于程序员),真实对象——做咖啡的师傅(实际工作的完成者 ),代理对象(又可以叫占位)——服务员(整个业务流程的控制者)。
这里我们就可以总结了:代理的意义就是在于通过生成代理对象,来代理真实对象,从而实现了对真实对象访问的控制。
代理的作用就是,在访问真实对象之前或者时候加入相应的逻辑,或者要求不访问真实对象。
那代理的好处在哪里:比如说JDBC,我要写方法查询数据库,其实我就只是要写sql这么简单,但是我每次都要在方法中都要写连接、关闭数据库这些辅助业务。这样耦合了核心业务与辅助业务,并且造成了大量的冗余代码。而代理就能解决这个问题,你只要写核心业务,那些辅助功能都由我代理抽取公共功能,我来做。
下面我们就来谈谈什么是静态代理
public interface Work {
public void doWork();
}
public class MyWork implements Work{
@Override
public void doWork() {
System.out.println("我在工作");
}
}
public class WorkProxy implements Work{
private Work realObject;
public WorkProxy(Work object) {
this.realObject=object;
}
@Override
public void doWork() {
System.out.println("我在准备工作了");//在工作之前加入逻辑
realObject.doWork();//代理对象实现真实对象的逻辑
System.out.println("我完成了工作了");//在工作之后加入逻辑
}
}
我在准备工作了
我在工作
我完成了工作了
这就是静态代理,通过创建一个代理类实现和真实对象方法相同的方法,且让代理类持有真实对象,然后在代理方法里调用真实对象的逻辑,来达到添加我们封装业务逻辑的目的。
优点:在不修改真实对象的情况,来拓展真实对象的功能;在编译的时候就生成了.class字节码文件,可直接使用,效率高。
缺点,他被要求必须实现和真实对象所实现的接口;并且不易维护(如果接口改了,他也要改),静态代理只能为一个真实对象服务,如果真实对象太多,要求生成很多的代理类。
那什么是动态代理呢
为了解决静态代理的缺点,我们可以通过在运行时,动态生成一个持有RealObject、并实现代理接口的Proxy,同时注入我们相同的扩展逻辑。哪怕你要代理的RealObject是不同的对象,甚至代理不同的方法,都可以动过动态代理,来扩展功能。一个代理能够服务多个真实对象就是动态代理的核心思想。或者说代理模式+动态生成(反射)=动态代理。动态代理技术最常用的是JDK动态代理和CGLIB动态代理。
JDK动态代理要求实现InvocationHandler接口:
public class MyJDKProxy implements InvocationHandler{
private Object object=null;//定义真实对象,初始化为null,作用仅仅是给两个方法提供使用,形式不同可以不要 其实
//绑定真实对象,返回代理对象
public Object bind(Object object) {
this.object=object;//将传进来的真实对象保存
//反射生成代理对象
return Proxy.newProxyInstance(object.getClass(). getClassLoader(),//真实对象的类加载器
object.getClass().getInterfaces(),//真实对象继承的接口
this);//定义当前类为实现方法逻辑的代理类,必须实现invoke(),他就是代理逻辑方法的现实方法
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我在准备工作了");//在调用真实方法之前加入逻辑
Object obj=method.invoke(this.object, args);//当代理对象调用真实方法时,会其自动的跳转到代理对象关联的hanlder对象的invoke方法调用
System.out.println("我完成了工作了");//再加入真实方法之后加入逻辑
return obj;
}
public static void main(String[] args) {
MyJDKProxy mjProxy=new MyJDKProxy();
Work work=(Work) mjProxy.bind(new MyWork());//注意这里Work已经是一个对象了,才需要转
work.doWork();
}
}
我在准备工作了
我在工作
我完成了工作了
·生成代理对象用的java.lang.reflect.Proxy(动态代理机制的主类,提供一组静态方法为一组接口动态的生成对象和代理类)中的newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法,他有三个参数
1、类加载器,这里我们使用的是真实对象的类加载器(类装载器类,将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy类与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。 )
2、把生成的代理对象挂在那些接口下。真实对象委托实现的接口。
3、定义实现方法逻辑的代理类,this指当前对象,必须实现InvocationHanlder的invoke方法,他是代理逻辑方法的现实方法。
CGLIB动态代理无法拦截静态方法
动态代理最大的好处就是接口中声明的所有的方法都被转移到一个集中的方法中去处理,就是invocke()方法.这样在接口中声明的方法比较多的情况下我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。动态代理只能代理接口,代理类都需要实现InvocationHandler类,实现invoke方法。该invoke方法就是调用被代理接口的所有方法时需要调用的,该invoke方法的返回值是被代理接口的一个实现类。
总结:
我们理解动态代理,要以代理这个概念为主体,就是以辅助功能为主体,辅助功能中加入业务逻辑,而并不是在业务逻辑前后加上辅助功能。这样动态代理才能服务多个真实对象,代理类(MyJdkProxy)是对辅助功能的抽象。