这是一个流程,
通过 SqlSessionFactory
开启一个 SqlSession
SqlSession
里有成员变量 MapperRegistry
MapperRegistry
里面通过包路径扫描,得到了一个 map
这个 map 封装了接口,跟它的代理类工厂
可以通过 get 的方式获得代理类工厂,用工厂的静态方法 new 出来一个代理对象
使用工厂模式可以屏蔽创建细节,延迟创建过程
![image-20240818141349865](/Users/sudong/Library/Application Support/typora-user-images/image-20240818141349865.png)
目前这个接口中对于数据库的操作仅仅只提供了 selectOne,后续还会有相应其他方法的定义
目录结构和代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 mybatis-step-02 └── src ├── main │ └── java │ └── cn.bugstack.mybatis │ ├── binding │ │ ├── MapperProxy.java │ │ ├── MapperProxyFactory.java │ │ └── MapperRegistry.java │ └── session │ ├── defaults │ │ ├── DefaultSqlSession.java │ │ └── DefaultSqlSessionFactory.java │ ├── SqlSession.java │ └── SqlSessionFactory.java └── test └── java └── cn.bugstack.mybatis.test.dao ├── dao │ ├── ISchoolDao.java │ └── IUserDao.java └── ApiTest.java
MapperProxy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class MapperProxy <T> implements InvocationHandler , Serializable { private static final long serialVersionUID = -6424540398559729838L ; private SqlSession sqlSession; private final Class<T> mapperInterface; public MapperProxy (SqlSession sqlSession, Class<T> mapperInterface) { this .sqlSession = sqlSession; this .mapperInterface = mapperInterface; } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this , args); } else { return "你的被代理了!" ; } } }
MapperProxyFactory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class MapperProxyFactory <T> { private final Class<T> mapperInterface; public MapperProxyFactory (Class<T> mapperInterface) { this .mapperInterface = mapperInterface; } public T newInstance (SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy <>(sqlSession, mapperInterface); return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class []{mapperInterface}, mapperProxy); } }
MapperRegistry
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 public class MapperRegistry { private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap <>(); public <T> T getMapper (Class<T> type, SqlSession sqlSession) { final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); if (mapperProxyFactory == null ) { throw new RuntimeException ("Type " + type + " is not known to the MapperRegistry." ); } try { return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new RuntimeException ("Error getting mapper instance. Cause: " + e, e); } } public <T> void addMapper (Class<T> type) { if (type.isInterface()) { if (hasMapper(type)) { throw new RuntimeException ("Type " + type + " is already known to the MapperRegistry." ); } knownMappers.put(type, new MapperProxyFactory <>(type)); } } private <T> boolean hasMapper (Class<T> type) { return knownMappers.containsKey(type); } public void addMappers (String packageName) { Set<Class<?>> mapperSet = ClassScanner.scanPackage(packageName); for (Class<?> mapperClass : mapperSet) { addMapper(mapperClass); } } }
DefaultSqlSession
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class DefaultSqlSession implements SqlSession { private final MapperRegistry mapperRegistry; DefaultSqlSession(MapperRegistry mapperRegistry){ this .mapperRegistry = mapperRegistry; } @Override public <T> T selectOne (String statement) { return (T) ("你被代理了!" + "方法:" + statement); } @Override public <T> T selectOne (String statement, Object parameter) { return (T) ("你被代理了!" + "方法:" + statement + " 入参:" + parameter); } @Override public <T> T getMapper (Class<T> type) { return mapperRegistry.getMapper(type, this ); } }
DefaultSqlSessionFactory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class DefaultSqlSessionFactory implements SqlSessionFactory { private final MapperRegistry mapperRegistry; public DefaultSqlSessionFactory (MapperRegistry mapperRegistry) { this .mapperRegistry = mapperRegistry; } @Override public SqlSession openSession () { return new DefaultSqlSession (mapperRegistry); } }
SqlSession
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public interface SqlSession { <T> T selectOne (String statement) ; <T> T selectOne (String statement, Object parameter) ; <T> T getMapper (Class<T> type) ; }
SqlSessionFactory
1 2 3 4 5 6 7 8 9 10 public interface SqlSessionFactory { SqlSession openSession () ; }
Dao 层接口
1 2 3 4 5 6 7 8 9 10 11 12 public interface ISchoolDao { String querySchoolName (String uId) ; }public interface IUserDao { String queryUserName (String uId) ; Integer queryUserAge (String uId) ; }
测试方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Test public void test_MapperProxyFactory () { MapperRegistry registry = new MapperRegistry (); registry.addMappers("cn.bugstack.mybatis.test.dao" ); SqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory (registry); SqlSession sqlSession = sqlSessionFactory.openSession(); IUserDao userDao = sqlSession.getMapper(IUserDao.class); String res = userDao.queryUserName("10001" ); logger.info("测试结果:{}" , res); }
答疑 1 2 3 4 5 6 7 8 9 <T> T getMapper (Class<T> type) ;
如何理解 “巧妙地使用了泛型,使得类型安全”
在这个方法里,<T>
表示这是一个泛型方法,其中传入的参数是 Class<T>
这说明参数的类型与返回值的类型应该相等
类型安全意味着编译器会在编译时进行类型检查,以防止类型错误,通过泛型,getMapper
方法能够确保返回的对象类型与调用者期望的类型一致,避免了强制类型转换
在调用的时候可以这样
1 UserMapper userMapper = sqlSesson.getMapepr(UserMapper.class);
与非泛型方法对比
1 public Object getMapper (Class<?> type) ;
在调用的时候需要强制类型转换,编译器无法验证类型转换是否正确,可能在运行时抛出 ClassCastException
1 UserMapper userMapper = (UserMapper) sqlSession.getMapper(UserMapper.class);
本篇文章是在小傅哥的基础上,添加了自己的理解写下的 小傅哥博客:https://bugstack.cn