静态代理和动态代理
代理Proxy:
为什么需要代理?
代理在我们生活中很常见,就像在朋友圈很火的卖面膜一样,他们只用从他的上级手机里面拿货就行了不用关心他的上级从哪获取的货源一样,他的上级就代表了一个代理对象,他们只管去卖其他的不用关心,由他们的代理对象来帮他们完成。我们在程序中也一样会用到代理,像日志输出、权限、事物等控制都是一些公用行为,我们就可以把这些公用的抽出来交给代理类来管理,通过在代理类上的设置就可以让用这些的方法公用他们,有什么改动的时候也可以一次性的改动不用像以前每个方法都改一次麻烦。
静态代理:
由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。
以下代码引自:http://layznet.iteye.com/blog/1182924
/** * 代理接口,处理给定名字的人物 * Created by lkf on 2017/8/17. */ public interface Subject { /** *执行给定名字的任务 *@author: lkf *@Date: 2017/8/17 11:42 */ void dealTask(String taskName); }
/** * 委托类 * 真正执行任务的类,实现了代理接口 * Created by likaifeng on 2017/8/17. */ public class RealSubject implements Subject{ /** *执行给定名字的任务。这里打印出任务名,并休眠500ms模拟任务执行了很长时间 *@author: lkf *@Date: 2017/8/17 11:43 */ @Override public void dealTask(String taskName) { System.out.println("正在执行任务:"+taskName); try{ Thread.sleep(500); }catch (Exception e){ e.printStackTrace(); } } }
/** * 静态代理类 * Created by lkf on 2017/8/17. */ public class ProxySubject implements Subject { //代理类持有一个委托类的对象引用 private Subject delegate; public ProxySubject(Subject delegate) { this.delegate = delegate; } @Override public void dealTask(String taskName) { long stime = System.currentTimeMillis(); //将请求分派给委托类处理 delegate.dealTask(taskName); long ftime = System.currentTimeMillis(); System.out.println("执行任务耗时"+(ftime - stime)+"毫秒"); } }
/** * 此类也可以不需要 * Created by lkf on 2017/8/17. */ public class SubjectStaticFactory { //客户类调用此工厂方法获得代理对象。 //对客户类来说,其并不知道返回的是代理类对象还是委托类对象。 public static Subject getInstance(){ return new ProxySubject(new RealSubject()); } }
/** *客户类 * Created by lkf on 2017/8/17. */ public class Client { public static void main(String[] args) { Subject proxy = SubjectStaticFactory.getInstance(); proxy.dealTask("Task"); } }
打印出来的结果为:
正在执行任务:Task
执行任务耗时500毫秒
通过以上代码我们可以看出在委托类执行之后输出了代理类里面的内容。
静态代理的优缺点:
优点:代理使客户端不需要知道实现类是什么,怎么做的,而客户端只需知道代理即可(解耦合),我们还用了工厂类,客户端连代理类是谁都不知道。
缺点:
1.代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
2.代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了
动态代理:
动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象
public class SubjectInvocationHandler implements InvocationHandler{ //代理类持有一个委托类的对象引用 private Object delegate; //绑定关系,也就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。 public Object newProxyInstance(Object delegate){ this.delegate=delegate; //该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例 //第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器 //第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口 //第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法 //根据传入的目标返回一个代理对象 return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(),this); } //Object proxy:被代理的对象 //Method method:要调用的方法 //Object[] args:方法调用时所需要参数 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long stime = System.currentTimeMillis(); //利用反射机制将请求分派给委托类处理。Method的invoke返回Object对象作为方法执行结果。 //因为示例程序没有返回值,所以这里忽略了返回值处理 method.invoke(delegate, args); long ftime = System.currentTimeMillis(); System.out.println("执行任务耗时"+(ftime - stime)+"毫秒"); return null; } }
public class Client { public static void main(String[] args) { SubjectInvocationHandler subjectInvocationHandler = new SubjectInvocationHandler(); //返回值和参数可以随意改变 Subject subject = (Subject) subjectInvocationHandler.newProxyInstance(new RealSubject()); subject.dealTask("s"); } }
通过以上代码我们可以实现代理不同类型的对象,如果我们把对外的接口都通过动态代理来实现,那么所有的方法都会经过invoke(),因此我们就可以在这里做一些自己想做的操作,比如日志系统、事务、拦截器、权限控制等。这也就是AOP(面向切面编程)的基本原理。
动态代理优点:
在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。而且动态代理的应用使我们的类职责更加单一,复用性更强