MyBtis(二)—— 动态代理

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/QQ2899349953/article/details/97847550

概念

按照代理的创建时期,代理类可以分为两种:

静态:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.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);

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

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

  • 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();
        proxy.sayXXX();
    }
}

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

测试代码运行结果:
在这里插入图片描述

CGLIB动态代理

JDK动态代理必须提供公共接口才可以使用,在一些不能提供接口的环境中,只能采用其他的第三方技术,比如CGLIB动态代理,它的优势在于不需要提供公共接口,只要一个非抽象类就能实现动态代理;

CGLIB依赖:

        <!--CGlib动态代理-->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.2</version>
        </dependency>

代码如下:

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里面,代理对象是真实对象的子类,它们是纵向关系;

猜你喜欢

转载自blog.csdn.net/QQ2899349953/article/details/97847550