本文共 6385 字,大约阅读时间需要 21 分钟。
按照代理的创建时期,代理类可以分为两种:
静态:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。
动态:在程序运行时运用反射机制动态创建而成。
先来看看静态代理的实现吧?
/** * @ClassName Star * @Description 公共接口 * @Author lzq * @Date 2019/8/1 10:39 * @Version 1.0 **/public interface Star { public void sell(); //卖商品}
/** * @ClassName RealStar * @Description 真实对象 委托类 * @Author lzq * @Date 2019/8/1 10:39 * @Version 1.0 **/public class RealStar implements Star{ @Override public void sell() { System.out.println("卖商品"); }}
/** * @ClassName StarProxy * @Description 代理类 * @Author lzq * @Date 2019/8/1 10:38 * @Version 1.0 **/public class StarProxy implements Star{ private RealStar realStar = new RealStar(); @Override public void sell() { realStar.sell(); }}
测试代码,我们只需要和委托类交互即可,不必管它的底层实现:
/** * @ClassName Test * @Description 测试 * @Author lzq * @Date 2019/8/1 11:08 * @Version 1.0 **/public class Test { public static void main(String[] args) { StarProxy proxy = new StarProxy(); proxy.sell(); }}
运行结果:
卖商品
其实,动态代理也是这么个原理,只不过它的创建对象的方式是通过反射来完成的,
在Java中,实现动态代理的技术有很多,比如JDK自带的、CGLIB、Javassist、ASM等,其中最常用的就是JDK、CGLIB,下面重点解释这两种;
上面我们用静态代理的时候,是定义一个公共接口,然后让委托类和代理类分别去实现这个接口(相当于后面说的,代理对象和真实对象都是挂在在这个公共接口下面的),最后在代理类里面定义一个委托类的对象,通过底层去调用委托类的相应的方法去实现代理逻辑的,在动态代理里面也是一样,也需要一个公共接口,一个已经实现了的委托类,但是不同的是,它的代理类是根据接口、委托类动态生成的;
首先我们定义一个接口HelloWorld:
/** * @ClassName HelloWorld * @Description 公共接口 * @Author lzq * @Date 2019/8/1 11:26 * @Version 1.0 **/public interface HelloWorld { public void sayHelloWorld();}
定义一个委托类实现这个接口:
/** * @ClassName HelloWorldImpl * @Description 委托类 真实对象类 * @Author lzq * @Date 2019/8/1 11:27 * @Version 1.0 **/public class HelloWorldImpl implements HelloWorld{ @Override public void sayHelloWorld() { System.out.println("Hello World"); }}
在JDK动态代理中,要实现代理逻辑必须去实现java.lang.reflect.InvocationHandler
接口,它里面定义了一个invoke方法,并提供接口数组用于挂在代理对象,下面这个类主要实现动态代理绑定和代理逻辑实现:
import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;/** 1. @ClassName JDKProxyExample 2. @Description 动态代理逻辑代码 3. @Author lzq 4. @Date 2019/8/1 11:28 5. @Version 1.0 **/public class JDKProxyExample implements InvocationHandler { private Object object = null; //真实对象 委托类对象 /** * 获取代理对象 * @param o * @return */ public Object bind(Object o) { this.object = o; return Proxy.newProxyInstance(object.getClass().getClassLoader(),o.getClass().getInterfaces(),this); } /** * 代理方法逻辑 * @param proxy * @param method * @param args * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("进入代理逻辑前的处理..."); System.out.println("调用真实对象前的服务..."); Object obj = method.invoke(object,args); System.out.println("调用真实对象之后的服务..."); return obj; }}
在这个类里面,我们需要:
1、建立代理对象和真实对象之间的关系
在这个类里面,我们是提供bind方法实现的,方法里面首先用类的属性object保存了真实对象,然后通过如下代码建立并生成代理对象:
Proxy.newProxyInstance(object.getClass().getClassLoader(),o.getClass().getInterfaces(),this);
这三个参数的意思是:
2、实现代理逻辑方法
invoke方法可以实现代理逻辑,它的三个参数含义如下:
当我们使用代理对象调度方法之后,它就会进入到invoke方法里面:
Object obj = method.invoke(object,args); //method.invoke(" 要调用的方法的名字所隶属的对象实体",方法的参数值);
这行代码相当于调度真实对象的的方法,只不过是通过发射实现而已。
这些各部分的关系就相当于:
测试代码如下:
/** * @ClassName Test * @Description 测试代码 * @Author lzq * @Date 2019/8/1 11:32 * @Version 1.0 **/public class Test { public static void main(String[] args) { JDKProxyExample jdk = new JDKProxyExample(); HelloWorld proxy = (HelloWorld)jdk.bind(new HelloWorldImpl()); proxy.sayHelloWorld(); }}
那么其实动态代理的好处在哪呢?因为它的代理对象都是通过反射动态生成的,如果需要新的功能,那么只需要在公共接口中添加方法,真实对象实现该方法即可,动态代理绑定和代理逻辑根本就不需要动;
测试代码运行结果:
JDK动态代理必须提供公共接口才可以使用,在一些不能提供接口的环境中,只能采用其他的第三方技术,比如CGLIB动态代理,它的优势在于不需要提供公共接口,只要一个非抽象类就能实现动态代理;
CGLIB依赖:
cglib cglib 3.2.2
代码如下:
import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;/** * @ClassName CglibProxyExample * @Description * @Author lzq * @Date 2019/8/1 12:12 * @Version 1.0 **/public class CglibProxyExample implements MethodInterceptor { /** * 生成CGLIB代理对象 * @param cls * @return */ public Object getProxy(Class cls) { //CGLIB Enhancer 增强类对象 Enhancer enhancer = new Enhancer(); //设置增强类型 enhancer.setSuperclass(cls); //带你一代理逻辑对象为当前对象,这就要求当前对象实现MethodInterceptor的方法intercept enhancer.setCallback(this); //生成并返回代理对象 return enhancer.create(); } /** * 代理逻辑方法 * @param o 代理对象 * @param method 方法 * @param objects 方法参数 * @param methodProxy 方法代理 * @return 代理逻辑返回 * @throws Throwable 异常 */ @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("调用真实对象之前的处理逻辑..."); Object result= methodProxy.invokeSuper(o,objects); System.out.println("调用真实对象之后的处理逻辑"); return result; }}
这里用到了CGLIB的加强者类Enhancer,通过设置超类的方法(setSuperclass),然通过setCallback方法设置哪个类为它的代理类,其中,参数this就意味着当前对象,那就要求this这个对象实现接口MethodInterceptor的方法——intercept,然后返回代理对象;
那么此时当前类的intercept方法就是代理逻辑方法,其参数含义见代码注解;
再写一个真实对象类;
/** * @ClassName Test * @Description 委托类 真实对象类 * @Author lzq * @Date 2019/8/1 12:28 * @Version 1.0 **/public class Test { public void say(String x) { System.out.println(x); }}
测试代码:
public static void main(String[] args) { CglibProxyExample cpe = new CglibProxyExample(); Test test = (Test)cpe.getProxy(Test.class); test.say("你好"); }
运行结果:
动态代理的实现其实都很相似,它们都是用getProxy方法生成代理对象,制定代理的逻辑类,而代理逻辑类要实现一个接口的一个方法,那么这个接口定义的方法就是代理逻辑方法,它可以控制真实对象的方法;
JDK动态代理和CGLIB动态代理的区别:
在JDK的动态代理里面,我们可以看到:
那它生成的代理对象和真实对象是同级的,都是公共接口下的实现类,它们是横向关系;再来看看CGLIB的:
它的原理是生成一个代理对象来拦截真实对象的方法,但这个代理对象却是真实对象的子类,继承关系,所以在CGLIB里面,代理对象是真实对象的子类,它们是纵向关系;转载地址:http://cxtbn.baihongyu.com/