美文网首页SpringBoot与Vue系列
Mybatis 初探,动态代理

Mybatis 初探,动态代理

作者: tanoak | 来源:发表于2018-06-10 16:02 被阅读7次

    在项目中经常使用Mybatis框架作用DAO层,但是你真的对Mybatis的原理清楚吗?带着这个疑惑我们来实现一个简单的动态代理,本次了解的是接口实现动态代理,还有基于类的Mybatis的实现不在本次讨论范围之内
    1.实现简单的动态代理
    在了解Mybatis之前我们先来实现一个简单的动态代理的小demo

    a. 定义一个接口

    public interface HelloDao {
        void eat() ;
    }
    

    b. 实现类 ,实现吃水果的方法

    public class HelloDapImpl implements HelloDao{
    
        @Override
        public void eat() {
            System.out.println("吃水果");
        }
    }
    

    c. 代理类 对传入的类方法进行增强

    public class HelloHandler implements InvocationHandler {
    
        // 目标对象
    
        private Object target;
    
        public HelloHandler(Object target) {
            super();
            this.target = target;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("先洗手\n------------------------------\n");
            Object result = method.invoke(target, args);
            System.out.println("\n------------------------------\n吃完了");
            return result;
        }
    
        /**
         * 获取目标对象的代理对象
         * @return 代理对象
         */
        public Object getProxy() {
            return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                            target.getClass().getInterfaces(), this);
        }
    }
    

    d. 测试demo

    public static void main(String[] args) {
            HelloDao helloDao = new HelloDapImpl() ;
            // 实例化InvocationHandler
            HelloHandler invocationHandler = new HelloHandler(helloDao);
    
            // 根据目标对象生成代理对象
            HelloDao proxy = (HelloDao) invocationHandler.getProxy();
    
            // 调用代理对象的方法
            proxy.eat();
        }
    

    看到运行的结果是不是感觉很像AOP对方法进行前置和后置,这里是可以这么理解,但是使用jdk的动态代理只能基于接口,后面我们回到Mybatis的上面来,跟着我一起继续

    1. Mybatis实现

    a. 书写实体类

    public class Student {
        private Integer id ;
        private String name ;
    /**
    *省略get、set
    */
    

    b. 实现InvocationHandler ,重写invoke方法

    public class StudentHandler implements InvocationHandler {
        private String sql ;
    
        public StudentHandler(String sql) {
            this.sql = sql;
        }
    
        public StudentHandler() {
        }
    
        public String getSql() {
            return sql;
        }
    
        public void setSql(String sql) {
            this.sql = sql;
        }
    //"select *from student"
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            MyExecutor ex = new MyExecutorImpl() ;
            Student query = ex.query(getSql());
            return query ;
        }
    }
    
    

    c. 数据库工具类

    public class DbUtils {
             static String URL = "jdbc:mysql://localhost:3308/test_demo";
             static String USERNAME = "root" ;
             static String PASSWORD = "123123" ;
    
        public <T> T query(String statement) {
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
    
            try {
                conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
                String sql = statement;
                ps = conn.prepareStatement(sql);
                rs = ps.executeQuery();
    
                Student stu = new Student();
                if (rs.next()) {
                    stu.setId(rs.getInt("id"));
                    stu.setName(rs.getString("name"));
                }
                return (T) stu;
    
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                close(conn,ps);
            }
            return null;
        }
    
        private void close(Connection conn,PreparedStatement ps){
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    d. 动态构造实现类

    public <T> T getDao(Class<T> clz){
            //获取这个类的名字
            String name = clz.getSimpleName();
                String sql = "select *from student" ;
                T t = (T)Proxy.newProxyInstance(clz.getClassLoader()
                                ,new Class[]{clz},new StudentHandler(sql));
                return t;
        }
    
    

    e. 测试demo

        public static void main(String[] args) {
    
            StudentTest session = new StudentTest();
            //根据传入的接口获取对应的动态生成的代理类对象
            StudentDao dao = session.getDao(StudentDao.class);
            Student one = dao.findOne();
            System.out.println(one.toString());
    
        }
    

    运行成功,读者可能有疑问了,你sql是在代码里写死的,不是通过配置文件读取的,我们可以回到getDao()这个方法,对这个方法加以改造,如下

    增加perperties文件

    selectOne=select *from student
    
    public <T> T getDao(Class<T> clz) throws FileNotFoundException {
            //获取这个类的名字
            String name = clz.getSimpleName();
    
            String file = "D:\\tools\\idea\\work_space\\WebMagicDemo\\src\\main\\resources\\"+name+".properties" ;
            //"D:\\tools\\idea\\work_space\\WebMagicDemo\\src\\main\\resources\\"+name+".properties"
            InputStream is = new FileInputStream(new File(file)) ;
            Properties prop = new Properties();
            try {
                //读取xxx.txt文件,读取其中的sql
                prop.load(is);
                //取出其中的sql
                String sql = prop.getProperty("selectOne");
                T t = (T)Proxy.newProxyInstance(clz.getClassLoader()
                                ,new Class[]{clz},new StudentHandler(sql));
                return t;
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    

    这样是不是就实现了从配置文件中读取sql了呢,当然现在的代码仍然不完善,因为它没办法读取到参数,参数的读取在下片文章中会具体的讲解,这篇文章是一个简化版的,便于理解动态代理在Mybatis中的应用,Mybatis做的工作不仅仅是这些,涉及到的很多,但是核心无非也是围绕着这个进行扩展的,文章如理解有误请在下方纠正,一起交流

    参考博客:
    http://rejoy.iteye.com/blog/1627405

    相关文章

      网友评论

        本文标题:Mybatis 初探,动态代理

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