静态代理仍是相对简单的,就是若是我是代理,我看中你的功能。我就和你联系(关联),而后我在作一些本身的工做,好比宣传推广,顺便赚取一些外快之类的事情。java
这中间最重要的两件事情一是:联系(关联),我要代理你的产品(功能),首先我要联系你。二是:咱们对外作一样的事情(实现一样的接口),好比我代理12306卖车票,那么咱们都提供了卖车票这个服务(实现这个接口)。这一点很容易就变成了装饰模式或者简单的依赖关系。spring
下面来一个例子,如今有一个卖药的接口以下:编程
public interface SaleMedicine { public void saleMedicine(); }
有一个药厂类实现了这个接口,他在卖药以前老是要生产药:缓存
public class MedicineFactory implements SaleMedicine{ @Override public void saleMedicine() { produceMedicine(); System.out.println("卖药"); } private void produceMedicine() { System.out.println("生产药品"); } }
还有一个医院类,也实现了卖药接口,可是它不能生产药啊,因此它以后作代理,一是它联系一个药厂作药厂的代理(关联一个药厂类):jvm
public class Hospital implements SaleMedicine { private SaleMedicine saleMedicine; @Override public void saleMedicine() { System.out.println("医生看病"); saleMedicine.saleMedicine(); System.out.println("收钱"); } public SaleMedicine getSaleMedicine() { return saleMedicine; } public void setSaleMedicine(SaleMedicine saleMedicine) { this.saleMedicine = saleMedicine; }
医院类在卖药以前,仍是要找医生看看病的对吧?如今来换一个思路,你以为医院不是为了卖药的啊,这不科学啊。医院就是为了看病的嘛。因此医院要实现一个看病的接口。先来一个治病接口:ide
public interface Heal { public void heal(); }
再来一个医院类:测试
public class KindHospital implements Heal{ private SaleMedicine saleMedicine; @Override public void heal() { System.out.println("治病"); saleMedicine.saleMedicine(); } public SaleMedicine getSaleMedicine() { return saleMedicine; } public void setSaleMedicine(SaleMedicine saleMedicine) { this.saleMedicine = saleMedicine; } }
再来一个测主类:this
public class StaticProxy { public static void main(String[] args) { proxySaleMedicine(); System.out.println("---------------------"); relationSaleMedicine(); } public static void proxySaleMedicine() { Hospital hospital = new Hospital(); SaleMedicine saleMedicine = new MedicineFactory(); hospital.setSaleMedicine(saleMedicine); hospital.saleMedicine(); } public static void relationSaleMedicine() { KindHospital hospital = new KindHospital(); SaleMedicine saleMedicine = new MedicineFactory(); hospital.setSaleMedicine(saleMedicine); hospital.heal(); } }
看这2个医院类没有什么区别对吧?的确没有太大的区别,从语义上来讲就是侧重点不一样而已。可是对于面向对象编程来讲,这是有意义的,使用静态代理的方式更加符合面向接口编程的原则。它使得共同的服务抽象在了共同的接口中。这让咱们在消费这一服务的时候有更加灵活的选择。spa
静态代理可能在实际的使用中不是特别多,下面就来看一看动态代理,这很是有用。在spring中aop就是使用动态代理实现的。代理
动态代理相对于静态代理最大的特色是它不用实现和要代理对象相同的接口,而是在你须要代理的时候把要代理的接口注入进去就能够了。若是以为抽象没有关系,先来看一个例子吧,如今有一个获取重对象的接口:
public interface HeavyObjectFactory { public Object getObject(String key); }
而后有一个获取重对象的类实现了HeavyObjectFactory
public class HeavyObject implements HeavyObjectFactory { @Override public Object getObject(String key) { try { Thread.sleep(3000);//模拟重对象产生 } catch (InterruptedException e) { e.printStackTrace(); } return "建立重对象"; } }
如今来一个动态生成代理对象的类,它能够代理实现了HeavyObjectFactory的类,若是要获取一个重对象就会加上缓存:
public class CacheProxy implements InvocationHandler { private Map<String,Object> cache = new HashMap<String,Object>(); private static Method getHeavyMethod; private static final Class<?> clazz = HeavyObjectFactory.class; static{ getHeavyMethod = clazz.getMethods()[0]; } //要被代理的对象,直接使用最顶层的Object类 private Object target; @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(getHeavyMethod.equals(method))//若是是获取重对象的方法就使用缓存 { String key = (String) args[0]; Object value = cache.get(key); if(null == value) { value = method.invoke(target, args); cache.put(key, value); } return cache; } return method.invoke(target, args); } public Object getTarget() { return target; } public void setTarget(Object target) { this.target = target; } public Object getCacheProxy(Object target) { this.target = target; Class<? extends Object> clazz = target.getClass(); //最好注入要代理的接口就能够了,不要注入全部接口,由于这个是在动态的生成一个对象 Object object = Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this); return object; } }
这个类有3个值得注意的地方:
1. 继承了java.lang.reflect.InvocationHandler这个类 2. 重写了public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 方法 3. 用Proxy的public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces, InvocationHandler h) throws IllegalArgumentException生成要代理的对象。
先来看Proxy的newProxyInstance方法,这个方法有3个参数,第1个是参数是被代理的对象的类加载器,由于代理类是动态生成的因此须要指定类加载器,jvm好加载。第2个是指定要代理的接口。动态生成的代理会代理这些接口的方法。第3个参数是InvocationHandler接口,这个接口只有一个invoke方法就是上面强调的第2点。
如今好理解了吧,动态生成的代理最终都是调用的
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
这个方法,这个方法就是把要代理的对象传进来,把被代理的方法和参数传进来,就能够在执行要代理的方法method以前,先作一点本身的事情了对吧。
如今来看一下动态代理的主类,主要测试获取5次重对象使用缓存代理和不使用缓存代理的差异:
public class DynamicProxy { public static void main(String[] args) { HeavyObject heavy = new HeavyObject(); long start = System.currentTimeMillis(); for(int i =0;i<5;i++) heavy.getObject("key"); long end = System.currentTimeMillis(); System.out.println("no cache time:"+(end-start)); CacheProxy cache = new CacheProxy(); HeavyObjectFactory factory = (HeavyObjectFactory) cache.getCacheProxy(heavy); start = System.currentTimeMillis(); for(int i =0;i<5;i++) factory.getObject("key"); end = System.currentTimeMillis(); System.out.println("caceh time:"+(end-start)); } }
其实这种代理再配上注解功能更增强大,好比spring中Cacheable注解。若是须要缓存,除了须要配置一下注解,其余基本上是透明的。看了spring aop均可以作的事情,你就会感叹原来动态代理能够这么强大。