我们来谈谈什么是动态代理。假设这样一个场景:
你们是一家软件公司,你是一位软件工程师。现在又客户带着一个需求来找你们公司做项目。显然不是找你直接谈,而是去找商务去谈,此时客户就会认为商务就代表你们公司。然后当客户与商务交流的差不多了,商务就会来找你接洽。显然商务与工程师之间是代理与被代理的关系。
Spring中通常用JDK和CGLIB。Mybatis中还是用了javassist
- JDK与CGLIB
jdk动态代理中,我们必须使用接口。而cglib则不需要。
因为在JDK动态代理中,需要这个接口作为参数传入。Proxy.newProxyInstance(···, obj.getClass().getInterfaces(),···);
下面我们讨论梁中最常用的代理:
JDK动态代理:
他必须通过接口才能生成代理对象,首先我们先生成一个接口
package com.rick.proxy;public interface Hello {public void sayHello();}
然后提供接口的实现类
package com.rick.proxy;public class HelloImpl implements Hello{@Overridepublic void sayHello() {System.out.println("say Hello......");}}
此时我们就可以开始动态代理了,分为两个步骤:
- 首先建立起代理对象和真是对象的关系
- 然后实现代理逻辑package com.rick.proxy;
import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class JdkProxyExample implements InvocationHandler{//真对象private Object target = null;//创建代理和真实对象的代理关系,并返回对象public Object bind(Object target){this.target = target;return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);/** 第一个参数是累的加载器,我们采用了target本身的类加载器* 第二个参数是吧生成的动态代理对象挂在哪些接口下面* 第三个表示定义实现方法逻辑的代理类,this表示当前对象他必须* 实现InvocationHandler的invoke方法,他就是代理逻辑方法的实现方法*/}//实现代理逻辑@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {/** proxy为代理对象,就是bind方法生成的对象* method为当前调度的方法* args为调度方法的参数*/System.out.println("进入代理逻辑方法");System.out.println("在调度真实对象之前。。");Object obj = method.invoke(target, args);//相当于调用sayHello方法System.out.println("在调度真实对象之后。。");return obj;}}
类比前面的例子,proxy相当于商务对象,target相当于软件工程师对象,bind方法就是建立商务和软件工程师代理关系的方法。而invoke就是商务逻辑,他控制软件工程师的访问
测试
package com.rick.proxy;public class Test {public static void main(String[] args) {JdkProxy();}private static void JdkProxy() {JdkProxyExample jdkProxyExample = new JdkProxyExample();Hello proxy = (Hello) jdkProxyExample.bind(new HelloImpl());proxy.sayHello();}}
CGLIB动态代理:
jkd动态代理必须提供接口才能使用,在一些不能提供接口的场景中,只能采取第三方技术。比如CGLIB动态代理。它的优势在于不需要提供接口,只要一个非抽象类就能实现动态代理。
例如:
package com.rick.proxy;public class UserImpl {public void sayHello(String name) {System.out.println("Hello:"+name);}}
他不存在实现任何借口,所以没有办法使用JDK动态代理,之力就要采用CGLIB动态代理
package com.rick.proxy;import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;public class CglibProxyExample implements MethodInterceptor{public Object getProxy(Class cls) {// CGLIB enhancer 增强类对象Enhancer enhancer = new Enhancer();// 设置增强类型enhancer.setSuperclass(cls);// 定义代理逻辑对象为当前对象,要求当前对象实现MethodInterceptor方法enhancer.setCallback(this);//生成并返回代理对象return enhancer.create();}@Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("调用真是对象前");// CGLIB反射调用真实对象方法Object result = methodProxy.invokeSuper(proxy, args);System.out.println("调用真实对象后");return result;}}
创建测试:
package com.rick.proxy;public class Test {public static void main(String[] args) {// JdkProxy();CGLIBProxy();}// private static void JdkProxy() {// JdkProxyExample jdkProxyExample = new JdkProxyExample();// Hello proxy = (Hello) jdkProxyExample.bind(new HelloImpl());// proxy.sayHello();// }public static void CGLIBProxy() {CglibProxyExample cglibProxyExample = new CglibProxyExample();UserImpl proxy = (UserImpl) cglibProxyExample.getProxy(UserImpl.class);proxy.sayHello("zhangsan");}}
