在项目中经常使用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的上面来,跟着我一起继续
- 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做的工作不仅仅是这些,涉及到的很多,但是核心无非也是围绕着这个进行扩展的,文章如理解有误请在下方纠正,一起交流
网友评论