zoukankan      html  css  js  c++  java
  • mybatis源码:由浅入深理解Java动态代理

    什么是代理

    代理模式,目的就是为其他对象提供一个代理以控制对某个对象的访问,代理类为被代理者处理过滤消息,说白了就是对被代理者的方法进行增强。
    看到这里,有没有感觉很熟悉?AOP,我们熟知的面向切面编程,不也是对方法增强,对切点进行处理过滤么。
    其实AOP这种设计思想,他的精髓便是,在预编译和运行阶段使用动态代理实现的。

    初体验

    下面是我自己写的小例子。

    //代理的接口
    /**
     * @created with IDEA
     * @author: yonyong
     * @version: 1.0.0
     * @date: 2020/4/21
     * @time: 21:13
     **/
    public interface person {
        void doSomething();
        void fun1();
        void fun2();
    }
    
    
    //被代理者/委托人
    /**
     * @created with IDEA
     * @author: yonyong
     * @version: 1.0.0
     * @date: 2020/4/21
     * @time: 21:13
     **/
    public class Student implements person{
        @Override
        public void doSomething() {
            System.out.println("dosomeThing");
        }
    
        @Override
        public void fun1() {
            System.out.println("fun1");
        }
    
        @Override
        public void fun2() {
            System.out.println("fun2");
    
        }
    }
    
    //实现InvocationHandler接口,加入切面逻辑
    /**
     * @created with IDEA
     * @author: yonyong
     * @version: 1.0.0
     * @date: 2020/4/21
     * @time: 21:15
     **/
    public class StudentProxyHandler implements InvocationHandler {
    
        Student target;
    
        public StudentProxyHandler(Student target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("pre");
            Object obj = method.invoke(target,args);
            System.out.println("aft");
            return obj;
        }
    }
    
    /**
     * @created with IDEA
     * @author: yonyong
     * @version: 1.0.0
     * @date: 2020/4/21
     * @time: 21:19
     **/
    public class Main {
        public static void main(String[] args) {
            Student p=new Student();
            person person = (person) Proxy.newProxyInstance(p.getClass().getClassLoader(), p.getClass().getInterfaces(), new StudentProxyHandler(p));
            person.doSomething();
            System.out.println("______________________");
            person.fun1();
            System.out.println("______________________");
            person.fun2();
        }
    }
    

    运行代码,我们可以得到:

    pre
    dosomeThing
    after
    ______________________
    pre
    dosomeThing
    after
    ______________________
    pre
    dosomeThing
    after
    

    我们来通过打印的结果来实实在在的体会代理模式的优点及aop的特性:
    如果有种业务场景,需要有多个方法有重复的代码块,或者相同的实现规则,如果不用aop我们可能要每个方法写一份规则,或者自定义个方法,每个方法调用来实现。首先这就已经使代码变得侵入性,也违反了java的封装重构原则。
    而使用java动态代理,无论这个委托人有多少方法,他都会执行切面逻辑里的规则,这样很便于后期的代码维护,即便是后续加入新的方法,也无须考虑其他的,因为在切面里都已经做好了,这样代码的侵入性便降了很多。其实这和AOP的原理是一样的。

    动态代理与mybatis

    看到这里,我便觉得mybatis和j动态代理必定有着很紧密的联系。
    但我们通过上面的例子,我们知道,想要用动态代理,必须要有个接口的实现类,否则代理接口便没什么意义。而mybatis只是接口+xml的形式,他是怎么被代理的呢?
    还有一个疑问,我们知道Spring的注解如果放在service的接口层,而不是放在实现类,他会找不到这个bean,那为什么mapper层可以加注解呢?

    首先我们通过查询资料知道,mybatis确实是有动态代理实现的。那我们带着这个疑问,去看mybatis的源码。

    为了便于查看源码,我使用sqlsession的方式获取mapper。

    LRoleMapper lRoleMapper = sqlSessionFactory.openSession().getMapper(LRoleMapper.class);
    lRoleMapper.selectAll();
    

    点进getMapper的实现类 SqlSessionManager
    发现这里有sql的各个方法

    Connection getConnection(){}
    void commit() {}
    rollback(){}
    int insert(String statement){}
    update(String statement){}
    ...
    

    那我们在往下寻找,我看到了这个方法

     private class SqlSessionInterceptor implements InvocationHandler {
            public SqlSessionInterceptor() {
            }
    
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                SqlSession sqlSession = (SqlSession)SqlSessionManager.this.localSqlSession.get();
                if (sqlSession != null) {
                    try {
                        return method.invoke(sqlSession, args);
                    } catch (Throwable var12) {
                        throw ExceptionUtil.unwrapThrowable(var12);
                    }
                } else {
                    SqlSession autoSqlSession = SqlSessionManager.this.openSession();
    
                    Object var7;
                    try {
                        Object result = method.invoke(autoSqlSession, args);
                        autoSqlSession.commit();
                        var7 = result;
                    } catch (Throwable var13) {
                        autoSqlSession.rollback();
                        throw ExceptionUtil.unwrapThrowable(var13);
                    } finally {
                        autoSqlSession.close();
                    }
    
                    return var7;
                }
            }
        }
    

    看到这里,是不是眼前一亮,那我们再看这个类的构造方法

     private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
            this.sqlSessionFactory = sqlSessionFactory;
            this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionManager.SqlSessionInterceptor());
        }
    

    那看到这里我们明白了,这个类代理的是SqlSessionFactory这个类,而它的切面逻辑,就是执行方法前,如果当前sqlsession存在sqlsession,就正常执行这个方法,如果不存在,就创建一个session,创建失败再回滚数据。
    那这里的opensession,我直接贴源码了,无非就是从配置文件读取jdbc,连接验证后,获取会话之类,大家感兴趣可以一层一层往下扒。

        private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
            Transaction tx = null;
    
            DefaultSqlSession var8;
            try {
                Environment environment = this.configuration.getEnvironment();
                TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
                tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
                Executor executor = this.configuration.newExecutor(tx, execType);
                var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
            } catch (Exception var12) {
                this.closeTransaction(tx);
                throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
            } finally {
                ErrorContext.instance().reset();
            }
    
            return var8;
        }
    

    我们继续回到getMapper方法,我们一层一层往下扒,最后我们可以看到这个类

      public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
            MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
            if (mapperProxyFactory == null) {
                throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
            } else {
                try {
                    return mapperProxyFactory.newInstance(sqlSession);
                } catch (Exception var5) {
                    throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
                }
            }
        }
    

    再继续扒newInstance方法

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    package org.apache.ibatis.binding;
    
    import java.io.Serializable;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.util.Map;
    import org.apache.ibatis.reflection.ExceptionUtil;
    import org.apache.ibatis.session.SqlSession;
    
    public class MapperProxy<T> implements InvocationHandler, Serializable {
        private static final long serialVersionUID = -6424540398559729838L;
        private final SqlSession sqlSession;
        private final Class<T> mapperInterface;
        private final Map<Method, MapperMethod> methodCache;
    
        public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
            this.sqlSession = sqlSession;
            this.mapperInterface = mapperInterface;
            this.methodCache = methodCache;
        }
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (Object.class.equals(method.getDeclaringClass())) {
                try {
                    return method.invoke(this, args);
                } catch (Throwable var5) {
                    throw ExceptionUtil.unwrapThrowable(var5);
                }
            } else {
                MapperMethod mapperMethod = this.cachedMapperMethod(method);
                return mapperMethod.execute(this.sqlSession, args);
            }
        }
    
        private MapperMethod cachedMapperMethod(Method method) {
            MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
            if (mapperMethod == null) {
                mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
                this.methodCache.put(method, mapperMethod);
            }
    
            return mapperMethod;
        }
    }
    
    

    那到这里我们可以得出,mybatis中使用了大量的java动态代理。
    这个getMapper呢,到这里也就结束了,可以看到,SqlSession这个对象,代理的接口就是我们的mapper层dao,而这里的切面逻辑,如果当前声明的类是Object,直接执行方法,如果不是,执行另外的excute。这里我没有细看,初步理解是区分注解sql与mapper.xml 的sql。因为获取mapper的sql是通过反射得到的Class类。有兴趣的同学可以继续扒,我精力有限就先到这儿了。
    到此为止,第一个问题迎刃而解,总结来说,其实我们注入的mapper,是动态代理产生的对象
    那么为什么Mapper层加注解,spring也能获取到呢。
    其实这个问题已经和java动态代理没什么关系了,在这里大概解释一下。
    mybatis并不是spring的产品,而作为第三方的插件,我们都知道spring被称作胶水框架,而第三方就需要将自己的产品让spring管理。
    换句话说,他们和spring自身的bean生命周期并不是同步的。

    spring--------------------
    class->扫描->新建实例->交给容器
    
    mybatis ---------------------spring---
    class -> 扫描-> 新建对象 -> 交给spring
    
    

    同理,mybatis那就需要自己创建对象,把他交给spring。而我们平时的那些注解Mapperscan之类的,其实只是mybatis在标志这些接口,使用反射,获取这些类,实现一个ImportBeanDefinitionRegistrar接口,把自己产生的对象交给Spring。

  • 相关阅读:
    将h.264视频流封装成flv格式文件(一.flv格式)(转载)<a href="http://blog.csdn.net/yeyumin89/article/details/7932368"></a>
    01背包,求最大值
    JNI vc6.0 dll
    jni java c++ 参数传递问题解决
    (转载)将h.264视频流封装成flv格式文件(二.开始动手)http://blog.csdn.net/yeyumin89/article/details/7932431
    达到最高效益的调度
    按照RFC3984协议实现H264视频流媒体
    语音识别 转载 待测试
    活动选择问题 动态规划算法(最大子集合或最大收益)
    整齐打印
  • 原文地址:https://www.cnblogs.com/yonyong/p/12750737.html
Copyright © 2011-2022 走看看