美文网首页
MyBatis动态代理(一)

MyBatis动态代理(一)

作者: Doooook | 来源:发表于2020-07-21 22:56 被阅读0次

    概要:在我们使用MyBatis的时候,发现Mapper仅仅只是一个接口,而不是一个包含逻辑的实现类的,我们都知道,一个接口是没有办法去执行的,那么为什么MyBatis就可以呢?这就涉及到动态代理了。

    image.png

    反射技术

    被代理的类:

    /**
     * @author: Jason
     * @date: 2020-07-19 22:19
     */
    public class ReflectService {
    
        /**
         * 服务方法
         * @param name 姓名
         */
        public void sayHello(String name) {
            System.err.println("hello " + name);
        }
    
    }
    

    通过反射创建ReflectService并调用其sayHello方法:

    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    /**
     * @author: Jason
     * @date: 2020-07-19 22:21
     */
    public class ReflectTest {
    
        public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
            // 通过反射创建ReflectService对象
            Object reflectService = Class.forName(ReflectService.class.getName()).newInstance();
            // 获取服务方法:sayHello
            Method method = reflectService.getClass().getMethod("sayHello", String.class);
            // 反射调用方法
            Object zhangsan = method.invoke(reflectService, "zhangsan");
            // void 无返回值,null
            System.out.println(zhangsan);
        }
    }
    

    JDK动态代理

    1. 编写服务类(HelloService)和接口(HelloServiceImpl),这个是真正的服务提供者,在JDK代理中接口是必须的。
    2. 编写代理类,实现InvocationHandler接口
    3. 通过bind方法绑定委托对象返回一个代理类
    4. 实现invoke方法,通过method调用真是对象的方法,并可以在前后做增强
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * @author: Jay Mitter
     * @date: 2020-07-19 22:34
     */
    public class HelloServiceProxy implements InvocationHandler {
    
        /**
         * 真实的代理对象
         */
        private Object target;
    
        /**
         * 绑定委托对象返回一个代理类
         * @param target
         * @return
         */
        public Object bind(Object target) {
            this.target = target;
            // target.getClass().getInterfaces() ==> JDK代理需要提供接口
            // spring默认使用jdk动态代理,如果类没有接口,则使用cglib。
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.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.err.println("################### 我是JDK动态代理 ###################");
            Object result = null;
            // 增强
            // 反射方法前调用
            System.err.println("我准备说hello。");
            // 执行方法,相当于调用HelloServiceImpl类的sayHello方法
            result = method.invoke(target, args);
            // 反射方法后调用
            System.err.println("我说过了hello。");
            return result;
        }
    }
    

    HelloService接口:

    /**
     * @author: Jay Miiter
     * @date: 2020-07-19 22:28
     */
    public interface HelloService {
    
        /**
         * sayHello
         * @param name 姓名
         */
        void sayHello(String name);
    
    }
    

    HelloService接口实现类HelloServiceImpl:

    /**
     * @author: Jason
     * @date: 2020-07-19 22:29
     */
    public class HelloServiceImpl implements HelloService {
        @Override
        public void sayHello(String name) {
            System.err.println("hello " + name);
        }
    }
    

    JDK的代理最大的缺点是需要提供接口,而MyBatis的Mapper就是一个接口,它采用的就是JDK的动态代理。

    CgLib动态代理

    JDK提供的动态代理存在一个缺陷,就是你必须提供接口才可以使用,为了克服这个缺陷,我们可以使用开源框架——CGLIB,它是一种流行的动态代理。

    import org.springframework.cglib.proxy.Enhancer;
    import org.springframework.cglib.proxy.MethodInterceptor;
    import org.springframework.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    /**
     * @author: Jay Mitter
     * @date: 2020-07-20 21:43
     */
    public class HelloServiceCgLib implements MethodInterceptor {
    
        /**
         * 创建代理对象
         * 根据一个类型产生代理类
         * @param clazz
         * @return
         */
        public Object getInstance(Class<?> clazz) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(clazz);
            // 回调方法
            enhancer.setCallback(this);
            // 创建代理对象
            return enhancer.create();
        }
    
        /**
         * 回调方法
         *
         * @param object
         * @param method
         * @param args
         * @param methodProxy
         * @return
         * @throws Throwable
         */
        @Override
        public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.err.println("################# 我是CGLIB的动态代理 #################");
            // 增强
            // 反射方法前调用
            System.err.println("我准备说hello。");
            Object returnObj = methodProxy.invokeSuper(object, args);
            // 反射方法后调用
            System.err.println("我说过hello了。");
            return returnObj;
        }
    
    }
    

    这样便能够实现CGLIB的动态代理。在MyBatis中通常在延迟加载的时候才会用到CGLIB的动态代理。

    无需接口的HelloServiceImplNoInterface类:

    /**
     * @author: Jay Mitter
     * @date: 2020-07-19 22:29
     * CgLib 无需实现接口
     */
    public class HelloServiceImplNoInterface {
    
        public void sayHello(String name) {
            System.err.println("hello " + name);
        }
    
    }
    

    测试

    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    /**
     * @author: Jay Mitter
     * @date: 2020-07-20 21:31
     */
    public class HelloServiceMain {
    
        public static void main(String[] args) {
            testJdkProxy();
            testCgLibProxy();
        }
    
        private static void testJdkProxy() {
            // 将生成的字节码保存到本地,
            createProxyClassFile();
            HelloServiceProxy helloServiceHandler = new HelloServiceProxy();
            HelloService proxy = (HelloService) helloServiceHandler.bind(new HelloServiceImpl());
            proxy.sayHello("张三");
        }
    
        private static void testCgLibProxy() {
            HelloServiceCgLib helloServiceCgLib = new HelloServiceCgLib();
            HelloServiceImplNoInterface instance = (HelloServiceImplNoInterface) helloServiceCgLib.getInstance(HelloServiceImplNoInterface.class);
            instance.sayHello("李四");
        }
    
        private static void createProxyClassFile() {
            String name = "ProxyHelloService";
            byte[] data = ProxyGenerator.generateProxyClass(name, new Class[]{HelloService.class});
            FileOutputStream out = null;
            try {
                out = new FileOutputStream(name + ".class");
                System.out.println((new File("hello")).getAbsolutePath());
                out.write(data);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (null != out) {
                    try {
                        out.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
    }
    

    通过字节啊生成的实现类:

    /**
     * @author Jay Mitter
     * @date 2020-07-20 21:43
     * JDK动态代理通过字节码生成的最终真正的代理类,它继承自Proxy并实现了我们定义的HelloService接口
     * 在实现Subject接口方法的内部,通过反射调用了InvocationHandlerImpl的invoke方法。
     */
    public final class ProxyHelloService extends Proxy implements HelloService {
    
        private static final long serialVersionUID = 4642095113614858015L;
        private static Method m1;
        private static Method m3;
        private static Method m2;
        private static Method m0;
    
        public ProxyHelloService(InvocationHandler var1) {
            super(var1);
        }
    
        @Override
        public final boolean equals(Object var1) {
            try {
                return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
    
        @Override
        public final void sayHello(String var1) {
            try {
                super.h.invoke(this, m3, new Object[]{var1});
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
    
        @Override
        public final String toString() {
            try {
                return (String)super.h.invoke(this, m2, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        @Override
        public final int hashCode() {
            try {
                return (Integer)super.h.invoke(this, m0, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                m3 = Class.forName("com.pengjs.kkb.mybatis.service.HelloService").getMethod("sayHello", Class.forName("java.lang.String"));
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:MyBatis动态代理(一)

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