SpringBoot与Vue系列

Mybatis 初探,动态代理

2018-06-10  本文已影响7人  tanoak

在项目中经常使用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

上一篇下一篇

猜你喜欢

热点阅读