LightORM 轻量级 ORM 框架架构设计文档

5 阅读9分钟

概述

项目名称:LightORM
项目目标:设计并实现一个简易 ORM 框架(类似 MyBatis 的轻量级版本),并将其封装为 Spring Boot Starter,帮助开发者深入理解 Spring 核心容器、扩展机制、AOP、事务、类型转换、自动配置等关键技术。
技术栈:Java 17、Spring Framework 6.x、Spring Boot 3.x、JDBC、JDK 动态代理、CGLIB、ThreadLocal、反射、Maven 多模块。
优化说明:本版基于原始设计进行了全面优化,补全了关键实现的细节代码,强化了与 Spring 框架内部机制的对比,完善了事务管理与类型映射,并大幅扩展了核心源码分析、自动配置原理及知识巩固章节,使文档更加详尽、层次分明,便于深度学习。


目录

  1. 项目整体架构设计
  2. @MapperScan 注解与 ImportBeanDefinitionRegistrar 实现
  3. MapperFactoryBean 与动态代理
  4. SqlSession 与 SQL 执行器
  5. 声明式事务支持(深度集成 Spring 事务管理器)
  6. 类型转换与结果映射(增强扩展性)
  7. Spring Boot 自动配置(逐步剖析)
  8. 事件机制应用(完整上下文集成)
  9. 自定义扩展点(深度实践)
  10. 完整可运行 Demo 设计
  11. README 内容(扩展版)
  12. 核心源码实现(完整补充与讲解)
  13. 总结与 Spring 知识巩固分析(深化对比)

1. 项目整体架构设计

1.1 模块划分

LightORM 采用 Maven 多模块结构,遵循典型的 Spring Boot Starter 分层设计:

light-orm
├── light-orm-core              # 核心实现模块,与 Spring 无关
├── light-orm-spring            # Spring 整合模块,负责将 Core 与 Spring 容器集成
├── light-orm-spring-boot-starter # Spring Boot 自动配置模块,提供开箱即用的体验
└── light-orm-demo              # 示例应用,用于验证框架功能

各模块职责:

模块名职责关键类/包
light-orm-core定义 ORM 核心抽象:注解、SqlSession、执行器、类型转换、事务等,不依赖任何 Spring API,可独立使用@Select, @Insert, SqlSession, Executor, TypeHandler, DataAccessException
light-orm-spring借助 Spring 扩展点(ImportBeanDefinitionRegistrarFactoryBeanBeanPostProcessor、AOP 等)将 Core 中的组件注册到容器,并实现代理增强、声明式事务@MapperScan, MapperScannerRegistrar, MapperFactoryBean, MapperProxy, LightTransactionAdvisor
light-orm-spring-boot-starter提供自动配置,根据 classpath 条件自动装配 DataSourceSqlSession、事务管理器等,实现“引入即用”LightOrmAutoConfiguration, LightOrmProperties, AutoConfiguration.imports
light-orm-demo典型 Spring Boot 应用,演示如何使用 @MapperScan 扫描接口,通过 @Select 等注解实现数据库操作和事务回滚UserMapper, UserService, DemoApplication

1.2 架构总览图

flowchart TB
    subgraph Boot_Starter ["Boot Starter"]
        Auto["LightOrmAutoConfiguration"]
        Props["LightOrmProperties"]
        Spring_Factories["AutoConfiguration.imports"]
    end

    subgraph Spring_Module ["Spring Module"]
        MapperScan["@MapperScan"]
        Registrar["MapperScannerRegistrar"]
        MapperFB["MapperFactoryBean"]
        Proxy["MapperProxy / JDK Dynamic Proxy"]
        BPP["MapperBeanProcessor"]
        TxAdvisor["LightTransactionAdvisor"]
        TxInterceptor["LightTransactionInterceptor"]
    end

    subgraph Core_Module ["Core Module"]
        Annotations["@Select, @Insert, @Update, @Delete, @Mapper"]
        SqlSession["SqlSession / DefaultSqlSession"]
        Executor["Executor / SimpleExecutor"]
        TypeHandler["TypeHandler / TypeHandlerRegistry"]
        Event["MapperRegisteredEvent"]
    end

    Boot_Starter -->|"自动装配"| Spring_Module
    Spring_Module -->|"整合"| Core_Module
    Spring_Module -->|"扩展点"| Spring_Container["Spring IoC Container"]
    Core_Module -->|"JDBC"| DataSource["DataSource"]
    TxAdvisor --> PlatformTransactionManager

    classDef starter fill:#e3f2fd,stroke:#1e88e5,stroke-width:2px,color:#0d47a1;
    classDef spring fill:#f3e5f5,stroke:#8e24aa,stroke-width:2px,color:#4a148c;
    classDef core fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,color:#1b5e20;
    class Auto,Props,Spring_Factories starter;
    class MapperScan,Registrar,MapperFB,Proxy,BPP,TxAdvisor,TxInterceptor spring;
    class Annotations,SqlSession,Executor,TypeHandler,Event core;

1.3 核心流程序列图

sequenceDiagram
    participant Dev as 开发者
    participant Boot as Spring Boot 启动
    participant Auto as LightOrmAutoConfiguration
    participant Registrar as MapperScannerRegistrar
    participant Scanner as ClassPathMapperScanner
    participant Ctx as ApplicationContext
    participant Factory as MapperFactoryBean
    participant Proxy as MapperProxy
    participant SqlSession as SqlSession
    participant TM as PlatformTransactionManager
    participant DB as DataSource

    Boot->>Auto: 加载自动配置
    Auto->>Auto: 注册 DataSource / SqlSession / TransactionManager
    Boot->>Registrar: 处理 @MapperScan 导入
    Registrar->>Scanner: doScan(basePackages)
    Scanner->>Scanner: 扫描接口,修改 BeanDefinition 为 MapperFactoryBean
    Scanner->>Ctx: publishEvent(MapperRegisteredEvent) (优化:已实现 ApplicationContextAware)
    Boot->>Factory: 实例化 MapperFactoryBean(FactoryBean)
    Factory->>Proxy: getObject() -> 创建动态代理
    Dev->>Proxy: 调用 userMapper.selectById(1L)
    Proxy->>Proxy: 解析 @Select,构建 MapperMethod
    Proxy->>SqlSession: selectOne(SQL, params)
    SqlSession->>DB: 获取连接(可能来自事务同步)
    SqlSession->>SqlSession: 执行 PreparedStatement
    DB-->>SqlSession: ResultSet
    SqlSession->>SqlSession: TypeHandler 映射实体
    SqlSession-->>Proxy: 结果
    Proxy-->>Dev: User 对象
    Note over Dev,TM: 事务示例
    Dev->>Proxy: transferMoney() (标注 @LightTransactional)
    Proxy->>TM: 开启事务
    TM->>DB: getConnection & setAutoCommit(false)
    TM->>SqlSession: 绑定线程连接
    SqlSession->>DB: 复用同一连接执行多条SQL
    Proxy->>TM: 提交/回滚事务

2. @MapperScan 注解与 ImportBeanDefinitionRegistrar 实现

2.1 设计目标

模仿 MyBatis-Spring 中 @MapperScan 的工作方式,通过 @Import(MapperScannerRegistrar.class) 在 Spring 容器启动时动态扫描指定包路径下的 Mapper 接口,并将它们的 BeanDefinition 修改为 MapperFactoryBean,从而让 Spring 容器管理 Mapper 代理对象。

2.2 核心类解析

2.2.1 @MapperScan 注解

package com.lightorm.spring.annotation;

import com.lightorm.spring.registrar.MapperScannerRegistrar;
import org.springframework.context.annotation.Import;

import java.lang.annotation.*;

/**
 * 启用 LightORM Mapper 接口扫描。
 * 使用方式:在配置类上添加 {@code @MapperScan("com.example.mapper")},
 * 将指定包下的所有标注了 {@link com.lightorm.core.annotation.Mapper} 的接口
 * 注册为 Spring Bean,底层通过 {@link com.lightorm.spring.factory.MapperFactoryBean} 创建代理。
 *
 * <p>该注解使用 Spring 的 {@link Import} 机制导入 {@link MapperScannerRegistrar},
 * 触发自定义的 {@link ImportBeanDefinitionRegistrar} 处理。
 *
 * <p>与 Spring 内部对比:类似于 {@code @ComponentScan} 通过 {@code @Import(ComponentScanRegistrar.class)} 
 * 导入其处理逻辑,但本文自定义了接口过滤和 FactoryBean 替换逻辑。
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {

    /**
     * 待扫描的基础包路径数组。支持占位符(如 ${myapp.mapper.packages})。
     */
    String[] basePackages() default {};

    /**
     * 指定需要扫描的类所在的包(以类的包为准),与 basePackages 二选一或组合使用。
     */
    Class<?>[] basePackageClasses() default {};
}

2.2.2 MapperScannerRegistrar(优化版:完整包路径合并)

package com.lightorm.spring.registrar;

import com.lightorm.spring.annotation.MapperScan;
import com.lightorm.spring.scanner.ClassPathMapperScanner;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 处理 {@link MapperScan} 注解的 ImportBeanDefinitionRegistrar 实现。
 * 在 Spring 容器启动的 BeanDefinition 注册阶段,读取注解属性,
 * 构造并调用 {@link ClassPathMapperScanner} 完成 Mapper 接口的扫描与注册。
 *
 * <p>该类同时实现了 {@link ResourceLoaderAware},用于获取资源加载器(ClassPath 扫描需要)。
 *
 * <p>与 Spring 内部对比:Spring 的 {@code ComponentScanAnnotationParser} 解析 @ComponentScan 后创建
 * {@code ClassPathBeanDefinitionScanner} 完成扫描;此处流程完全类似,但自定义了过滤器。
 */
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {

    private ResourceLoader resourceLoader;

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                                        BeanDefinitionRegistry registry) {
        AnnotationAttributes mapperScanAttrs = AnnotationAttributes
                .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
        if (mapperScanAttrs == null) {
            return;
        }

        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
        if (resourceLoader != null) {
            scanner.setResourceLoader(resourceLoader);
        }

        // 合并 basePackages 和 basePackageClasses(优化:完整处理逻辑)
        List<String> basePackages = new ArrayList<>();
        // 处理字符串包名
        basePackages.addAll(
                Arrays.stream(mapperScanAttrs.getStringArray("basePackages"))
                        .filter(StringUtils::hasText)
                        .collect(Collectors.toList())
        );
        // 处理类所在的包
        basePackages.addAll(
                Arrays.stream(mapperScanAttrs.getClassArray("basePackageClasses"))
                        .map(ClassUtils::getPackageName)
                        .collect(Collectors.toList())
        );
        if (basePackages.isEmpty()) {
            // 默认使用导入注解的类所在包
            basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName()));
        }

        scanner.doScan(StringUtils.toStringArray(basePackages));
    }
}

2.2.3 ClassPathMapperScanner(增强:实现 ApplicationContextAware 发布事件)

package com.lightorm.spring.scanner;

import com.lightorm.core.annotation.Mapper;
import com.lightorm.spring.event.MapperRegisteredEvent;
import com.lightorm.spring.factory.MapperFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.type.filter.AnnotationTypeFilter;

import java.util.Set;

/**
 * 自定义的类路径 BeanDefinition 扫描器,负责扫描带有 {@link Mapper} 注解的接口,
 * 并将其 BeanDefinition 替换为 {@link MapperFactoryBean}。
 *
 * <p>优化:实现 {@link ApplicationContextAware} 以获取应用上下文,
 * 在每次成功注册 Mapper 后发布 {@link MapperRegisteredEvent} 事件。
 */
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public ClassPathMapperScanner(BeanDefinitionRegistry registry) {
        super(registry, false);
        addIncludeFilter(new AnnotationTypeFilter(Mapper.class));
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

        if (!beanDefinitions.isEmpty()) {
            processBeanDefinitions(beanDefinitions);
        }

        return beanDefinitions;
    }

    private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
        for (BeanDefinitionHolder holder : beanDefinitions) {
            GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();

            String originalClassName = definition.getBeanClassName();
            // 设置 FactoryBean 类
            definition.setBeanClass(MapperFactoryBean.class);
            // 传入原始接口类型作为构造参数
            definition.getConstructorArgumentValues().addGenericArgumentValue(originalClassName);
            // 按类型自动注入 (SqlSession 等)
            definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

            // 发布 Mapper 注册事件(优化:确保上下文已注入)
            if (applicationContext != null) {
                try {
                    Class<?> mapperInterface = Class.forName(originalClassName);
                    applicationContext.publishEvent(
                            new MapperRegisteredEvent(this, mapperInterface, holder.getBeanName())
                    );
                } catch (ClassNotFoundException e) {
                    // 日志记录
                }
            }
        }
    }
}

Spring 扩展点体现

  • ImportBeanDefinitionRegistrar:允许在容器启动时动态注册额外的 BeanDefinition,是 Spring 提供给框架开发者的核心扩展点之一。与之对应的内部实现有 AspectJAutoProxyRegistrarAsyncAnnotationBeanPostProcessor 等。
  • ResourceLoaderAware:使 Registrar 能够感知 Spring 的资源加载器,方便类路径扫描。
  • ClassPathBeanDefinitionScanner:继承并定制扫描逻辑,体现了 Spring 扫描机制的可扩展性。Spring 内部的 ComponentScanBeanDefinitionParser 最终也会使用 ClassPathBeanDefinitionScanner 或其子类。
  • BeanDefinitionRegistry:直接操作注册表,实现了程序化 Bean 注册。

3. MapperFactoryBean 与动态代理

3.1 设计目标

每个 Mapper 接口在 Spring 容器中对应一个 MapperFactoryBean,该 FactoryBean 负责在运行时生成 JDK 动态代理对象。代理对象拦截所有方法调用,解析方法上的 SQL 注解,并通过 SqlSession 执行 SQL,返回映射结果。

3.2 MapperFactoryBean(优化:增加泛型安全与缓存代理)

package com.lightorm.spring.factory;

import com.lightorm.core.session.SqlSession;
import com.lightorm.spring.proxy.MapperProxy;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;

import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 针对每个 Mapper 接口的 FactoryBean 实现。
 *
 * <p>优化:内部缓存代理实例,避免多次调用 getObject() 时重复创建。
 * 但 Spring 容器默认单例,该缓存非必须,此处仅展示 FactoryBean 单例模式下的细节。
 */
public class MapperFactoryBean<T> implements FactoryBean<T>, InitializingBean {

    private Class<T> mapperInterface;
    private SqlSession sqlSession;
    private T cachedProxy;

    /**
     * 通过构造函数传入接口类型,通常由 BeanDefinition 的构造器参数提供。
     */
    public MapperFactoryBean(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    @Override
    public void afterPropertiesSet() {
        Assert.notNull(mapperInterface, "Mapper interface must not be null");
        Assert.isTrue(mapperInterface.isInterface(),
                "MapperFactoryBean only supports interfaces, but " + mapperInterface + " is not");
        Assert.notNull(sqlSession, "SqlSession must not be null");
    }

    @Override
    @SuppressWarnings("unchecked")
    public T getObject() {
        if (cachedProxy == null) {
            cachedProxy = (T) Proxy.newProxyInstance(
                    mapperInterface.getClassLoader(),
                    new Class[]{mapperInterface},
                    new MapperProxy(sqlSession, mapperInterface)
            );
        }
        return cachedProxy;
    }

    @Override
    public Class<T> getObjectType() {
        return mapperInterface;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    public void setSqlSession(SqlSession sqlSession) {
        this.sqlSession = sqlSession;
    }
}

3.3 MapperProxy(优化:完善返回类型处理与参数映射)

package com.lightorm.spring.proxy;

import com.lightorm.core.annotation.Delete;
import com.lightorm.core.annotation.Insert;
import com.lightorm.core.annotation.Select;
import com.lightorm.core.annotation.Update;
import com.lightorm.core.session.SqlSession;

import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.*;
import java.util.*;

/**
 * Mapper 接口的 JDK 动态代理处理器。
 *
 * <p>优化:自动识别返回类型是集合还是单个对象,并支持简单对象映射。
 */
public class MapperProxy implements InvocationHandler {

    private final SqlSession sqlSession;
    private final Class<?> mapperInterface;
    private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();

    public MapperProxy(SqlSession sqlSession, Class<?> 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);
        }
        if (method.isDefault()) {
            return MethodHandles.lookup()
                    .findSpecial(method.getDeclaringClass(), method.getName(),
                            MethodType.methodType(method.getReturnType(), method.getParameterTypes()),
                            method.getDeclaringClass())
                    .bindTo(proxy)
                    .invokeWithArguments(args);
        }

        MapperMethod mapperMethod = cachedMapperMethod(method);
        return mapperMethod.execute(sqlSession, args, method.getReturnType());
    }

    private MapperMethod cachedMapperMethod(Method method) {
        return methodCache.computeIfAbsent(method, m -> new MapperMethod(mapperInterface, m));
    }

    /**
     * 封装一个 Mapper 方法对应的 SQL 命令。
     */
    private static class MapperMethod {
        private final SqlCommandType commandType;
        private final String sql;
        private final Class<?> entityClass; // 新增:映射实体类型,从泛型返回值推断

        MapperMethod(Class<?> mapperInterface, Method method) {
            if (method.isAnnotationPresent(Select.class)) {
                this.sql = method.getAnnotation(Select.class).value();
                this.commandType = SqlCommandType.SELECT;
            } else if (method.isAnnotationPresent(Insert.class)) {
                this.sql = method.getAnnotation(Insert.class).value();
                this.commandType = SqlCommandType.INSERT;
            } else if (method.isAnnotationPresent(Update.class)) {
                this.sql = method.getAnnotation(Update.class).value();
                this.commandType = SqlCommandType.UPDATE;
            } else if (method.isAnnotationPresent(Delete.class)) {
                this.sql = method.getAnnotation(Delete.class).value();
                this.commandType = SqlCommandType.DELETE;
            } else {
                throw new IllegalStateException("No SQL annotation found on method " + method.getName());
            }
            // 推断实体类型
            this.entityClass = resolveEntityClass(method);
        }

        /**
         * 从方法返回值泛型中解析实体类型。例如 List<User> -> User.class。
         */
        private Class<?> resolveEntityClass(Method method) {
            Type returnType = method.getGenericReturnType();
            if (returnType instanceof ParameterizedType) {
                Type[] actualTypeArgs = ((ParameterizedType) returnType).getActualTypeArguments();
                if (actualTypeArgs.length > 0 && actualTypeArgs[0] instanceof Class) {
                    return (Class<?>) actualTypeArgs[0];
                }
            }
            // 否则可能是单个实体
            return method.getReturnType();
        }

        Object execute(SqlSession sqlSession, Object[] args, Class<?> returnType) {
            switch (commandType) {
                case SELECT:
                    if (Collection.class.isAssignableFrom(returnType)) {
                        return sqlSession.selectList(sql, args, entityClass);
                    } else {
                        return sqlSession.selectOne(sql, args, entityClass);
                    }
                case INSERT:
                    return sqlSession.insert(sql, args);
                case UPDATE:
                    return sqlSession.update(sql, args);
                case DELETE:
                    return sqlSession.delete(sql, args);
                default:
                    throw new UnsupportedOperationException();
            }
        }
    }

    private enum SqlCommandType {
        SELECT, INSERT, UPDATE, DELETE
    }
}

Spring 扩展点体现

  • FactoryBean<T>:Spring 中用于创建复杂 Bean 的核心接口。Spring 自身大量使用(如 SqlSessionFactoryBeanProxyFactoryBean)。此处返回代理对象,通过 Spring 的 getBean 即可获得 Mapper 实现。
  • InitializingBean:属性设置完成后回调,确保 SqlSession 已注入。
  • JDK 动态代理:体现了 AOP 核心思想,通过代理拦截方法调用并织入横切逻辑。这里的 MapperProxy 相当于一个 MethodInterceptor,和 Spring AOP 的 JdkDynamicAopProxy 设计思想一致,但更简化。

4. SqlSession 与 SQL 执行器

4.1 SqlSession 接口(优化:支持实体类型参数)

package com.lightorm.core.session;

import java.sql.Connection;
import java.util.List;

public interface SqlSession {

    <T> T selectOne(String sql, Object[] params, Class<T> entityClass);

    <E> List<E> selectList(String sql, Object[] params, Class<E> entityClass);

    int insert(String sql, Object[] params);

    int update(String sql, Object[] params);

    int delete(String sql, Object[] params);

    Connection getConnection();

    void close();
}

4.2 DefaultSqlSession(优化:连接获取委托给事务管理器,解耦连接创建)

package com.lightorm.core.session;

import com.lightorm.core.executor.Executor;
import com.lightorm.core.executor.SimpleExecutor;
import com.lightorm.core.transaction.TransactionContext;
import com.lightorm.core.exception.DataAccessException;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

public class DefaultSqlSession implements SqlSession {

    private final DataSource dataSource;
    private final Executor executor;
    private boolean closed;

    public DefaultSqlSession(DataSource dataSource) {
        this.dataSource = dataSource;
        this.executor = new SimpleExecutor();
    }

    @Override
    public <T> T selectOne(String sql, Object[] params, Class<T> entityClass) {
        List<T> list = selectList(sql, params, entityClass);
        if (list.size() == 1) {
            return list.get(0);
        } else if (list.isEmpty()) {
            return null;
        } else {
            throw new DataAccessException("Expected one result, but got " + list.size());
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public <E> List<E> selectList(String sql, Object[] params, Class<E> entityClass) {
        return (List<E>) execute(sql, params, QueryType.SELECT, entityClass);
    }

    @Override
    public int insert(String sql, Object[] params) {
        return (int) execute(sql, params, QueryType.INSERT, null);
    }

    @Override
    public int update(String sql, Object[] params) {
        return (int) execute(sql, params, QueryType.UPDATE, null);
    }

    @Override
    public int delete(String sql, Object[] params) {
        return (int) execute(sql, params, QueryType.DELETE, null);
    }

    private Object execute(String sql, Object[] params, QueryType queryType, Class<?> entityClass) {
        assertNotClosed();
        try {
            // 尝试获取当前线程绑定的连接(事务中)
            Connection conn = TransactionContext.getCurrentConnection();
            boolean isTransactional = (conn != null);
            if (conn == null) {
                // 非事务场景,从数据源获取独立连接
                conn = dataSource.getConnection();
            }
            try {
                switch (queryType) {
                    case SELECT:
                        return executor.query(conn, sql, params, entityClass);
                    case INSERT:
                    case UPDATE:
                    case DELETE:
                        return executor.update(conn, sql, params, queryType);
                    default:
                        throw new DataAccessException("Unknown query type");
                }
            } finally {
                // 非事务连接需在此关闭,事务连接由事务管理器管理
                if (!isTransactional && conn != null) {
                    conn.close();
                }
            }
        } catch (SQLException e) {
            throw new DataAccessException("Error executing SQL", e);
        }
    }

    @Override
    public Connection getConnection() {
        try {
            // 优先返回事务连接,否则新建
            Connection txConn = TransactionContext.getCurrentConnection();
            if (txConn != null) {
                return txConn;
            }
            return dataSource.getConnection();
        } catch (SQLException e) {
            throw new DataAccessException("Cannot get connection", e);
        }
    }

    @Override
    public void close() {
        this.closed = true;
    }

    private void assertNotClosed() {
        if (closed) throw new DataAccessException("SqlSession is closed");
    }

    private enum QueryType { SELECT, INSERT, UPDATE, DELETE }
}

4.3 Executor 与 SimpleExecutor(优化:使用 TypeHandlerRegistry 进行完整映射)

package com.lightorm.core.executor;

import com.lightorm.core.type.TypeHandlerRegistry;
import com.lightorm.core.type.TypeHandler;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.*;

public interface Executor {
    <E> List<E> query(Connection conn, String sql, Object[] params, Class<E> entityClass) throws SQLException;
    int update(Connection conn, String sql, Object[] params, QueryType queryType) throws SQLException;
}

public class SimpleExecutor implements Executor {

    private final TypeHandlerRegistry typeHandlerRegistry = TypeHandlerRegistry.getDefault();

    @Override
    public <E> List<E> query(Connection conn, String sql, Object[] params, Class<E> entityClass) throws SQLException {
        try (PreparedStatement ps = conn.prepareStatement(sql)) {
            setParameters(ps, params);
            try (ResultSet rs = ps.executeQuery()) {
                return mapResultSet(rs, entityClass);
            }
        }
    }

    private void setParameters(PreparedStatement ps, Object[] params) throws SQLException {
        if (params != null) {
            for (int i = 0; i < params.length; i++) {
                setParameter(ps, i + 1, params[i]);
            }
        }
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    private void setParameter(PreparedStatement ps, int index, Object value) throws SQLException {
        if (value == null) {
            ps.setNull(index, Types.NULL);
            return;
        }
        TypeHandler handler = typeHandlerRegistry.getTypeHandler(value.getClass());
        handler.setParameter(ps, index, value);
    }

    /**
     * 将 ResultSet 映射为实体列表。通过反射设置字段值。
     */
    private <E> List<E> mapResultSet(ResultSet rs, Class<E> entityClass) throws SQLException {
        List<E> results = new ArrayList<>();
        if (entityClass == null) return results; // 非查询操作无实体

        Field[] fields = entityClass.getDeclaredFields();
        while (rs.next()) {
            E instance = createInstance(entityClass);
            for (Field field : fields) {
                field.setAccessible(true);
                TypeHandler<?> handler = typeHandlerRegistry.getTypeHandler(field.getType());
                Object value = handler.getResult(rs, field.getName());
                try {
                    field.set(instance, value);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("Failed to set field " + field.getName(), e);
                }
            }
            results.add(instance);
        }
        return results;
    }

    private <E> E createInstance(Class<E> clazz) {
        try {
            Constructor<E> constructor = clazz.getDeclaredConstructor();
            constructor.setAccessible(true);
            return constructor.newInstance();
        } catch (Exception e) {
            throw new RuntimeException("Cannot create instance of " + clazz, e);
        }
    }

    @Override
    public int update(Connection conn, String sql, Object[] params, QueryType queryType) throws SQLException {
        try (PreparedStatement ps = conn.prepareStatement(sql)) {
            setParameters(ps, params);
            return ps.executeUpdate();
        }
    }
}

Spring 扩展点体现

  • 模板方法模式DefaultSqlSession.execute 定义了固定的步骤(获取连接、执行、异常翻译),具体执行委托给 Executor。与 JdbcTemplateexecute 方法类似。
  • 资源管理:通过 TransactionContext 判断是否为事务连接,非事务连接在 finally 中自动关闭,避免泄漏。
  • 异常翻译:将 SQLException 转换为 DataAccessException(非检查异常),与 Spring 的 DataAccessException 层级理念一致。

5. 声明式事务支持(深度集成 Spring 事务管理器)

5.1 设计目标

实现与 Spring @Transactional 机制完全兼容的声明式事务管理。不再自行管理连接生命周期,而是委托给 Spring 的 PlatformTransactionManager,使 LightORM 能够无缝融入 Spring 的全局事务(支持与 JPA、JdbcTemplate 混用)。同时保留自定义 @LightTransactional 注解以演示原理。

5.2 核心组件

5.2.1 @LightTransactional 注解与 @EnableLightTransactionManagement

package com.lightorm.spring.annotation;

import com.lightorm.spring.transaction.LightTransactionManagementRegistrar;
import org.springframework.context.annotation.Import;

import java.lang.annotation.*;

/**
 * 声明式事务注解,可标注在方法或类上。
 * 与 Spring 的 @Transactional 类似,但内部实现利用 Spring 事务管理器。
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LightTransactional {
    // 可扩展 propagation, isolation 等属性,此处省略
}

/**
 * 启用 LightORM 声明式事务管理。
 * 通过 @Import 引入事务注册器,后者负责向容器注册事务 Advisor 和 Interceptor。
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(LightTransactionManagementRegistrar.class)
public @interface EnableLightTransactionManagement {
}

5.2.2 LightTransactionManagementRegistrar(替代之前的简单注册器)

package com.lightorm.spring.transaction;

import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

/**
 * 负责注册事务增强逻辑的 ImportBeanDefinitionRegistrar。
 * 注册一个 Advisor 用于拦截带有 @LightTransactional 注解的方法,
 * 该 Advisor 使用 Spring 的 PlatformTransactionManager 管理事务。
 */
public class LightTransactionManagementRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                                        BeanDefinitionRegistry registry) {
        // 注册事务拦截器
        BeanDefinition interceptorDef = BeanDefinitionBuilder
                .genericBeanDefinition(LightTransactionInterceptor.class)
                .addPropertyReference("transactionManager", "platformTransactionManager") // 依靠外部注入
                .getBeanDefinition();
        registry.registerBeanDefinition("lightTransactionInterceptor", interceptorDef);

        // 注册 Advisor:切点为 @LightTransactional
        BeanDefinition advisorDef = BeanDefinitionBuilder
                .genericBeanDefinition(DefaultPointcutAdvisor.class)
                .addConstructorArgReference("lightTransactionPointcut")
                .addConstructorArgReference("lightTransactionInterceptor")
                .getBeanDefinition();
        registry.registerBeanDefinition("lightTransactionAdvisor", advisorDef);

        // 注册切点
        BeanDefinition pointcutDef = BeanDefinitionBuilder
                .genericBeanDefinition(AspectJExpressionPointcut.class)
                .addPropertyValue("expression", "@annotation(com.lightorm.spring.annotation.LightTransactional)")
                .getBeanDefinition();
        registry.registerBeanDefinition("lightTransactionPointcut", pointcutDef);
    }
}

5.2.3 LightTransactionInterceptor(集成 PlatformTransactionManager)

package com.lightorm.spring.transaction;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

/**
 * 基于 Spring 事务管理器的事务拦截器。
 * 在方法调用前通过 PlatformTransactionManager 开启事务,
 * 方法成功则提交,异常则回滚。完全委托给 Spring 的事务基础设施。
 */
public class LightTransactionInterceptor implements MethodInterceptor {

    private PlatformTransactionManager transactionManager;

    public void setTransactionManager(PlatformTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
        // 可根据需要设置传播行为、隔离级别等
        definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus status = transactionManager.getTransaction(definition);
        try {
            Object result = invocation.proceed();
            transactionManager.commit(status);
            return result;
        } catch (Throwable ex) {
            transactionManager.rollback(status);
            throw ex;
        }
    }
}

5.2.4 TransactionContext 的优化

由于事务管理已交给 Spring,TransactionContext 可以退化为仅提供线索连接绑定,以便 DefaultSqlSession 在同一个事务中复用连接。更优雅的方式:直接通过 DataSourceUtils.getConnection(dataSource) 利用 Spring 的事务同步管理器。此处为了说明原理,保留 TransactionContext,但建议在 Spring 环境下使用 Spring 内置机制。

// TransactionContext 保留,但推荐在自动配置中启用 Spring 事务同步
public class TransactionContext {
    private static final ThreadLocal<Connection> currentConnection = new ThreadLocal<>();
    // ... 同前,但应说明建议用 DataSourceUtils
}

Spring 扩展点体现

  • AOP 代理:通过 AdvisorMethodInterceptor 创建 AOP 代理,类似于 Spring 内部的 BeanFactoryTransactionAttributeSourceAdvisorTransactionInterceptor
  • ImportBeanDefinitionRegistrar + @Enable:模块化驱动注册 AOP 组件,和 @EnableTransactionManagement 导入 TransactionManagementConfigurationSelector 的机制一致。
  • 代理创建:Spring 容器会自动检测容器中的 Advisor,通过 AbstractAutoProxyCreator 为匹配切点的 Bean 创建代理,无需手动 BeanPostProcessor

6. 类型转换与结果映射(增强扩展性)

6.1 设计目标

实现一个可扩展的 TypeHandler 体系,支持 Java 类型与 JDBC 类型之间的相互转换,并通过 TypeHandlerRegistry 管理。用户可以自定义 TypeHandler 并通过配置注册,处理枚举、JSON 等复杂类型。

6.2 TypeHandler 接口与实现

package com.lightorm.core.type;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public interface TypeHandler<T> {
    T getResult(ResultSet rs, String columnName) throws SQLException;
    void setParameter(PreparedStatement ps, int index, T value) throws SQLException;
}

内建实现(示例):

public class StringTypeHandler implements TypeHandler<String> {
    public String getResult(ResultSet rs, String columnName) throws SQLException {
        return rs.getString(columnName);
    }
    public void setParameter(PreparedStatement ps, int index, String value) throws SQLException {
        ps.setString(index, value);
    }
}
// IntegerTypeHandler、LongTypeHandler、DateTypeHandler 类似

6.3 TypeHandlerRegistry(扩展点:支持 SPI 加载用户自定义类型处理器)

package com.lightorm.core.type;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 类型处理器注册表,维护 Java 类型到 TypeHandler 的映射。
 * 支持从 SPI 加载自定义 TypeHandler,允许用户扩展。
 */
public class TypeHandlerRegistry {

    private final Map<Class<?>, TypeHandler<?>> handlerMap = new ConcurrentHashMap<>();

    private static final TypeHandlerRegistry INSTANCE = new TypeHandlerRegistry();

    private TypeHandlerRegistry() {
        // 注册默认处理器
        register(String.class, new StringTypeHandler());
        register(Integer.class, new IntegerTypeHandler());
        register(int.class, new IntegerTypeHandler());
        register(Long.class, new LongTypeHandler());
        register(long.class, new LongTypeHandler());
        register(Date.class, new DateTypeHandler());
        // 加载 SPI 扩展(如 META-INF/services/com.lightorm.core.type.TypeHandler)
        loadSpiExtensions();
    }

    public static TypeHandlerRegistry getDefault() {
        return INSTANCE;
    }

    /**
     * 注册一个类型处理器。
     */
    public <T> void register(Class<T> javaType, TypeHandler<T> handler) {
        handlerMap.put(javaType, handler);
    }

    @SuppressWarnings("unchecked")
    public <T> TypeHandler<T> getTypeHandler(Class<T> javaType) {
        TypeHandler<T> handler = (TypeHandler<T>) handlerMap.get(javaType);
        if (handler == null && javaType.isEnum()) {
            // 自动为枚举类型创建默认处理器(存储枚举名)
            handler = (TypeHandler<T>) new EnumTypeHandler(javaType);
            register(javaType, handler);
        }
        if (handler == null) {
            throw new IllegalArgumentException("No TypeHandler for " + javaType);
        }
        return handler;
    }

    /**
     * 通过 Java SPI 机制加载自定义 TypeHandler。
     */
    private void loadSpiExtensions() {
        ServiceLoader<TypeHandler> loader = ServiceLoader.load(TypeHandler.class);
        for (TypeHandler<?> handler : loader) {
            // 简单演示:要求自定义处理器实现 TypeHandler 并标注 @HandlesType
            HandlesType annotation = handler.getClass().getAnnotation(HandlesType.class);
            if (annotation != null) {
                register(annotation.value(), handler);
            }
        }
    }
}

自定义处理器示例:

@HandlesType(LocalDateTime.class)
public class LocalDateTimeTypeHandler implements TypeHandler<LocalDateTime> {
    @Override
    public LocalDateTime getResult(ResultSet rs, String columnName) throws SQLException {
        Timestamp ts = rs.getTimestamp(columnName);
        return ts != null ? ts.toLocalDateTime() : null;
    }
    @Override
    public void setParameter(PreparedStatement ps, int index, LocalDateTime value) throws SQLException {
        ps.setTimestamp(index, Timestamp.valueOf(value));
    }
}

META-INF/services/com.lightorm.core.type.TypeHandler 文件中添加:

com.yourpackage.LocalDateTimeTypeHandler

这样当调用 TypeHandlerRegistry.getDefault() 时,会自动加载并注册。

Spring 扩展点体现

  • 策略模式TypeHandler 接口作为策略,TypeHandlerRegistry 作为上下文,根据类型选择具体策略,与 Spring 的 ConversionServiceConverter 设计类似。
  • SPI 加载:利用 JDK 内置的 SPI 机制扩展类型处理器,Spring 自身也通过 SPI(如 SpringFactoriesLoader)加载扩展。此处演示了一种简单的扩展点,类似 Spring 的 ConverterRegistry 通过 addConverter 注册。

7. Spring Boot 自动配置(逐步剖析)

7.1 自动配置类(优化:条件化并集成 PlatformTransactionManager)

package com.lightorm.spring.boot.autoconfigure;

import com.lightorm.core.session.DefaultSqlSession;
import com.lightorm.core.session.SqlSession;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

/**
 * LightORM 自动配置。
 * 条件:
 * - classpath 中有 DataSource
 * - 未自定义 SqlSession Bean
 * - 存在事务管理器(用于声明式事务,如果没有则暂不支持事务切面)
 *
 * 该配置会创建:
 * - SqlSession (DefaultSqlSession)
 * - 触发事务 Advisor 注册(由 @EnableLightTransactionManagement 驱动)
 */
@AutoConfiguration
@ConditionalOnClass(DataSource.class)
@EnableConfigurationProperties(LightOrmProperties.class)
public class LightOrmAutoConfiguration {

    /**
     * 创建 SqlSession,需要 DataSource 注入。
     * 当用户未自定义 SqlSession 时生效。
     */
    @Bean
    @ConditionalOnMissingBean
    public SqlSession sqlSession(DataSource dataSource) {
        DefaultSqlSession session = new DefaultSqlSession(dataSource);
        if (lightOrmProperties.isShowSql()) {
            // 可包装 logging 装饰器
        }
        return session;
    }

    // 如果用户需要事务支持,容器中应有 PlatformTransactionManager(Spring Boot 自动配置提供)
    // LightTransactionInterceptor 会通过 @EnableLightTransactionManagement 注册,在 Advisor 中引用 platformTransactionManager

    // 将 LightOrmProperties 注入为 Bean,方便其他组件读取
    @Bean
    @ConditionalOnMissingBean
    public LightOrmProperties lightOrmProperties() {
        return new LightOrmProperties();
    }
}

7.2 LightOrmProperties

package com.lightorm.spring.boot.autoconfigure;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "light-orm")
public class LightOrmProperties {

    /** Mapper 接口所在包(可配合自动扫描使用,若使用 @MapperScan 则非必需) */
    private String[] basePackages;

    /** SQL 方言,默认 MySQL,实际未完全实现 */
    private String sqlDialect = "MySQL";

    /** 是否打印 SQL 语句到控制台 */
    private boolean showSql = false;

    // getters and setters ...
}

7.3 自动配置注册文件

META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 内容:

com.lightorm.spring.boot.autoconfigure.LightOrmAutoConfiguration

对于 Spring Boot 2.x 兼容,可保留 spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.lightorm.spring.boot.autoconfigure.LightOrmAutoConfiguration

Spring 扩展点体现

  • @AutoConfiguration@ConditionalOnClass / @ConditionalOnMissingBean:条件化装配,与 Spring Boot 内置的 DataSourceAutoConfigurationJdbcTemplateAutoConfiguration 类似。
  • @ConfigurationProperties:类型安全的属性绑定,与 spring.datasource 等配置原理相同。
  • AutoConfiguration.imports:Spring Boot 3.x 新的注册方式,更清晰。

8. 事件机制应用(完整上下文集成)

8.1 MapperRegisteredEvent

package com.lightorm.spring.event;

import org.springframework.context.ApplicationEvent;

/**
 * 当一个新的 Mapper 接口被扫描并注册到 Spring 容器后发布的事件。
 */
public class MapperRegisteredEvent extends ApplicationEvent {
    private final Class<?> mapperInterface;
    private final String beanName;

    public MapperRegisteredEvent(Object source, Class<?> mapperInterface, String beanName) {
        super(source);
        this.mapperInterface = mapperInterface;
        this.beanName = beanName;
    }

    public Class<?> getMapperInterface() { return mapperInterface; }
    public String getBeanName() { return beanName; }
}

8.2 在 Scanner 中发布事件(已实现在 ClassPathMapperScanner 中)

8.3 @MapperWarmUp 与 @EventListener

package com.lightorm.spring.event;

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

/**
 * 监听 Mapper 注册事件,进行预热操作。
 */
@Component
public class MapperWarmUpListener {

    @EventListener
    public void onMapperRegistered(MapperRegisteredEvent event) {
        // 例如:提前解析 SQL、加载元数据、缓存 MapperMethod
        System.out.printf("Mapper registered: bean=%s interface=%s%n",
                event.getBeanName(), event.getMapperInterface().getName());
    }
}

Spring 扩展点体现

  • ApplicationEvent:Spring 事件模型的基本抽象。
  • @EventListener:注解驱动事件监听,替代 ApplicationListener 接口,解耦组件。
  • 该机制可用于框架内部的异步初始化、统计等,类似 Spring 内部的 ContextRefreshedEvent 应用。

9. 自定义扩展点(深度实践)

9.1 SqlSessionFactoryBean(可选的扩展创建方式)

package com.lightorm.spring.factory;

import com.lightorm.core.session.DefaultSqlSession;
import com.lightorm.core.session.SqlSession;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;

import javax.sql.DataSource;

public class SqlSessionFactoryBean implements FactoryBean<SqlSession>, InitializingBean {
    private DataSource dataSource;
    private SqlSession sqlSession;

    @Override
    public void afterPropertiesSet() {
        this.sqlSession = new DefaultSqlSession(dataSource);
    }

    @Override
    public SqlSession getObject() {
        return sqlSession;
    }

    @Override
    public Class<?> getObjectType() {
        return SqlSession.class;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
}

9.2 MapperBeanProcessor(展示 BeanPostProcessor)

package com.lightorm.spring.processor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

/**
 * 对所有的 Mapper 代理 Bean 进行后处理,例如添加日志或性能监控装饰。
 * 通过判断 Bean 是否为 MapperFactoryBean 创建的代理(可根据包名或标记接口)来增强。
 */
public class MapperBeanProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean.getClass().getName().contains("$Proxy") &&
                bean.getClass().getInterfaces().length > 0 &&
                bean.getClass().getInterfaces()[0].getPackage().getName().startsWith("com.example.demo.mapper")) {
            // 示例:打印日志
            System.out.println("Mapper proxy bean [" + beanName + "] initialized.");
        }
        return bean;
    }
}

将此处理器注册为 Bean 即可参与容器生命周期。

Spring 扩展点体现

  • FactoryBean + InitializingBean:组合使用提供自定义对象创建和初始化回调。
  • BeanPostProcessor:参与所有 Bean 的初始化过程,是 AOP、@Autowired@PostConstruct 等功能的基石。Spring 内部有几十个 BeanPostProcessor 实现,如 AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor

10. 完整可运行 Demo 设计

与初版相同,增加事务测试用例。

UserServiceTest 事务回滚示例

@Test
void testTransactionRollback() {
    UserService userService = context.getBean(UserService.class);
    assertThrows(RuntimeException.class, () -> userService.transferAge(1L, 2L, -5));
    // 验证数据未被修改(查询数据库确认年龄未变)
}

配置文件中启用事务注解:

@SpringBootApplication
@MapperScan("com.example.demo.mapper")
@EnableLightTransactionManagement  // 开启 LightORM 事务
public class DemoApplication { ... }

Spring Boot 会根据 DataSource 自动配置 PlatformTransactionManager,LightORM 的事务拦截器将与之集成。


11. README 内容(扩展版)

篇幅所限,将扩展版 README 摘要如下,实际可写入项目 README.md:

  • 项目简介、架构图(Mermaid)、序列图
  • 快速开始:添加依赖、配置数据源、编写 Mapper 接口、开启扫描、注入使用
  • 详细说明每个扩展点的用途及其在 Spring 源码中的参照
  • 事务使用:@LightTransactional 注解,需要 @EnableLightTransactionManagement
  • 类型处理器扩展:实现 TypeHandler 接口,通过 SPI 或手动注册
  • 和 MyBatis-Spring 的对比表格(映射机制、事务处理、自动配置方式)
  • 常见问题:如何调试 SQL、如何兼容 Spring Boot 2.x

12. 核心源码实现

在前述各节中已经补全了关键源码。此节进行集中展示和讲解,梳理核心类之间的调用关系。

12.1 启动与扫描阶段

@MapperScan (basePackages = "com.example.mapper")
   -> @Import(MapperScannerRegistrar.class)
        -> registerBeanDefinitions()
             -> ClassPathMapperScanner.doScan()
                  -> 找到所有接口,将 BeanDefinition 替换为 MapperFactoryBean
                  -> publishEvent(MapperRegisteredEvent)

Spring 在处理 @Import 时会调用 ImportBeanDefinitionRegistrarregisterBeanDefinitions,此时可以拿到 BeanDefinitionRegistry,进行编程式注册。这与 @ComponentScan 的处理流程(ComponentScanAnnotationParser -> ClassPathBeanDefinitionScanner)完全类似。

12.2 Mapper 代理创建

容器实例化 MapperFactoryBean (FactoryBean)
   -> afterPropertiesSet() 验证
   -> getObject() 被调用时
        -> Proxy.newProxyInstance(..., new MapperProxy(sqlSession, mapperInterface))

MapperProxy 内部缓存 MapperMethod,每个方法对应一个 SQL 指令和实体类型。

12.3 方法调用链路

userMapper.selectById(1L)
   -> MapperProxy.invoke()
        -> 解析 @Select 注解,构建 MapperMethod(SELECT, SQL, User.class)
        -> sqlSession.selectOne(sql, args, entityClass)
             -> DefaultSqlSession.execute()
                  -> TransactionContext.getCurrentConnection() 检查事务连接
                  -> Executor.query(conn, sql, params, entityClass)
                       -> TypeHandlerRegistry 设置参数,执行,映射结果
             -> 返回 User 对象

12.4 事务拦截链路

@LightTransactional 方法调用
   -> LightTransactionInterceptor (Advisor 的 Advice)
        -> transactionManager.getTransaction(def)
        -> invocation.proceed() 执行原方法(内部调用 Mapper 方法)
        -> 提交或回滚

Spring 的 AbstractAutoProxyCreator 在创建 Bean 时检测到 Advisor,为匹配的 Bean 生成 AOP 代理,织入事务拦截器。


13. 总结与 Spring 知识巩固分析(深化对比)

通过设计 LightORM,我们实践并深化了以下 Spring 核心知识:

13.1 知识要点与 Spring 源码对比表

LightORM 组件使用的 Spring 扩展点Spring 内部对应实现对比分析
MapperScannerRegistrarImportBeanDefinitionRegistrarComponentScanRegistrar 处理 @ComponentScan两者都通过 @Import 触发,在 registerBeanDefinitions 中构建扫描器,修改 BeanDefinition。Spring 的扫描器基于 ClassPathBeanDefinitionScanner,默认过滤器为 @Component,LightORM 自定义了 @Mapper 过滤器。
MapperFactoryBeanFactoryBean + InitializingBeanSqlSessionFactoryBean (MyBatis-Spring 自身) 或 ProxyFactoryBeanFactoryBean 用于生成复杂对象。Spring AOP 的 ProxyFactoryBean 也返回代理对象。此处我们直接使用 JDK 动态代理,而 Spring 通常使用 ProxyFactory 结合 AOP 拦截器链。
MapperProxy (InvocationHandler)JDK 动态代理,AOP 思想JdkDynamicAopProxy (Spring AOP)两者都实现 InvocationHandler,区别在于 Spring AOP 会组装拦截器链(Advisor 集合),支持多增强;LightORM 直接执行 SQL,简化但明确。
LightTransactionInterceptor + AdvisorMethodInterceptor, AOP Advisor 注册TransactionInterceptor + BeanFactoryTransactionAttributeSourceAdvisorLightORM 简化版使用了相同的架构:Advisor = Pointcut + Advice。Spring 的 TransactionInterceptor 会解析 @Transactional 的属性(传播、隔离级别等),LightORM 若需要可扩展为同样支持。
TransactionContext (ThreadLocal)线程绑定资源TransactionSynchronizationManagerSpring 在事务管理中通过 TransactionSynchronizationManagerConnection 绑定到线程,并支持资源同步。LightORM 的 TransactionContext 模仿了其基本功能。
TypeHandlerRegistry策略模式 + SPIDefaultConversionService + Converter 注册两者都是类型转换注册表。Spring 的 ConversionService 可以注册任意转换器,而 LightORM 的 TypeHandler 专注于 JDBC 层面,但设计思想一致。
LightOrmAutoConfiguration@AutoConfiguration + 条件注解 + AutoConfiguration.importsDataSourceAutoConfiguration完全遵循 Spring Boot 自动配置规范,根据类路径和已有 Bean 条件化装配。
MapperRegisteredEvent + @EventListener事件发布/监听ContextRefreshedEventSpring 的事件机制基于 ApplicationEventMulticaster,LightORM 复用相同的基类,展示了事件的解耦能力。
MapperBeanProcessorBeanPostProcessorAutowiredAnnotationBeanPostProcessor所有 BeanPostProcessor 都在 Bean 初始化前后被调用,Spring 利用它们进行依赖注入、代理生成等。LightORM 的处理器展示了可在此阶段进行自定义增强。
@MapperScan / @EnableLightTransactionManagement@Import + 模块驱动注解@EnableTransactionManagement, @EnableCachingSpring 的 @Enable* 注解通常导入一个 ImportSelectorRegistrar,动态注册必要的组件,使模块化功能开启更加便利。

13.2 设计模式汇总

  • 工厂模式MapperFactoryBeanSqlSessionFactoryBean 用于创建复杂对象。
  • 代理模式:JDK 动态代理实现 Mapper 接口的 SQL 接入。
  • 模板方法模式DefaultSqlSession.execute() 定义流程骨架,Executor 实现细节。
  • 策略模式TypeHandler 接口定义策略,TypeHandlerRegistry 负责选择。
  • 观察者模式:事件机制,MapperRegisteredEvent 发布,@EventListener 监听。
  • 装饰器模式:事务 AOP 将事务管理装饰在原方法调用之外。

13.3 核心启示

  1. Spring 的扩展点不是孤立的ImportBeanDefinitionRegistrarFactoryBean 结合,可以完全控制 Bean 的定义和实例化过程,这是实现框架集成的基础。
  2. AOP 是横切逻辑的利器:通过动态代理和 MethodInterceptor,我们可以为 LightORM 添加事务、日志等能力,而无需修改核心代码。
  3. 自动配置遵循约定大于配置:通过条件注解和 spring.factories / AutoConfiguration.imports,框架能智能适配用户环境,这与 Spring Boot 的设计哲学高度一致。
  4. 理解 Spring 源码设计的关键在于掌握其扩展点:当我们使用 LightORM 这种方式学习时,会发现 Spring 本身的大量功能(如 MyBatis-Spring、Spring Data JPA)都是基于完全相同的一套扩展机制实现的。掌握这些扩展点,就等于掌握了 Spring 的“元语法”。

通过完成本项目的设计与优化,开发者将对 Spring 核心容器、AOP、事务、数据访问、自动配置等形成立体而深入的认识,为后续阅读 Spring 源码和开发定制框架打下坚实基础。