以前面试的过程当中,有被面试官问到Spring中的AOP相关问题。当时只回答了具体的一个概念和相关应用,还补充了下底层是运用了代理模式;但以前一直并无对相关的代理模式进行一个深刻的理解,特此写下了这篇博客,记录下。。。java
废话很少说,首先咱们知道代理模式是设计模式中的一种,书中给到的定义是:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。网上通俗说是像咱们生活中的中介。 举个例子:我要买房子,但我这我的没什么生活经验,我只懂得付钱面试
public class people{ public void buy(){ System.out.println("付钱"); } }
但我又不想向下面代码那样去主动学怎么找房子、怎么办理相关手续。设计模式
public void buy(){ System.out.println("找房子"); System.out.println("付钱"); System.out.println("办理手续"); }
那这个时候我该怎么办呢?简单,有问题找中介,我只要会付钱,其余的东西中介帮我搞定。这个中介就是一个代理对象,在不改变“我”这个目标对象的功能的状况下,用代理对象的方式实现功能扩展。ide
想要实现以上的需求有三种方式,也就是java的三种代理模式:静态代理、动态代理(JDK代理)、Cglib代理。函数
public interface IPeople{ void buy(); }
/** * 目标对象实现了某一接口 */ public class People implements IPeople{ public void buy(){ System.out.println("付钱"); } }
/** * 代理对象(中介)和目标对象(我)实现相同的接口 */ public class PeopleProxy implements IPeople{ // 接收目标对象,以便调用buy方法 private IPeople target; public PeopleProx(IPeople target){ this.target = target; } // 对目标对象的buy方法进行功能扩展 public void buy() { System.out.println("找房子"); target.buy(); System.out.println("办理手续"); } }
/** * 测试类 */ public class Test { public static void main(String[] args) { //目标对象 IPeople target = new People(); //代理对象 IPeople proxy = new PeopleProxy(target); //执行的是代理的方法 proxy.buy(); } }
总结:静态代理业务类只须要关注业务逻辑自己,保证了业务类的重用性。代理对象的一个接口只服务于一种类型的对象,若是要代理的方法不少,须要为每一种方法都进行代理,静态代理在程序规模稍大时就没法胜任。若是接口增长一个方法,除了全部实现类须要实现这个方法外,全部代理类也须要实现此方法,增长了代码维护的复杂度工具
public interface IPeople{ void buy(); }
/** * 目标对象实现了某一接口 */ public class People implements IPeople { public void buy(){ System.out.println("付钱"); } }
/** JDK动态代理的实现原理大概流程 一、为接口建立代理类的字节码文件 二、使用ClassLoader将字节码文件加载到JVM 三、建立代理类实例对象,执行对象的目标方法 **/ public class JdkProxyTest { public static void main(String[] args) { People target = new People(); IPeople proxy = (IPeople) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object o, Method method, Object[] objects) throws Throwable { System.out.println("找房子"); //执行目标对象方法 Object returnValue = method.invoke(target, objects); System.out.println("办理手续"); return returnValue; } } ); proxy.buy(); } }
总结:动态代理与静态代理相比较,最大的好处是接口中声明的全部方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler invoke)。这样,在接口方法数量比较多的时候,能够进行灵活处理,而不须要像静态代理那样每个方法进行中转。并且动态代理的应用使类职责更加单一,复用性更强。测试
/** * 目标对象不用实现接口 */ public class People { public void buy(){ System.out.println("付钱"); } }
/** * Cglib子类代理工厂 **/ public class ProxyFactory implements MethodInterceptor { //维护目标对象 private Object target; public ProxyFactory(Object target){ this.target = target; } // 给目标对象建立一个代理对象 public Object getProxyInstance(){ //1.工具类 Enhancer en = new Enhancer(); //2.设置父类 en.setSuperclass(target.getClass()); //3.设置回调函数 en.setCallback(this); //4.建立子类(代理对象) return en.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("找房子"); Object returnValue = method.invoke(target,objects); System.out.println("办理手续"); return returnValue; } }
public class Test { public static void main(String[] args) { //目标对象 People target= new People(); //代理对象 People targer =(People) new ProxyFactory(target).getProxyInstance(); //执行代理对象的方法 targer.buy(); } }
总结:静态代理和动态代理都须要目标对象实现接口,而Cglib代理则不须要这么麻烦。最后,顺便提下关于Cglib的问题,若是目标对象有不一样方法,须要实现不一样代理,那么代码须要怎么实现呢?this
特此补充:在sping中JDK代理和cglib代理混合使用,若是被代理对象实现了接口,就优先使用JDK代理,若是没有实现接口,就用用cglib代理spa