设计模式学习笔记:一、代理模式(动态代理实现)
以数据库查询为例:
1.声明接口:执行数据库操作
public interface SqlSession {
public int save(String sql)throws Exception;
}
2.接口实现类:具体的主要业务,sql执行的实现
public class DeptMapper implements SqlSession {
PreparedStatement ps;
@Override
public int save(String sql) throws SQLException {//JDBC主要业务 输送sql
int num= ps.executeUpdate(sql);
return num;
}
}
3.InvocationHanler接口实现类:次要业务,前期数据库连接,后期sqlsession关闭
public class Invaction implements InvocationHandler {
private SqlSession obj;//具体被监控对象
Connection connection ;
PreparedStatement pStatement;
public Invaction(SqlSession param){
this.obj = param;
}
/*
*
* invoke方法:在被监控行为将要执行时,会被JVM拦截
* 被监控行为和行为实现方会被作为参数输送invoke
* ****通知JVM,这个被拦截方法是如何与当前次要业务方法绑定实现
* invoke方法三个参数
*
* int v= 小明.eat();//JVM拦截
* eat方法封装为Mehtod类型对象
* eat方法运行时接受所有的实参封装到Object[]
* 将负责监控小明的代理对象作为invoke方法第一个参数
*
*/
@Override
public Object invoke(Object porxy, Method method, Object[] params) throws Throwable {
Object value;
//1.执行JDBC初始次要业务
init();
//2.执行JDBC主要业务
Field psField = obj.getClass().getDeclaredField("ps");
psField.setAccessible(true);
psField.set(obj, pStatement);
value= method.invoke(obj, params);
//3.执行JDBC结束次要业务
close();
return value; //返回被拦截方法,需要调用地方
}
//次要业务
private void init()throws Exception{
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "123");
pStatement = connection.prepareStatement("");
}
private void close() throws SQLException{
if(pStatement!=null){
pStatement.close();
}
if(connection!=null){
connection.close();
}
}
}
4.代理对象创建
public class SqlSessionFactory {
/*
*
* JDK动态代理模式下,代理对象的数据类型
* 应该由监控行为来描述
* 参数: Class文件,监控类
*/
public static SqlSession Builder(Class classFile)throws Exception {
//1.创建被监控实例对象
SqlSession obj= (SqlSession) classFile.newInstance();
//2.创建一个通知对象
InvocationHandler adviser= new Invaction(obj);
//3.向JVM申请负责监控obj对象指定行为的监控对象(代理对象)
/*
* loader:被监控对象隶属的类文件在内存中真实地址
* interfaces:被监控对象隶属的类文件实现接口
* h:监控对象发现小明要执行被监控行为,应该有哪一个通知对象进行辅助
*/
SqlSession $proxy= (SqlSession) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), adviser);
return $proxy;
}
}
测试:
public static void main(String[] args) throws Exception {
Map StatementMapper = new HashMap();
StatementMapper.put("dept.save", "insert into dept values(50,'TEST','BEIJING',1)");
SqlSession dao = SqlSessionFactory.Builder(DeptMapper.class);
dao.save((String)StatementMapper.get("dept.save"));
}
这样,模拟mybatis的这种动态代理的实现就完成了