代理就是真实对象的代表。
代理模式的应用场景:
1.远程代理,也就是为一一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实。
例如当某一个产品在当地做大后,那么就可以去其他地方发展,每个地方可以雇佣一个代理来帮忙打理。
2.虚拟代理,是根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。这样就可以达到性能的最优化,比如说你打开一个很大的HTML网页时,里面可能有很多的文字和图片,但你还是可以很快打开它,此时你所看到的是所有的文字,但图片却是一张一张地下载后才能看到。那些未打开的图片框,就是通过虚拟代理来替代了真实的图片,此时代理存储了真实图片的路径和尺寸。
3.安全代理,用来控制真实对象访问时的权限。一般用于对象应该有不同的访问权限的时候。
4.智能引用代理(项目开发中应用最广泛),是指当调用真实的对象时,代理处理另外一些事。如计算真实对象的引用次数, 这样当该对象没有引用时,可以自动释放它;或当第一次引用- 一个持久对象时,将它装入内存;或在访问一个实际对象前,检查是否已经锁定它,以确保其他对象不能改变它。它们都是通过代理在访问一个对象时附加一些内务处理。(日志管理,权限管理,事物处理...)
代理模式结构图
静态代理:代理和被代理对象在代理之前是确定的。他们都实现相同的接口或者继承相同的抽象类。
常用的静态代理方式通过聚合和继承来完成,聚合是通过实现相同的接口来实现的,继承是通过继承相同的抽象类来实现的。
聚合是比继承更适合代理模式:
如果使用继承来实现代理功能的叠加,每当我们要添加或者更改功能时,我们就需要写不同的类来实现功能的变更,就算功能实现的顺序改变也需要重新写一个代理类。这样代理类会越来越多,使得代码臃肿。
使用聚合的方式来就可以灵活调用各个代理模块,代理之间可以互相传递。
静态代理
代理类对真实对象需要做的事情前后添加东西,客户端通过接口去实现代理类,代理类又依赖目标类
public class Main { public static void main(String[] args) { Subject subject=new Proxy(new RealSubject()); subject.execute(); } } //对需要做的一件事进行抽象 interface Subject { void execute(); } //真实对象 class RealSubject implements Subject { @Override public void execute() { // TODO Auto-generated method stub System.out.println("吃饭..."); try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } //代理对象 class Proxy implements Subject { //真实对象 RealSubject realSubject; public Proxy(RealSubject realSubject) { super(); this.realSubject = realSubject; } @Override public void execute() { // TODO Auto-generated method stub long starttime = System.currentTimeMillis(); System.out.println("记录开始时间...."); realSubject.execute(); long endtime = System.currentTimeMillis(); System.out.println("记录结束时间.... 用时:" + (endtime - starttime) + "毫秒!"); } }
当我们有多个接口,多个实现类,这些实现类都需要使用到记录时间的功能,如果我们使用静态代理的话,需要为每个实现类去写一个代理接口,这样先让会让代码变得臃肿起来。这时我们就可以使用动态代理的方式来处理这个问题。
动态代理分为JDK动态代理和Cglib动态代理(本文主要说的是Jdk动态代理)
Jdk动态代理只能代理实现接口的类,没有实现接口的类不能实现JDK的动态代理。
Cglib动态代理是针对类来实现代理的,对指定目标类产生一个子类,通过方法拦截技术拦截所有父类方法的调用。
Cglib看https://blog.csdn.net/BushQiang/article/details/98260252
Java动态代理类位于java.lang.reflect包下,一-般主要涉及到以下两个类:
(1)Interface InvocationHandler :该接口中仅定义了一个方法public object invoke(Object obj;Method method, Object[] args)在实际使用时,第一个参数obj一般是指代理类, method是被代理的方法, args为该方法的参数数组。 这个抽象方法在代理类中动态实现。
(2)Proxy :该类即为动态代理类
static Object newProxyInstance(ClassLoader loader, Class[] interfaces,InvocationHandler h) :返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在接口中声明过的方法)
上面的时间代理可以修改为适合任何类的
package com.smxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * * @Description :动态代理模式 * @author Bush罗 * @date 2018年10月7日 * */ public class ProxyTest { public static void main(String[] args) throws InstantiationException, IllegalAccessException { Work work = (Work) new ProxyTime().getInstance(new RealProxy()); work.execute(); } } interface Work { void execute(); } class RealProxy implements Work { @Override public void execute() { // TODO Auto-generated method stub System.out.println("吃饭..."); } } class ProxyTime implements InvocationHandler { private Work target; public Object getInstance(Work target) { this.target = target; Class<?> clazz = target.getClass(); Object obj = Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this); return obj; } @Override public Object invoke(Object obj, Method method, Object[] args) throws Throwable { long starttime = System.currentTimeMillis(); System.out.println("记录开始时间...."); // 第一个参数是target,也就是被代理类的对象;第二个参数是方法中的参数 method.invoke(target, args); long endtime = System.currentTimeMillis(); System.out.println("记录结束时间.... 用时:" + (endtime - starttime) + "毫秒!"); return null; } }