上回书只是说到单体的mapper代理, 但我们在平时的业务开发中肯定是一对的mapper类啊. 上回书的代理工厂,只是用来生成单个对象的工厂,而不是用于管理.
MapperRegistry
so 我们做一个注册器, 把这些工厂类都注册进来,根据不同的mapper类注册对应的代理工厂.
package com.linnine.mybatis.binding;
/**
* 再对映射代理工厂进行管理
*/
public class MapperRegistry {
// 注册 代理映射工厂
private final Map<Class<?>,MapperProxyFactory<?>> knownMappers = new HashMap<>();
/**
* 获取代理类
* @param type
* @param sqlSession
* @param <T>
* @return
*/
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));
}
}
/**
* 通过包名扫描添加
* @param packageName
*/
public void addMappers(String packageName){
Set<Class<?>> mapperSet = ClassScanner.scanPackage(packageName);
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}
private <T> boolean hasMapper(Class<T> type) {
return knownMappers.containsKey(type);
}
}
MapperProxy
既然我们已经可以对mapper进行管理,获取结果时也不用再类名拼接方法名了,直接用方法名,所以改造一下代理类.
package com.linnine.mybatis.binding;
public class MapperProxy<T> implements InvocationHandler, Serializable {
/**
* 会话结果管理
* key: 类名.方法名
* value: 对应返回结果(这里String是先small版的简单处理)
*/
private SqlSession sqlSession;
/**
* mapper类
*/
private final Class<T> mapperInterface;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
}
/**
* 不管什么方法都走进来
* @param proxy 代理对象
* @param method 执行的方法
* @param args 执行需要的参数
* @return 返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())){
return method.invoke(this,args);
}else {
//key 就是类的全名+方法名 获取实现就是通过这个key获取结果
return sqlSession.selectOne(method.getName(),args);
}
}
}
SqlSession
上面那个mapper用起来不是很方便,所以我们整合成一个对象, 其实就是对mapper接口的方法 统一处理类
package com.linnine.mybatis.session;
public interface SqlSession {
/**
* 根据sqlId(也就是方法名 一般通过这个绑定),获取一条数据 等同于01中的通过key获取value, 附带参数
* @param statement
* @param parameter
* @param <T>
* @return
*/
<T> T selectOne(String statement,Object parameter);
/**
* 根据类型获取该映射器
* @param type
* @param <T>
* @return
*/
<T> T getMapper(Class<T> type);
}
DefaultSqlSession
然后做个简单实现
package com.linnine.mybatis.session;
public class DefaultSqlSession implements SqlSession{
private final MapperRegistry mapperRegistry;
public DefaultSqlSession(MapperRegistry mapperRegistry) {
this.mapperRegistry = mapperRegistry;
}
/**
* 在这个类里统一代理
* @param statement
* @param parameter
* @param <T>
* @return
*/
@Override
public <T> T selectOne(String statement, Object parameter) {
return (T)("你被代理了!"+"方法:"+statement+",入参:"+parameter);
}
/**
* 从注册中心拿
* @param type
* @param <T>
* @return
*/
@Override
public <T> T getMapper(Class<T> type) {
//返回代理类,并返回代理方法的处理结果
return mapperRegistry.getMapper(type,this);
}
}
SqlSessionFactory
然后再造一个SqlSession的生成工厂
package com.linnine.mybatis.session;
public interface SqlSessionFactory {
/**
* 打开一个 session
* @return
*/
SqlSession openSqlSession();
}
DefaultSqlSessionFactory
造一个简单实现
package com.linnine.mybatis.session;
public class DefaultSqlSessionFactory implements SqlSessionFactory{
private final MapperRegistry mapperRegistry;
public DefaultSqlSessionFactory(MapperRegistry mapperRegistry) {
this.mapperRegistry = mapperRegistry;
}
@Override
public SqlSession openSqlSession() {
return new DefaultSqlSession(mapperRegistry);
}
}
准备工作好了,接下来就是测试类准备了
之前的IUserDao再补一个用于扫描
public interface ISchoolDao {
String querySchoolName(String uId);
}
测试方法
@Test
public void testMapperProxyFactory(){
String packagePath="com.linnine.mybatis.dao";
MapperRegistry mapperRegistry = new MapperRegistry();
//扫描注册
mapperRegistry.addMappers(packagePath);
DefaultSqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(mapperRegistry);
//创建一个会话
SqlSession sqlSession = sqlSessionFactory.openSqlSession();
IUserDao userDao = sqlSession.getMapper(IUserDao.class);
String s = userDao.queryUserName("10001");
System.out.println(s);
}
然后就可以看到类的方法调用被代理类实现了