首先,我们先模仿正常的项目,创建一个Dao层
package com.dxh.dao; import com.dxh.pojo.User; import java.util.List; public interface IUserDao { //查询所有用户 public List<User> findAll() throws Exception; //根据条件进行查询 public User findByCondition(User user) throws Exception; } package com.dxh.dao; import com.dxh.io.Resource; import com.dxh.pojo.User; import com.dxh.sqlSession.SqlSession; import com.dxh.sqlSession.SqlSessionFacetory; import com.dxh.sqlSession.SqlSessionFacetoryBuild; import java.io.InputStream; import java.util.List; public class IUserDaoImpl implements IUserDao { @Override public List<User> findAll() throws Exception { InputStream resourceAsStream = Resource.getResourceAsStream("sqlMapConfig.xml"); SqlSessionFacetory sqlSessionFacetory = new SqlSessionFacetoryBuild().build(resourceAsStream); SqlSession sqlSession = sqlSessionFacetory.openSession(); List<User> userList = sqlSession.selectList("user.selectList"); return userList; } @Override public User findByCondition(User user) throws Exception { InputStream resourceAsStream = Resource.getResourceAsStream("sqlMapConfig.xml"); SqlSessionFacetory sqlSessionFacetory = new SqlSessionFacetoryBuild().build(resourceAsStream); SqlSession sqlSession = sqlSessionFacetory.openSession(); User user2 = sqlSession.selectOne("user.selectOne",user); return user2; } } 问题分析:
很明显存在代码重复的问题,他们的前三句话都一样(加载配置文件、创建SqlSessionFacetory、生产SqlSeesion)
InputStream resourceAsStream = Resource.getResourceAsStream("sqlMapConfig.xml"); SqlSessionFacetory sqlSessionFacetory = new SqlSessionFacetoryBuild().build(resourceAsStream); SqlSession sqlSession = sqlSessionFacetory.openSession();
statementId存在硬编码问题
List<User> userList = sqlSession.selectList("user.selectList"); User user2 = sqlSession.selectOne("user.selectOne",user); 解决思路:使用代理模式生成Dao层代理实现类。
在SqlSession接口中增加一个方法并实现:
//为Dao接口生产代理实现类 public <T> T getMapper(Class<?> mapperClass); @Override public <T> T getMapper(Class<?> mapperClass) { //使用JDK动态代理来为Dao接口生成代理对象,并返回 Object o = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; } }); return (T) o; }我们使用Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法来生产代理对象。一会我们再来实现invoke方法。
那么此时我们如果再想执行方法应该这样做:
IUserDao iUserDao = sqlSession.getMapper(IUserDao.class); List<User> all = iUserDao.findAll();通过sqlSession.getMapper()方法获得代理对象
通过代理对象调用findAll()方法
执行invoke方法
我们来看看invoke方法:
Object proxy :当前代理对象的引用
Method method :当前被调用方法的引用
比如我们当前的代理对象iUserDao调用的是findAll()方法,而method就是findAll方法的引用
Object[] args : 传递的参数,比如我们想要根据条件查询
编写invoke()方法:
我们要首先明确一点,不论如何封装,底层都还是执行JDBC代码,那么我们就要根据不同情况 调用selectList或者selectOne。
此时就有一个疑问了:selectList和selectOne都需要一个参数——statementId,而此时我们是拿不到statementId的。
但是我们可以根据method对象得到方法名,和方法所在类的全类名。
因此我们需要规范下statementId的组成:
statementId = namespace.id = 方法所在类的全类名.方法名
修改UserMapper.xml
@Override public <T> T getMapper(Class<?> mapperClass) { //使用JDK动态代理来为Dao接口生成代理对象,并返回 Object o = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //底层都还是执行JDBC代码 //根据不同情况 调用selectList或者selectOne //准备参数: 1. statementId /** * **此时就有一个疑问了:`selectList`和`selectOne`都需要一个参数——`statementId`, * 而此时我们是拿不到`statementId`的。 * 但是我们可以根据`method`对象得到方法名,和方法所在类的全类名。 * 因此我们需要规范下statementId的组成: * **statementId = namespace.id = 方法所在类的全类名.方法名 */ String methodName = method.getName(); String className = method.getDeclaringClass().getName(); String statementId = className+"."+methodName; //准备参数:2. args //获取被调用方法的返回值类型 Type genericReturnType = method.getGenericReturnType(); //判断是否进行了泛型类型参数化 就是判断当前的返回值类型是否有泛型 if (genericReturnType instanceof ParameterizedType){ List<Object> selectList = selectList(statementId, args); return selectList; } return selectOne(statementId,args); } }); return (T) o; } 测试: package com.dxh.test; import com.dxh.dao.IUserDao; import com.dxh.io.Resource; import com.dxh.pojo.User; import com.dxh.sqlSession.SqlSession; import com.dxh.sqlSession.SqlSessionFacetory; import com.dxh.sqlSession.SqlSessionFacetoryBuild; import org.dom4j.DocumentException; import org.junit.Test; import java.beans.PropertyVetoException; import java.io.InputStream; import java.util.List; public class IPersistenceTest { @Test public void test() throws Exception { InputStream resourceAsStream = Resource.getResourceAsStream("sqlMapConfig.xml"); SqlSessionFacetory sqlSessionFacetory = new SqlSessionFacetoryBuild().build(resourceAsStream); SqlSession sqlSession = sqlSessionFacetory.openSession(); IUserDao iUserDao = sqlSession.getMapper(IUserDao.class); List<User> all = iUserDao.findAll(); System.out.println(all); //打印结果:[User{id=1, username='lucy'}, User{id=2, username='李四'}, User{id=3, username='null'}] User user1 = iUserDao.findByCondition(user); System.out.println(user1); //User{id=1, username='lucy'} } }