实现一个mybatis

本文详述了如何从需求分析到代码实现一个简单的Mybatis框架,包括代码设计、初始化、MapperRegistry、MapperProxy的实现,以及配置、依赖、性能优化等关键步骤。并提供了一级缓存的优化实践,代码已开源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

需求分析
  • 执行sql
  • 对结果集进行友好封装
  • 使用简单
  • 性能优化
代码设计
代码架构

在这里插入图片描述

代码执行流程

在这里插入图片描述

代码精剪
初始化
@Component
public class MybatisInit implements ApplicationContextAware{

    @Value("${mybatis.mapperPackage}")
    private String mapperPackage;

    @PostConstruct
    public void init() throws BeansException {
        String packagePath = getPackagePath(mapperPackage);
        String path = getClass().getClassLoader().getResource(packagePath).getPath();
        File file = new File(path);
        File[] files = file.listFiles();
        ClassLoader classLoader = this.getClass().getClassLoader();
        for (File child : files) {
            try {
                String filePath = child.getAbsolutePath();
                String externalName = filePath.substring(filePath.lastIndexOf('/') + 1,filePath.length() - 6);
                externalName = mapperPackage + "." + externalName;
                Class<?> aClass = classLoader.loadClass(externalName);
                MapperRegistry.addMapper(aClass);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    @PreDestroy
    private void destroy(){
        ConnectFactory.destroy();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        String[] beanNames = applicationContext.getBeanNamesForAnnotation(Controller.class);
        for (String beanName : beanNames) {
            Object bean = applicationContext.getBean(beanName);
            Field[] declaredFields = bean.getClass().getDeclaredFields();
            for (Field declaredField : declaredFields) {
                Mapper resource = declaredField.getDeclaredAnnotation(Mapper.class);
                if (resource != null){
                    try {
                        declaredField.setAccessible(true);
                        declaredField.set(bean,createService(declaredField.getType()));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public static <T, P> T createService(Class<T> interfaceClass) {
        return (T) Proxy.newProxyInstance(
                interfaceClass.getClassLoader(),
                new Class<?>[]{interfaceClass},
                new MapperProxy<T>(interfaceClass,new DefaultSqlSession()));
    }


    private String getPackagePath(String packageName) {
        return packageName == null ? null : packageName.replace('.', '/');
    }

}
MapperRegistry
public class MapperRegistry {

    private static final Map<String, SqlBind> knownMappers = new HashMap<>();

    public static <T> void addMapper(Class<T> type) {
        if (type.isInterface()) {
            Method[] methods = type.getDeclaredMethods();
            for (Method method : methods) {
                String key = getKey(type.getName(), method.getName());
                Select annotation = method.getAnnotation(Select.class);
                try {
                    Type genericReturnType = method.getGenericReturnType();
                    String typeName = genericReturnType.getTypeName();
                    String className = null;
                    if (typeName.contains("<") && typeName.contains(">")){
                        className = typeName.substring(typeName.indexOf("<") + 1,typeName.indexOf(">"));
                    }
                    knownMappers.put(key,new SqlBind(annotation.sql().toLowerCase(),method.getParameterTypes(),method.getReturnType(),className));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static <T> String getKey(String className, String methodName) {
        return className + "#" + methodName;
    }


    public static <T> SqlBind<T> getMapper(Method method){
        return knownMappers.get(getKey(method.getDeclaringClass().getName(), method.getName()));
    }
}
MapperProxy
public class MapperProxy<T> implements InvocationHandler, Serializable {

    private Class<T> aClass;

    private SqlSession session;

    public MapperProxy(Class<T> aClass, SqlSession session) {
        this.aClass = aClass;
        this.session = session;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args){
        SqlBind<T> sqlBind = MapperRegistry.getMapper(method);
        List<Object> execute = session.select(sqlBind.getReturnType(), sqlBind.getGenericClassName(), args, sqlBind.getSql());
        if(sqlBind.getReturnType().getName().equals("java.util.ArrayList")){
            return execute;
        }

        if (execute.size() > 0 && sqlBind.getGenericClassName() == null){
            return execute.get(0);
        }

        return null;
    }
}
示例
依赖
		<dependency>
            <groupId>com.yutian</groupId>
            <artifactId>simple-mybatis</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

备注:包扫描com.yutian

配置

resources下创建配置文件mybatis.properties,内容如下

jdbc.url=
jdbc.user=
jdbc.password=
创建mapper接口
public interface MapperDemo {

    /**
     *
     * @return 返回值
     */
    @Select(sql = "SELECT name name,age age,gender gender from student where name = ?")
    Student demo(String name);
}
调用
@Mapper
    private MapperDemo mapperDemo;

    @RequestMapping("/test2")
    public Student test(String name){
        Student demo = mapperDemo.demo(name);
        return demo;
    }
调用结果

在这里插入图片描述

性能优化

实现了一级缓存,对于同一个sqlSession的同一sql,同一参数,同一返回值进行了本地缓存。

代码地址

https://github.com/yutian1999/simple-mybatis

关注个人公众号

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值