在Spring中,严格的来讲不仅仅使用JDK的动态代理,还会使用CGLIB动态代理,可是它们大同小异,咱们这里讲JDK的动态代理,有了它的理解CGLIB也不难掌握,读者能够阅读其余的文档。java
首先,让咱们先了解什么是动态代理,假设读者是一个软件工程师,而你的公司是家软件公司,我是一个客户,我须要你公司提供软件的专业服务。显然我是找到大家公司的商务,而不是你去讨论个人要求。那么商务就等同于你的一个代理,我也会认为商务等于大家公司,而不会去管你这个软件工程师如何工做的。bash
代理的实际就是在真实服务对象(软件工程师)以前加多一个占位或者叫作代理对象(商务),这个占位(商务)能够根据调用者(客户)的要求去控制真实服务对象的访问(软件工程师)。有了这个比喻是否是好理解不少??ide
那么代理模式的好处在于什么?首先占位(商务)能够在真实服务对象以前以后作一些服务,同时根据须要选择是否须要启用真实服务对象,也会加入一些规则,好比商务也能够根据客户和公司的规则来提供额外的服务,这时可能就连真实服务对象(软件工程师)都不会启用。测试
好,上面的东西貌似很神奇,咱们用一张图来表达代理的含义。this
好,这里的代理对象就是咱们以前谈到的占位,它代理了咱们看到的真实对象,能够在真实对象以前以后,甚至是代替真实对象提供服务。代理
动态代理有好几种,Spring使用了CGLIG和JDK动态代理。在JDK动态代理中,要求必须提供接口,而CGLIB是不须要的,咱们这里只谈论JDK动态代理,在大部分的状况下,笔者建议你使用JDK动态代理,由于JDK动态代理的速度要比CGLIB要快,在Spring中一个有切面的Bean若是有接口声明,Spring就会用JDK动态代理代理它,否者启用CGLIB。code
好了论述讲了一大截,咱们开始讲JDK动态代理,首先咱们须要提供一个简单的接口:对象
public interface HelloService { public void sayHello(String name); }
跟着是实现类:接口
public class HelloServiceImpl implements HelloService { @Override public void sayHello(String name) { System.err.println("hello " + name); } }
咱们跟着就是要生成代理对象(proxy),分红两步: 成代理对象要创建代理对象(proxy)和真实对象(HelloServiceImpl)的代理关系。 实现代理方法 在JDK动态代理中须要实现接口:java.lang.reflect.InvocationHandler。文档
public class HelloProxy implements InvocationHandler { private Object target; /** * 生成代理对象,并和真实服务对象绑定. * @param target 真实服务对线下 * @return 代理对象 */ public Object bind(Object target) { this.target = target; //生成代理对象,并绑定. Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); //指明代理类,this表明用当前类对象,那么就要求其实现InvocationHandler接口 return proxy; } /** * 当生成代理对象时,第三个指定使用HelloProxy进行代理时,代理对象调用的方法就会进入这个方法。 * @param proxy ——代理对象 * @param method -- 被调用的方法 * @param args -- 方法参数 * @return 代理方法返回。 * @throws Throwable -- 异常处理 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.err.println("反射真实对象方法前"); Object obj = method.invoke(target, args);//至关于sayHello方法调用. System.err.println("反射真实对象方法后"); return obj; } }
首先声明了一个类的属性,它的做用是保存真实服务对象。
而后用bind方法绑定代理对象和真实对象,是经过这样去绑定的:
Object proxy = Proxy.newProxyInstance( target.getClass().getClassLoader(), //类的加载器 target.getClass().getInterfaces(), //对象的接口,明确代理对象挂在哪些接口下 this );//指明代理类,this表明用当前类对象,那么就要求其实现InvocationHandler接口
这样声明就会进入当前类invoke方法,它有三个参数:
Object proxy ——当前代理对象
Method method —— 当前调度的方法
Object[] args -- 方法参数 而后咱们经过反射调度真实对象的方法,不懂的能够看到第一篇的论述(连接描述)
Object obj = method.invoke(target, args);//至关于sayHello方法调用. 让咱们测试一下这段代码:
public class Chapter1Main { public static void main(String[] args) { HelloProxy helloProxy = new HelloProxy(); //由于使用了接口HelloService绑定了代理对象,因此能够用HelloService做为代理对象的声明. HelloService proxy = (HelloService) helloProxy.bind(new HelloServiceImpl()); proxy.sayHello("张三");//此时使用代理对象运行方法进入HelloProxy的invoke方法里 } }
这和时候,咱们运行一下能够获得下面的打印
反射真实对象方法前 hello 张三 反射真实对象方法后
好了,咱们看到的HelloService实际已是一个代理对象了,而不是咱们普通人看到的HelloServiceImpl这个真实对象,在Spring中不要被它迷糊了哦。