美文网首页MyBatis+SpringMVC+SpringBootSpringBoot精选
深入浅出MyBatis:反射和动态代理基础

深入浅出MyBatis:反射和动态代理基础

作者: 情情说 | 来源:发表于2018-03-28 22:31 被阅读30次

    前三篇详细总结了Mybatis的基本特性、常用配置、映射器,相对于Hibernate,映射器的配置相对复杂,但有很好的灵活性和扩展性,可以应对各种业务场景。熟练掌握这些内容,可以流畅的使用MyBatis进行开发了。

    后面准备介绍MyBatis的解析和运行原理以及自定义插件,今天看了书籍的这部分,都会涉及到反射和动态代理这些基础,本篇文章总结下这些,便于理解原理。

    通过本篇的介绍,你会了解到:

    • 反射和动态代理是解决什么问题的
    • Class对象
    • 反射能做什么
    • 动态代理的实现方式:JDK动态代理、CGLIB

    理解反射和动态代理

    反射

    首先看看官网对反射的定义:

    可以通过java代码,获取当前加载类的字段、方法、构造函数等信息,并在安全限制内,使用反射字段、方法、构造函数进行操作。

    简单来说,可以在运行时获得程序中每一个类型的成员信息。程序中定义的对象,其类型都是在编译期确定的,而反射可以动态地创建对象,并访问或调用其成员。

    动态代理

    所谓代理,是一个人或组织代替另一个人或组织做事,主要有3个角色:访问者、代理人、被代理人,访问者经由代理人,与被代理人交互,中间会加入一些自己的处理。

    图片来自互联网

    所谓的动态代理,是说在编译时不需要定义代理类,而是在运行时创建,这个是关键:在运行时创建代理类。

    Class对象

    Class类是一个实实在在的类,存在于java.lang包中,用来表示运行时类型信息。Class对象表示自定义类的类型信息,比如创建一个User类,JVM就会创建一个User对应的Class对象,保存User类相关的类型信息,该对象保存在jvm堆中,作为访问方法区中User类型信息的接口。

    在使用自定义类时,会首先检查这个类的Class对象是否已经加载,如果没有加载,默认的类加载器就会先根据类名查找.class文件,Class对象就会被加载到内存。

    可以通过下面3种方法获取Class对象:

    • 使用Class类的forName静态方法;
    • 直接获取某一个对象的class;
    • 调用某个对象的getClass()方法;

    Class对象是反射的基础,提供了获取类信息的方法,后面会介绍。

    反射提供的功能

    java反射框架主要提供以下内容:

    • 在运行时判断对象所属的类;
    • 在运行时创建对象;
    • 在运行时获取类包含的成员变量、方法、父类、接口等信息;
    • 在运行时调用一个对象的方法;

    下面举例说明相关功能

    创建实例:

    //获取String所对应的Class对象
    Class<?> c = User.class;
    //获取String类带一个String参数的构造器
    Constructor constructor = c.getConstructor(String.class);
    //根据构造器创建实例
    User user = (User)constructor.newInstance("calm");
    

    获取方法:

    //返回类或接口声明的所有方法,包括私有的,但不包括继承的方法
    public Method[] getDeclaredMethods() throws SecurityException
    
    //所有public方法,包括继承的方法
    public Method[] getMethods() throws SecurityException
    
    //返回一个特定的方法,第一个参数为方法名称,后面的参数为方法参数对应Class的对象
    public Method getMethod(String name, Class<?>... parameterTypes)
    
    

    调用方法:

    Class<?> userClass=User.class;
    Object obj = userClass.newInstance();
    Method method =klass.getMethod("addRole",String.class);
    method.invoke(obj,"超级管理员");
    

    JDK动态代理

    JDK本身提供了动态代理的实现,要求被代理者必须实现接口。

    public static Object newProxyInstance(ClassLoader loader,
    Class<?>[] interfaces,InvocationHandler h)
    

    第一个参数为类加载器,第二个参数是被代理者实现的接口列表,第三个参数是实现了InvocationHandler接口的对象。

    InvocationHandler是一个接口,用于规范执行被代理者的方法,可在执行方法前后,添加公共的处理代码。生成的动态代理类包含一个InvocationHandler属性,调用对应方法时,会触发invoke方法的调用。

    public class JDKProxy implements InvocationHandler {    
        
        private Object targetObject;//被代理对象   
        
        public Object newProxy(Object targetObject) {        
            this.targetObject = targetObject;     
            return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
    targetObject.getClass().getInterfaces(), this);
    }    
        
        public Object invoke(Object proxy, Method method, Object[] args)//invoke方法    
                throws Throwable {    
            Object ret = null;         
            ret  = method.invoke(targetObject, args);      
            return ret;    
        }    
    }    
    

    测试代码:

    JDKProxy jdkProxy=new JDKProxy();
    UserService userService = (UserService) 
    jdkProxy.newProxy(new UserServiceImp());    
    userService.addRole("超级管理员");    
    

    JDK动态代理的基本原理是根据定义好的规则,用传入的接口创建一个新类。

    CGLIB动态代理

    JDK动态代理要求必须有接口,CGLIB(Code Generate Library)动态代理没有这个要求,它是通过创建一个被代理类的子类,然后使用ASM字节码库修改代码来实现的。

    public class CGLibProxy implements MethodInterceptor {    
        private Object targetObject; //被代理对象
    
        public Object createProxyObject(Object obj) {    
            this.targetObject = obj;    
            Enhancer enhancer = new Enhancer();    
            enhancer.setSuperclass(obj.getClass());    
            enhancer.setCallback(this);    
            Object proxyObj = enhancer.create();    
            return proxyObj;
        }    
        
        public Object intercept(Object proxy, Method method, Object[] args,    
                MethodProxy methodProxy) throws Throwable {    
            Object obj = null;       
            obj = method.invoke(targetObject, args);    
            return obj;    
        }       
    }
    

    测试代码:

    CGLibProxy cgLibProxy=new CGLibProxy();
    UserService userService = (UserService) 
    cgLibProxy.newProxy(new UserServiceImp());    
    userService.addRole("超级管理员");    
    

    ASM 是一个 Java 字节码操控框架。它能够以二进制形式修改已有类或者动态生成类。不过ASM在创建class字节码的过程中,操纵的级别是底层JVM的汇编指令级别,这要求ASM使用者要对class组织结构和JVM汇编指令有一定的了解。另外可以使用javassist框架操作字节码,它对开发者提供的接口比较优化。

    了解了反射和动态代理,对后面介绍MyBatis的解析和运行原理有很大帮助,下一篇会重点介绍。

    欢迎扫描下方二维码,关注我的个人微信公众号 ~

    情情说

    相关文章

      网友评论

      本文标题:深入浅出MyBatis:反射和动态代理基础

      本文链接:https://www.haomeiwen.com/subject/hlhpcftx.html