Mybatis 初探,动态代理

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

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏决胜机器学习

PHP数据结构(八) ——赫夫曼树实现字符串编解码(实践2)

PHP数据结构(八)——赫夫曼树实现字符串编解码(实践2) (原创内容,转载请注明来源,谢谢) 公众号规定不能超过3000字,只能分两篇,见谅。 由于需要分两篇...

3456
来自专栏老九学堂

十七个C语言新手编程时常犯的错误及解决方式

C编译的程序对语法检查并不像其它高级语言那么严格,这就给编程人员留下“灵活的余地”,但还是由于这个灵活给程序的调试带来了许多不便,尤其对初学C语言的人来说,经常...

2937
来自专栏www.96php.cn

PHP关键字、PHP 语言结构(Language constructs)和函数的区别

1、 什么是语言结构和函数 语言结构: 就是PHP语言的关键词,语言语法的一部分; 它不可以被用户定义或者添加到语言扩展或者库中; ...

3739
来自专栏一个会写诗的程序员的博客

《一切皆是映射:代码的本质》Java 动态读取源代码,并编译 & 加载执行

动态的执行一段简单代码,采用生成java文件,调用javac编译,反射执行的方式。

1833
来自专栏老马说编程

(86) 动态代理 / 计算机程序的思维逻辑

前面两节,我们介绍了反射和注解,利用它们,可以编写通用灵活的程序,本节,我们来探讨Java中另外一个动态特性 - 动态代理。 动态代理是一种强大的功能,它可以...

1876
来自专栏开发与安全

C++中四种类型转换以及const_cast是否能改变常量的问题

we have four specific casting operators:dynamic_cast, reinterpret_cast, static_c...

20410
来自专栏开发与安全

从零开始学C++之动态创建对象

回顾前面的文章,实现了一个简单工厂模式来创建不同类对象,但由于c++没有类似new "Circle"之类的语法,导致CreateShape 函数中需要不断地i...

3110
来自专栏木木玲

Reference 、ReferenceQueue 详解

2887
来自专栏小灰灰

Java学习之深拷贝浅拷贝及对象拷贝的两种方式

I. Java之Clone 0. 背景 对象拷贝,是一个非常基础的内容了,为什么会单独的把这个领出来讲解,主要是先前遇到了一个非常有意思的场景 有一个任务,需要...

3329
来自专栏数据分析

char varchar nchar nvarcharar到底有多大区别

首先说明下,ASP.NET MVC系列还在龟速翻译中。 工作好多年,基础知识甚是薄弱,决定以后在coding(cv操作)的时候尽量多google下,然后总结下来...

2856

扫码关注云+社区