java框架SSM学习——持久层Mybatis之动态代理模式

java的动态代理模式有哪些?

①JDK动态代理
②CGLIB动态代理
③Javassist
④ASM

那我们需要学习哪个?

我在本目录下我只讲解JDK动态代理CGLIB动态代理

1.JDK动态代理

此动态代理是JDK自带的功能。我通俗点讲,里面有名词:调用者,真实对象和代理对象

比如在一个软件公司里,有客户来找此公司设计软件,但是首先接待客户的并不是真正的软件工程师,而是专门负责谈商务业务的人,来问你的需求以及各项事宜,包括讲完需求后需要的薪资、以及签订什么样的合同之类的,也包括失败以后是否终止合作。那么讲完这些,达成协议以后,商务人员才会拿着需求给到相应的软件工程师。然后软件工程师才会进行开发设计,将需求完成以后再将设计好的软件给商务人员拿给客户,然后拿到最后的报酬。
在这个过程里,客户就相当于动态代理里的调用者,商务人员就相当于动态代理里的代理对象,软件工程师相当于动态代理里的真实对象。在以下图中可以明确一点他们的关系
在这里插入图片描述
客户并不会直接去和软件工程师去接触,而是通过一个谈判者(商务人员)去进行交涉,最后谈判者(商务人员)再把需要的东西给真实对象(软件工程师)去完成。这个过程就形象的描述出了JDK动态代理的思想。
再通俗点讲就是代理对象虽然不是真实对象,但是代理对象拥有真实对象的方法,我可以用代理对象去完成你调用者的需求,但是你并不用知道我是如何去完成的,我可以去给真实对象完成,然后再回去给你返回值,仅此而已。然后我们用简单的代码去了解一下底层是如何实现的

先建立一个接口类
public interface HelloWorld {
    void printHello();
}

再写出接口的实现类
public class HelloWorldImpl implements HelloWorld{

    public void printHello() {
        System.out.println("heyman");
    }
}

这个时候就可以动态代理了。先要建立起代理对象和真实对象的关系,以及代理逻辑。

在JDK动态代理里,一定要实现的接口是InvocationHandle,里面定义了invoke方法
import org.junit.Test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//实现接口InvocationHandler
public class ProxyJDK implements InvocationHandler {
    //声明一个真实对象
    private Object realTarget;

    //此方法用来使用代理对象绑定真实对象
    /*
        realTarget为需要绑定的真实对象(比如接口实现类的对象)
        return的是一个Object Proxy对象,newProxyInstance方法为实例化
        realTarget.getClass().getClassLoader()为获取真实对象的类加载器
        realTarget.getClass().getInterfaces()为获取真实对象的下挂接口(下挂接口是所有的接口)
        this为当前对象,也就是传入的真实对象
     */
    public Object bind(Object realTarget){
        this.realTarget = realTarget;
        return Proxy.newProxyInstance(realTarget.getClass().getClassLoader() ,
                realTarget.getClass().getInterfaces() , this);
    }

    //此方法是实现InvokationHandler接口里的invoke方法
    /*
        Object proxy代表在bind方法中返回的Object对象(Proxy)
        Method method代表需要调度的方法
        Object[] args代表方法需要的方法
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("在调度真实方法前");
        Object object = method.invoke(realTarget , args);
        System.out.println("在调度真实方法后");
        return object;
    }

    //测试方法
    /*
        声明ProxyJDK对象拿到两个方法,用代理对象helloWorld去调用真实对象的方法
        先将代理对象绑定于HelloWorldImpl对象,然后在内部invoke需要调用的printHello方法
     */
    @Test
    public void testJdkProxy(){
        ProxyJDK proxyJDK = new ProxyJDK();
        HelloWorld helloWorld = (HelloWorld) proxyJDK.bind(new HelloWorldImpl());
        helloWorld.printHello();
    }
}

在里面我已经讲的很详细的,全部都写在了注释上。到此JDK动态代理就完成了。
最后测试完的结果:
在这里插入图片描述
可以看到invoke方法的执行顺序和输出是一样的。

说明一下,如果看不懂代码的建议回头复习一下基础编程里的反射。其中过程也就是创建接口和实现类,我们并不是直接调用实现类去完成需要的方法,而是通过实现类的接口用绑定的方法与实现类绑定起来,然后用类的接口去调用方法,这是一个很大的区别,希望注意一下。如果第一遍看不懂建议可以看多几遍,我在刚开始也是不能看懂,但是可以先放一放,学习后面的知识,学到一定的程度,返回来再看,就会有一种醍醐灌顶的感觉。最好是跟着我的思路来打一遍,有助于理解。

2.CGLIB动态代理

其实能理解JDK动态代理,对于CGLIB动态代理来说就显得很简单了。首先要知道CGLIB动态代理和JDK动态代理有什么区别?为什么要分离出两个动态代理方法?有时候一些类并没有接口,那么没有接口还怎么用JDK动态代理去生存代理对象呢?那就可以用CGLIB动态代理实现,它是不需要接口去实现动态代理的。CGLIB属于第三方技术,所以需要导入jar包才能使用。现在用代码显示一下

因为是第三方技术,需要在Maven中导入CGLIB的jar包

在dependencies中加入这些配置:

<dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2.2</version>
        </dependency>

然后就能使用到CGLIB了

写一个PeopleSayHello类
public class PeopleSayHello {
    public void sayHello(String name){
        System.out.println(name + ",hello");
    }
}
然后建立CGLIB类
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.junit.Test;

import java.lang.reflect.Method;

public class CglibProxy implements MethodInterceptor {
    //利用类获取类的CGLIB的代理对象
    /*
        参数c为传入的类
        return为返回生成的CGLIB代理对象
        enhancer实例对象是CGLIB的增强类对象
        setSuperclass是设置增强类型,比如增强为你传入的类的类型
        setCallback为设置当前对象为代理逻辑对象
     */
    public Object getProxy(Class c){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(c);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    //接口MethodInterceptor要实现的方法intercept
    /*
        o代表由getProxy生成的代理对象
        method代表要调用的方法
        objects代表调用方法的参数
        methodProxy代表方法的代理
        CGLIB调用代理方法的形式:methodProxy.invokeSuper(o , objects);
     */
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("调用CGLIB前");
        Object result = methodProxy.invokeSuper(o , objects);
        System.out.println("调用CGLIB后");
        return result;
    }

    //测试方法
    /*
        先生成CGLIB的实例对象
        将真实对象的代理对象实例出来
        然后用代理对象调用方法
     */
    @Test
    public void testCGLIBProxy(){
        CglibProxy cglibProxy = new CglibProxy();
        PeopleSayHello peopleSayHello = (PeopleSayHello) cglibProxy.getProxy(PeopleSayHello.class);
        peopleSayHello.sayHello("帅哥");
    }
}

关于里面的参数以及细节,都在注释中解释清楚了。
测试结果:
在这里插入图片描述

其实关于CGLIB动态代理的方法和JDK动态代理理解即可,因为在Mybatis中以及封装好了相关的代码,在Mybatis中你可以直接用SqlSession的getMapper方法去创建一个接口类的代理对象。但是实际的原理以及如何实现的,还是得知道才能做到毫无疑问的走向下一步,而以后接触到底层的源码也肯定会看到这些代码。到时候就不会一头雾水这是什么,而是恍然大悟原来封装在了这里。Mybatis既允许你直接创建一个接口类的代理对象去操作方法,也允许你写实现类去操作方法,要如何去使用还是得看到时候是什么需求再判断。好了动态代理的两种方法我已经讲完了。如果看不懂,没关系,先往后学习,学完持久层Mybatis的时候再返回来看,也就有新的理解了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值