博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
MyBtis(二)—— 动态代理
阅读量:3677 次
发布时间:2019-05-21

本文共 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,下面重点解释这两种;

JDK动态代理

上面我们用静态代理的时候,是定义一个公共接口,然后让委托类和代理类分别去实现这个接口(相当于后面说的,代理对象和真实对象都是挂在在这个公共接口下面的),最后在代理类里面定义一个委托类的对象,通过底层去调用委托类的相应的方法去实现代理逻辑的,在动态代理里面也是一样,也需要一个公共接口,一个已经实现了的委托类,但是不同的是,它的代理类是根据接口、委托类动态生成的;

首先我们定义一个接口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);

这三个参数的意思是:

  • 第一个是类加载器,就是要加载这个代理对象用哪个类加载器加载,这里我们用的是加载真实对象的类的类加载器;
  • 第二个是把生成的代理对象下挂在哪些接口下面,这个写法就是放在object实现的接口下,HelloWorldImpl对象的接口显然就是HelloWorld;
  • 第三个是定义实现方法逻辑的代理类,this表示当前对象,它必须实现InvocationHandler接口中的invoke方法,它就是代理逻辑方法的实现方法;

2、实现代理逻辑方法

invoke方法可以实现代理逻辑,它的三个参数含义如下:

  • proxy,代理对象,就是bind方法生成的对象;
  • method,当前调度的方法;
  • args,调度方法的参数;

当我们使用代理对象调度方法之后,它就会进入到invoke方法里面:

Object obj = method.invoke(object,args); //method.invoke(" 要调用的方法的名字所隶属的对象实体",方法的参数值);

这行代码相当于调度真实对象的的方法,只不过是通过发射实现而已。

这些各部分的关系就相当于:

  • proxy 相当于商务对象,代理对象的意思;
  • object 相当于软件工程师对象,真实对象,委托者;
  • bind 这个方法就是建立商务和软件工程师代理关系的方法;
  • 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();    }}

那么其实动态代理的好处在哪呢?因为它的代理对象都是通过反射动态生成的,如果需要新的功能,那么只需要在公共接口中添加方法,真实对象实现该方法即可,动态代理绑定和代理逻辑根本就不需要动;

测试代码运行结果:

在这里插入图片描述

CGLIB动态代理

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/

你可能感兴趣的文章
木马工作原理——病毒木马 002
查看>>
mysql安装和配置ODBC驱动,然后tableau链接MySQL数据库
查看>>
物联网之智能灯开发-前言
查看>>
物联网之智能灯-Django(一)
查看>>
使用计算机视觉技术进行工业品质检测
查看>>
Java重要知识点——方法的定义
查看>>
LinkedHashSet的使用
查看>>
JUC - 线程池:
查看>>
JUC - Java8流式编程
查看>>
JavaWeb框架 - Spring注解部分:
查看>>
SpringBoot使用外部的Tomcat: bean with name 'defaultValidator' defined in class path resource
查看>>
SpringBoot上传文件413问题:
查看>>
Java多线程 - AQS简单实现:
查看>>
建造者模式:
查看>>
适配器模式:
查看>>
美团Java一面面经
查看>>
疏漏总结(九)——http与https
查看>>
疏漏总结(十)
查看>>
线程池
查看>>
servlet的其他作用,git的使用方法
查看>>