MyBATIS插件原理第一篇——技术基础(反射和JDK动态代理)

在介绍MyBATIS插件原理前我们需要先学习一下一些基础的知识,否则我们是很难理解MyBATIS的运行原理和插件原理的。

MyBATIS最主要的是反射和动态代理技术,让我们首先先熟悉它们。

1:Java反射技术

在Java中反射技术已经大行其道,通过不断的优化性能得到了巨大的提高,而反射技术使得Java的可配置性大大提高。让我们来写一个服务打印hello + 姓名。

    import java.lang.reflect.InvocationTargetException;
     
    import java.lang.reflect.Method;
     
    public class ReflectService {
     
        /**
         * 服务方法
         * @param name -- 姓名
         */
     
        public void sayHello(String name) {
     
            System.err.println("hello " + name);
     
        }
     
        
     
        /**
         * 测试入口.
         * @param args
         */
     
        public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException,
            InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
     
            //通过反射创建ReflectService对象.
     
            Object service = Class.forName("ReflectService").newInstance();
     
            //获取服务方法——sayHello
     
            Method method = service.getClass().getMethod("sayHello", String.class);
     
            //反射调用方法.
     
            method.invoke(service, "ykzhen2015");
     
        }
     
    }


        这段代码是一段简易的通过反射技术去创建ReflectService对象,并且通过获取方法通过反射调用。反射调用的最大好处是我们可以配置性大大提高,就如同Spring IOC容器一样,我们可以给很多配置,设置参数,配置调用那个方法提供服务等,使得Spring能够顺利跑起来,这大大提高了Java的灵活性,对于反射笔者不再做详细的介绍,需要了解的读者可以参考其它书籍和文档。


2、JDK动态代理

这个动态代理还是不好理解的,我们先用一张图来表达它:


好,它的意义是在我们访问一个对象的时候,动态代理可以给我们生成一个代理对象【占位】,它的作用是可以控制【真实对象】的访问。

比方说,你是未成年人,我可能是是去访问你父母,由你父母来代替你访问你,那么你父母就是这个占位,而你就是真实对象。

在JDK动态代理的要求是对象存在接口,所以真正的服务对象必须存在接口。

假设我们现在有这样的一个服务,我给名字它会说: “hello ”+名字。

那么我们先看看这个接口:

    public interface HelloService {
        
        public void sayHello(String name);
        
    }


非常简单的接口,然后我们给出实现类:

    public class HelloServiceImpl implements HelloService{
        @Override
        public void sayHello(String name) {
            System.err.println("hello " + name);
        }
    }


好,它实现了这个接口,并且实现了接口的方法。

现在我们要变变,我希望

在说:hello+name前打印:我准备说hello。

之后打印:我说过hello了

如果采用我们的代理对象【占位】显然实现起来非常容易,于是我们首先来生成这个【占位】

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
     
    public class HelloServiceProxy implements InvocationHandler {
     
     
        private Object target;  
        /**
         * 绑定委托对象并返回一个【代理占位】
         * @param target 真实对象
         * @return  代理对象【占位】
         */  
        public  Object bind(Object target, Class[] interfaces) {  
            this.target = target;  
            //取得代理对象  
           <span style="color:#FF0000;"> return Proxy.newProxyInstance(target.getClass().getClassLoader(),  
                    target.getClass().getInterfaces(), this);  </span>
        }  
      
        @Override  
        /**
         * 同过代理对象调用方法首先进入这个方法.
         * @param proxy --代理对象
         * @param method -- 方法,被调用方法.
         * @param args -- 方法的参数
         */
        public Object invoke(Object proxy , Method method, Object[] args) throws Throwable {  
            System.err.println("############我是JDK动态代理################");
            Object result = null;  
            //反射方法前调用
            System.err.println("我准备说hello。");  
            //反射执行方法  相当于调用target.sayHelllo;
            result=method.invoke(target, args);
            //反射方法后调用.
            System.err.println("我说过hello了");  
            return result;  
        }  
    }


这里的bind方法里面有句:

    Proxy.newProxyInstance(target.getClass().getClassLoader(),  
                    target.getClass().getInterfaces(), this);

它的意义就是生成一个代理对象,这里有三个参数:第一个参数是类加载器,第二个参数是或者对象的接口(代理对象挂在那个接口下),第三个参数this代表当前HelloServiceProxy类,换句话说是使用HelloServiceProxy作为对象的代理。

一旦这样绑定后,那么在进入代理对象方法调用的时候就会到HelloServiceProxy的invoke方法上,invoke方法有三个参数:第一个proxy是代理对象,第二个是当前调用那个方法,第三个是方法的参数。

比方说,现在HelloServiceImpl对象(obj)用bind方法绑定后,返回其【占位】(proxy),我们在调用proxy.sa6yHello("张三"),那么它就会进入到HelloServiceProxy的invoke()方法。而invoke参数中第一个便是代理对象proxy,方法便是sayHello,参数是"张三"。那么我们就可以通过反射技术调度真实对象的方法。


现在让我们看看测试代码:

    public class ProxyTest {
        
        public static void main(String[] args) {
            HelloServiceProxy proxy = new HelloServiceProxy();
            HelloService service = new HelloServiceImpl();
            //绑定代理对象。
            service = (HelloService) proxy.bind(service, new Class[] {HelloService.class});
            //这里service经过绑定,就会进入invoke方法里面了。
            service.sayHello("张三");
        }
    }


好,我们运行一下,看一下结果。

############我是JDK动态代理################
我准备说hello。
hello 张三
我说过hello了


好了,我们这样就可以看到动态代理。


正确的理解反射和动态代理是我们进行MyBATIS底层的基础,尤其是动态代理,如果还不了解务必要多做上面的例子,切实理解好。
 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值