手撸mybatis02: 实现映射器的自动注册及获取使用

上回书只是说到单体的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);
}

然后就可以看到类的方法调用被代理类实现了
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值