第二部分:设计篇 - IoC 容器架构设计
在第一部分中,我们理解了 IoC 和 AOP 的核心概念。现在,让我们开始设计自己的 IoC 容器。
第3章:IoC 容器核心架构
3.1 整体架构设计
一个完整的 IoC 容器需要以下核心组件:
┌─────────────────────────────────────────────────────────────────┐
│ 应用层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ @Service │ │ @Autowired │ │ @Aspect │ │
│ │ PlayerSvc │ │ Repository │ │ LogAspect │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ API 层 (common) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 注解定义 │ │ IoCAPI │ │ AopAPI │ │
│ │ @Service │ │ getBean() │ │createProxy()│ │
│ │ @Autowired │ │ register() │ │ parseAspect │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 核心层 (common) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │BeanFactory │ │BeanDefinition│ │Interceptor │ │
│ │ 创建/管理 │ │ Bean元数据 │ │ Chain │ │
│ │ Bean │ │ │ │ 拦截器链 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 运行时层 (runtime) │
│ 具体实现 │
└─────────────────────────────────────────────────────────────────┘
分层设计的好处
- API 层:定义接口和注解,不依赖具体实现
- 核心层:实现核心逻辑,平台无关
- 运行时层:适配不同平台(Spigot/Forge)
这种分层设计让我们可以:
- 在不同平台复用核心代码
- 轻松添加新平台支持
- 单独测试每一层
3.2 Bean 定义与元数据
什么是 BeanDefinition?
BeanDefinition 是描述一个 Bean 的所有信息的数据结构,包括:
- Bean 的类型(Class)
- Bean 的名称
- 作用域(单例/原型)
- 是否懒加载
- 依赖关系
- 初始化/销毁方法
kotlin
/**
* Bean 定义
* 描述一个 Bean 的所有元数据
*/
data class BeanDefinition(
/** Bean 的类型 */
val beanClass: Class<*>,
/** Bean 的名称(默认为类名首字母小写) */
val beanName: String = beanClass.simpleName.replaceFirstChar { it.lowercase() },
/** 作用域 */
val scope: BeanScope = BeanScope.SINGLETON,
/** 是否懒加载 */
val lazy: Boolean = false,
/** 依赖的其他 Bean 类型 */
val dependencies: List<Class<*>> = emptyList(),
/** 初始化方法名 */
val initMethod: String? = null,
/** 销毁方法名 */
val destroyMethod: String? = null
)
/**
* Bean 作用域
*/
enum class BeanScope {
/** 单例:整个容器只有一个实例 */
SINGLETON,
/** 原型:每次获取都创建新实例 */
PROTOTYPE
}
Bean 的生命周期
一个 Bean 从创建到销毁,经历以下阶段:
┌─────────────────────────────────────────────────────────────────┐
│ Bean 生命周期 │
└─────────────────────────────────────────────────────────────────┘
1. 实例化 2. 属性填充 3. 初始化
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 创建对象 │ ────────▶ │ 注入依赖 │ ────────▶ │ @PostConstr │
│ new Bean() │ │ @Autowired │ │ uct │
└─────────────┘ └─────────────┘ └─────────────┘
│
▼
6. 销毁 5. 使用中 4. 就绪
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ @PreDestroy │ ◀──────── │ 业务调用 │ ◀──────── │ 放入容器 │
│ 容器关闭 │ │ │ │ │
└─────────────┘ └─────────────┘ └─────────────┘
3.3 注解设计
我们需要设计一套注解来标记 Bean 和注入点。
Bean 标记注解
kotlin
/**
* 标记一个类为 Service Bean
* 通常用于业务逻辑层
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class Service(
/** Bean 名称,默认为类名首字母小写 */
val value: String = ""
)
/**
* 标记一个类为 Component Bean
* 通用组件标记
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class Component(
val value: String = ""
)
/**
* 标记一个类为 Repository Bean
* 通常用于数据访问层
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class Repository(
val value: String = ""
)
依赖注入注解
kotlin
/**
* 字段注入
* 标记需要自动注入的字段
*/
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
annotation class Autowired(
/** 是否必须,如果为 true 且找不到 Bean 则抛异常 */
val required: Boolean = true
)
/**
* 构造器注入
* 标记使用哪个构造器进行依赖注入
*/
@Target(AnnotationTarget.CONSTRUCTOR)
@Retention(AnnotationRetention.RUNTIME)
annotation class Inject
/**
* 配置值注入
* 从配置文件注入值
*/
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
annotation class Value(
/** 配置路径,如 "database.host" */
val value: String
)
我们的场景只考虑@Autowired,因为我们使用Kotlin,由于Kotlin Object单例类型的存在构造函数注入和配置值注入已经很少使用到
生命周期注解
kotlin
/**
* 初始化回调
* Bean 创建并注入依赖后调用
*/
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class PostConstruct
/**
* 销毁回调
* 容器关闭时调用
*/
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class PreDestroy
作用域注解
kotlin
/**
* 单例作用域(默认)
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class Singleton
/**
* 原型作用域
* 每次获取都创建新实例
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class Prototype
/**
* 懒加载
* 第一次使用时才创建
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class Lazy
使用示例
kotlin
@Service
@Singleton
class PlayerService(
@Autowired private val repository: PlayerRepository,
@Value("game.max-players") private val maxPlayers: Int
) {
@Autowired
private lateinit var logger: Logger
@PostConstruct
fun init() {
logger.info("PlayerService 初始化完成,最大玩家数: $maxPlayers")
}
@PreDestroy
fun cleanup() {
logger.info("PlayerService 正在关闭...")
}
fun getPlayer(uuid: String): PlayerData? {
return repository.findByUUID(uuid)
}
}
@Repository
@Lazy // 懒加载,第一次使用时才连接数据库
class PlayerRepository(
@Autowired private val database: Database
) {
fun findByUUID(uuid: String): PlayerData? {
return database.query("SELECT * FROM players WHERE uuid = ?", uuid)
}
}
第4章:Bean 工厂设计
Bean 工厂是 IoC 容器的核心,负责创建和管理所有的 Bean。
4.1 BeanFactory 接口设计
kotlin
/**
* Bean 工厂接口
* 负责 Bean 的创建、获取和管理
*/
interface BeanFactory {
/**
* 根据类型获取 Bean
* @param type Bean 的类型
* @return Bean 实例,如果不存在返回 null
*/
fun <T> getBean(type: Class<T>): T?
/**
* 根据名称和类型获取 Bean
* @param name Bean 的名称
* @param type Bean 的类型
* @return Bean 实例,如果不存在返回 null
*/
fun <T> getBean(name: String, type: Class<T>): T?
/**
* 检查是否包含指定名称的 Bean
*/
fun containsBean(name: String): Boolean
/**
* 检查是否包含指定类型的 Bean
*/
fun containsBean(type: Class<*>): Boolean
/**
* 获取指定类型的所有 Bean
* 用于获取某个接口的所有实现
*/
fun <T> getBeansOfType(type: Class<T>): List<T>
/**
* 注册一个 Bean 定义
*/
fun registerBeanDefinition(definition: BeanDefinition)
/**
* 注册一个已存在的实例作为 Bean
*/
fun registerSingleton(name: String, instance: Any)
}
容器管理器设计
为了方便全局访问,我们设计一个静态的容器管理器,内部维护若干哈希表:
kotlin
/**
* IoC 容器管理器
* 提供全局访问点
*/
object IoCContainerManager {
/** Bean 定义注册表 */
private val beanDefinitions = ConcurrentHashMap<String, BeanDefinition>()
/** 单例 Bean 缓存 */
private val singletonBeans = ConcurrentHashMap<String, Any>()
/** 类型到名称的映射(用于按类型查找) */
private val typeToNames = ConcurrentHashMap<Class<*>, MutableList<String>>()
/**
* 根据类型获取 Bean
*/
@Suppress("UNCHECKED_CAST")
fun <T> getBean(type: Class<T>): T? {
// 1. 先从单例缓存查找
val names = typeToNames[type] ?: return null
if (names.isEmpty()) return null
val name = names.first()
return singletonBeans[name] as? T ?: createBean(name)
}
/**
* 注册 Bean 定义
*/
fun registerBeanDefinition(definition: BeanDefinition) {
val name = definition.beanName
beanDefinitions[name] = definition
// 建立类型映射
typeToNames.getOrPut(definition.beanClass) { mutableListOf() }.add(name)
// 同时映射所有父类和接口
registerTypeHierarchy(definition.beanClass, name)
}
/**
* 注册类型层次结构
*/
private fun registerTypeHierarchy(clazz: Class<*>, beanName: String) {
// 注册父类
clazz.superclass?.let { superClass ->
if (superClass != Any::class.java) {
typeToNames.getOrPut(superClass) { mutableListOf() }.add(beanName)
registerTypeHierarchy(superClass, beanName)
}
}
// 注册接口
for (iface in clazz.interfaces) {
typeToNames.getOrPut(iface) { mutableListOf() }.add(beanName)
}
}
/**
* 初始化所有非懒加载的单例 Bean
*/
fun initializeEagerBeans() {
for ((name, definition) in beanDefinitions) {
if (definition.scope == BeanScope.SINGLETON && !definition.lazy) {
if (!singletonBeans.containsKey(name)) {
createBean(name)
}
}
}
}
}
4.2 Bean 创建流程
Bean 的创建是一个复杂的过程,需要处理依赖注入、生命周期回调等。
kotlin
/**
* 创建 Bean 实例
*/
@Suppress("UNCHECKED_CAST")
private fun <T> createBean(name: String): T? {
val definition = beanDefinitions[name] ?: return null
// 1. 检查是否正在创建(循环依赖检测)
if (beansInCreation.contains(name)) {
throw BeanCreationException("检测到循环依赖: $name")
}
beansInCreation.add(name)
try {
// 2. 实例化
val instance = instantiate(definition)
// 3. 提前暴露(解决循环依赖)
if (definition.scope == BeanScope.SINGLETON) {
earlySingletonBeans[name] = instance
}
// 4. 属性填充(依赖注入)
populateBean(instance, definition)
// 5. 初始化
initializeBean(instance, definition)
// 6. 如果是单例,放入缓存
if (definition.scope == BeanScope.SINGLETON) {
singletonBeans[name] = instance
earlySingletonBeans.remove(name)
}
return instance as T
} finally {
beansInCreation.remove(name)
}
}
实例化阶段
kotlin
/**
* 实例化 Bean
*/
private fun instantiate(definition: BeanDefinition): Any {
val clazz = definition.beanClass
// 检查是否是 Kotlin object
if (IoCAPI.get().isKotlinObject(clazz)) {
// 通过反射获取实例
return IoCAPI.get().getKotlinInstance(clazz)
?: throw BeanCreationException("无法获取 Kotlin object 实例: ${clazz.name}")
}
// 查找构造器
val constructor = findConstructor(clazz)
// 解析构造器参数
val args = resolveConstructorArgs(constructor)
// 通过反射创建实例
return if (args.isEmpty()) {
IoCAPI.get().createInstance(clazz)
} else {
IoCAPI.get().createInstanceWithArgs(clazz, *args.toTypedArray())
}
}
/**
* 查找合适的构造器
* 优先级:@Inject 标记的 > 参数最多的 > 无参构造器
*/
private fun findConstructor(clazz: Class<*>): Constructor<*> {
// 1. 查找 @Inject 标记的构造器
val injectConstructor = IoCAPI.get().getConstructorWithAnnotation(clazz, Inject::class.java)
if (injectConstructor != null) return injectConstructor
// 2. 查找参数最多的构造器(Kotlin 主构造器通常参数最多)
val primaryConstructor = IoCAPI.get().getPrimaryConstructor(clazz)
if (primaryConstructor != null) return primaryConstructor
// 3. 使用无参构造器
return clazz.getDeclaredConstructor()
}
/**
* 解析构造器参数
*/
private fun resolveConstructorArgs(constructor: Constructor<*>): List<Any?> {
return constructor.parameters.map { param ->
// 检查 @Value 注解
val valueAnnotation = param.getAnnotation(Value::class.java)
if (valueAnnotation != null) {
return@map resolveValue(valueAnnotation.value, param.type)
}
// 检查 @Autowired 注解
val autowired = param.getAnnotation(Autowired::class.java)
val required = autowired?.required ?: true
// 按类型查找 Bean
val bean = getBean(param.type)
if (bean == null && required) {
throw BeanCreationException("无法解析构造器参数: ${param.type.name}")
}
bean
}
}
属性填充阶段
kotlin
/**
* 填充 Bean 属性(字段注入)
*/
private fun populateBean(instance: Any, definition: BeanDefinition) {
val clazz = definition.beanClass
// 获取所有 @Autowired 字段
val autowiredFields = IoCAPI.get().getFieldsWithAnnotation(clazz, Autowired::class.java)
for (field in autowiredFields) {
val annotation = field.getAnnotation(Autowired::class.java)
val required = annotation?.required ?: true
// 按类型查找 Bean
val bean = getBean(field.type)
if (bean == null && required) {
throw BeanCreationException("无法注入字段 ${field.name}: 找不到类型 ${field.type.name} 的 Bean")
}
if (bean != null) {
IoCAPI.get().setField(instance, field.name, bean)
}
}
// 获取所有 @Value 字段
val valueFields = IoCAPI.get().getFieldsWithAnnotation(clazz, Value::class.java)
for (field in valueFields) {
val annotation = field.getAnnotation(Value::class.java)
val value = resolveValue(annotation.value, field.type)
IoCAPI.get().setField(instance, field.name, value)
}
}
初始化阶段
kotlin
/**
* 初始化 Bean
*/
private fun initializeBean(instance: Any, definition: BeanDefinition) {
val clazz = definition.beanClass
// 调用 @PostConstruct 方法
val postConstructMethods = IoCAPI.get().getMethodsWithAnnotation(clazz, PostConstruct::class.java)
for (method in postConstructMethods) {
IoCAPI.get().invokeMethod(instance, method.name)
}
// 调用自定义初始化方法
definition.initMethod?.let { methodName ->
IoCAPI.get().invokeMethod(instance, methodName)
}
}
4.3 循环依赖处理
循环依赖是 IoC 容器必须处理的问题。
什么是循环依赖?
kotlin
@Service
class ServiceA(
@Autowired private val serviceB: ServiceB // A 依赖 B
)
@Service
class ServiceB(
@Autowired private val serviceA: ServiceA // B 依赖 A
)
创建 A 时需要 B,创建 B 时又需要 A,形成死循环。
三级缓存解决方案
Spring 使用三级缓存来解决循环依赖,我们采用简化版的两级缓存:
kotlin
object IoCContainerManager {
/** 一级缓存:完全初始化的单例 Bean */
private val singletonBeans = ConcurrentHashMap<String, Any>()
/** 二级缓存:提前暴露的 Bean(已实例化但未完成初始化) */
private val earlySingletonBeans = ConcurrentHashMap<String, Any>()
/** 正在创建中的 Bean 名称集合 */
private val beansInCreation = ConcurrentHashSet<String>()
/**
* 获取 Bean(支持循环依赖)
*/
fun <T> getBean(name: String): T? {
// 1. 先从一级缓存获取
singletonBeans[name]?.let { return it as T }
// 2. 再从二级缓存获取(解决循环依赖)
earlySingletonBeans[name]?.let { return it as T }
// 3. 创建新的 Bean
return createBean(name)
}
}
循环依赖解决流程
创建 ServiceA:
1. 标记 A 正在创建
2. 实例化 A(new ServiceA(),此时 serviceB 为 null)
3. 将 A 放入二级缓存(提前暴露)
4. 填充属性:需要 ServiceB
│
└──▶ 创建 ServiceB:
1. 标记 B 正在创建
2. 实例化 B
3. 将 B 放入二级缓存
4. 填充属性:需要 ServiceA
│
└──▶ 从二级缓存获取 A(提前暴露的实例)✓
5. 初始化 B
6. B 放入一级缓存,从二级缓存移除
7. 返回 B
│
◀──┘
5. 将 B 注入到 A
6. 初始化 A
7. A 放入一级缓存,从二级缓存移除
注意:构造函数循环依赖无法解决!但在我们的场景这个不需要
kotlin
// 这种情况无法解决,因为实例化 A 就需要 B
@Service
class ServiceA(private val serviceB: ServiceB)
@Service
class ServiceB(private val serviceA: ServiceA)
解决方案:将其中一个改为字段注入:
kotlin
@Service
class ServiceA(private val serviceB: ServiceB)
@Service
class ServiceB {
@Autowired
private lateinit var serviceA: ServiceA // 改为字段注入
}
第5章:注解扫描器设计
IoC 容器需要自动发现和注册 Bean,这就需要一个注解扫描器。
5.1 类扫描机制
类扫描器需要从不同来源加载类:
┌─────────────────────────────────────────────────────────────────┐
│ 类扫描来源 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ JAR 文件 │ │ 文件系统 │ │ 外部插件 │ │
│ │ (生产环境) │ │ (开发环境) │ │ (扩展支持) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ └────────────────┼────────────────┘ │
│ ▼ │
│ ┌───────────────┐ │
│ │ ClassScanner │ │
│ │ 统一接口 │ │
│ └───────┬───────┘ │
│ │ │
│ ▼ │
│ ┌───────────────┐ │
│ │ List<Class<?>>│ │
│ │ 扫描结果 │ │
│ └───────────────┘ │
└─────────────────────────────────────────────────────────────────┘
类扫描器实现
kotlin
/**
* 类扫描器
* 从 JAR 文件或文件系统扫描类
*/
object ClassScanner {
/**
* 扫描指定包下的所有类
*
* @param mainClass 主类(用于获取 ClassLoader)
* @param packagePrefix 包前缀
* @return 扫描到的类列表
*/
fun scan(mainClass: Class<*>, packagePrefix: String): List<Class<*>> {
val classes = mutableListOf<Class<*>>()
val packageDirName = packagePrefix.replace('.', '/')
try {
val dirs = mainClass.classLoader.getResources(packageDirName)
while (dirs.hasMoreElements()) {
val url = dirs.nextElement()
when (url.protocol) {
"file" -> {
// 从文件系统扫描(开发环境)
val filePath = URLDecoder.decode(url.file, "UTF-8")
scanDirectory(packagePrefix, filePath, classes, mainClass.classLoader)
}
"jar" -> {
// 从 JAR 文件扫描(生产环境)
scanJar(url, packagePrefix, packageDirName, classes, mainClass.classLoader)
}
}
}
} catch (e: IOException) {
logger.error("扫描类时发生错误", e)
}
return classes
}
/**
* 从 JAR 文件扫描类
*/
private fun scanJar(
url: URL,
packagePrefix: String,
packageDirName: String,
classes: MutableList<Class<*>>,
classLoader: ClassLoader
) {
val jar = (url.openConnection() as JarURLConnection).jarFile
val entries = jar.entries()
while (entries.hasMoreElements()) {
val entry = entries.nextElement()
val name = entry.name
// 检查是否在目标包下
if (name.startsWith(packageDirName) &&
name.endsWith(".class") &&
!entry.isDirectory
) {
// 转换为类名
val className = name
.substring(0, name.length - 6)
.replace('/', '.')
// 跳过内部类
if (className.contains("$")) continue
// 加载类
tryLoadClass(className, classLoader, classes)
}
}
}
/**
* 从文件系统扫描类
*/
private fun scanDirectory(
packageName: String,
packagePath: String,
classes: MutableList<Class<*>>,
classLoader: ClassLoader
) {
val dir = File(packagePath)
if (!dir.exists() || !dir.isDirectory) return
dir.listFiles()?.forEach { file ->
if (file.isDirectory) {
// 递归扫描子目录
scanDirectory(
"$packageName.${file.name}",
file.absolutePath,
classes,
classLoader
)
} else if (file.name.endsWith(".class")) {
// 加载类文件
val className = "$packageName.${file.name.dropLast(6)}"
if (!className.contains("$")) {
tryLoadClass(className, classLoader, classes)
}
}
}
}
/**
* 尝试加载类
*/
private fun tryLoadClass(
className: String,
classLoader: ClassLoader,
classes: MutableList<Class<*>>
) {
try {
// 使用 initialize=false 避免触发静态初始化
val clazz = Class.forName(className, false, classLoader)
classes.add(clazz)
} catch (e: ClassNotFoundException) {
// 忽略
} catch (e: NoClassDefFoundError) {
// 忽略(依赖缺失)
} catch (e: LinkageError) {
// 忽略(类加载冲突)
}
}
}
5.2 注解处理器设计
扫描到类之后,需要根据注解进行不同的处理。我们设计一个灵活的注解处理器系统。
注解处理器接口
kotlin
/**
* 注解处理器接口
* 处理特定注解标记的类
*/
interface AnnotationHandler<A : Annotation> {
/** 处理的注解类型 */
val annotationClass: Class<A>
/**
* 处理带有该注解的类
*
* @param clazz 被注解的类
* @param annotation 注解实例
* @param instance 类的实例(如果已创建)
*/
fun handle(clazz: Class<*>, annotation: A, instance: Any?)
}
内置处理器实现
kotlin
/**
* @Service 注解处理器
*/
class ServiceAnnotationHandler : AnnotationHandler<Service> {
override val annotationClass = Service::class.java
override fun handle(clazz: Class<*>, annotation: Service, instance: Any?) {
// 创建 Bean 定义
val beanName = annotation.value.ifEmpty {
clazz.simpleName.replaceFirstChar { it.lowercase() }
}
val definition = BeanDefinition(
beanClass = clazz,
beanName = beanName,
scope = determinScope(clazz),
lazy = clazz.isAnnotationPresent(Lazy::class.java)
)
// 注册到容器
IoCContainerManager.registerBeanDefinition(definition)
}
private fun determinScope(clazz: Class<*>): BeanScope {
return when {
clazz.isAnnotationPresent(Prototype::class.java) -> BeanScope.PROTOTYPE
else -> BeanScope.SINGLETON
}
}
}
/**
* @Aspect 注解处理器
*/
class AspectAnnotationHandler : AnnotationHandler<Aspect> {
override val annotationClass = Aspect::class.java
override fun handle(clazz: Class<*>, annotation: Aspect, instance: Any?) {
// 获取或创建切面实例
val aspectInstance = instance ?: IoCAPI.get().getKotlinInstance(clazz)
?: IoCAPI.get().createInstance(clazz)
// 解析切面,生成拦截器
val interceptors = AopAPI.get().parseAspect(clazz, aspectInstance)
// 注册拦截器
AopManager.registerInterceptors(interceptors)
}
}
DSL 风格的处理器注册
为了让注册更加灵活,我们设计了 DSL 风格的 注解扫描 API:
kotlin
/**
* 注解扫描构建器
*/
class AnnotationScanBuilder {
private val handlers = mutableListOf<AnnotationHandler<*>>()
private val classHandlers = mutableListOf<(Class<*>) -> Unit>()
private val instanceHandlers = mutableListOf<(Class<*>, Any) -> Unit>()
/**
* 注册类级别的注解处理器
*/
inline fun <reified A : Annotation> onClass(
crossinline handler: (Class<*>, A) -> Unit
) {
handlers.add(object : AnnotationHandler<A> {
override val annotationClass = A::class.java
override fun handle(clazz: Class<*>, annotation: A, instance: Any?) {
handler(clazz, annotation)
}
})
}
/**
* 注册实例级别的注解处理器
*/
inline fun <reified A : Annotation> onInstance(
crossinline handler: (Any, A) -> Unit
) {
instanceHandlers.add { clazz, instance ->
clazz.getAnnotation(A::class.java)?.let { annotation ->
handler(instance, annotation)
}
}
}
/**
* 注册方法级别的注解处理器
*/
inline fun <reified A : Annotation> onMethod(
crossinline handler: (Any, Method, A) -> Unit
) {
instanceHandlers.add { clazz, instance ->
for (method in clazz.declaredMethods) {
method.getAnnotation(A::class.java)?.let { annotation ->
handler(instance, method, annotation)
}
}
}
}
/**
* 注册字段级别的注解处理器
*/
inline fun <reified A : Annotation> onField(
crossinline handler: (Any, Field, A) -> Unit
) {
instanceHandlers.add { clazz, instance ->
for (field in clazz.declaredFields) {
field.getAnnotation(A::class.java)?.let { annotation ->
handler(instance, field, annotation)
}
}
}
}
internal fun build(): AnnotationScanConfig {
return AnnotationScanConfig(handlers, classHandlers, instanceHandlers)
}
}
使用示例
kotlin
// 注册自定义注解处理器
IoCAPI.get().registerAnnotationHandlers {
// 处理 @Service 注解的类
onClass<Service> { clazz, annotation ->
val beanName = annotation.value.ifEmpty { clazz.simpleName.lowercase() }
IoCContainerManager.registerBeanDefinition(
BeanDefinition(clazz, beanName)
)
}
// 处理 @Aspect 注解的类
onClass<Aspect> { clazz, annotation ->
val instance = IoCAPI.get().getKotlinInstance(clazz)
if (instance != null) {
val interceptors = AopAPI.get().parseAspect(clazz, instance)
AopManager.registerInterceptors(interceptors)
}
}
// 处理 @PostConstruct 注解的方法
onMethod<PostConstruct> { instance, method, _ ->
method.invoke(instance)
}
// 处理 @Autowired 注解的字段
onField<Autowired> { instance, field, annotation ->
val bean = IoCContainerManager.getBean(field.type)
if (bean != null) {
field.isAccessible = true
field.set(instance, bean)
} else if (annotation.required) {
throw BeanCreationException("无法注入字段: ${field.name}")
}
}
}
5.3 外部类注册机制
有时候,我们需要将外部的类也纳入 IoC 容器管理。
为什么需要外部类注册?
┌─────────────────────────────────────────────────────────────────┐
│ 主项目 (Behemiron) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ IoC 容器 │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ServiceA │ │ServiceB │ │ServiceC │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
▲
│ 想要使用 IoC 容器
│
┌─────────────────────────────────────────────────────────────────┐
│ 外部插件 (MyPlugin) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ @Service │ │ @Autowired │ │ @Cacheable │ │
│ │ MyService │ │ ServiceA │ │ getData() │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ 这些类也想被 IoC 容器管理! │
└─────────────────────────────────────────────────────────────────┘
外部提供者注册 API
kotlin
/**
* 外部提供者信息
*/
data class ExternalProvider(
val providerClass: Class<*>,
val packagePrefix: String?
) {
/**
* 获取有效的包前缀
*/
fun getEffectivePackagePrefix(): String {
if (packagePrefix != null) return packagePrefix
val parts = providerClass.`package`?.name?.split(".") ?: return ""
return if (parts.size >= 2) "${parts[0]}.${parts[1]}" else parts.joinToString(".")
}
}
// IoCAPI 中的注册方法
object IoCAPI {
companion object {
private val externalProviders = mutableListOf<ExternalProvider>()
/**
* 注册外部提供者
*
* 必须在 onInitialized 回调中调用!
*/
fun registerExternalProvider(providerClass: Class<*>, packagePrefix: String? = null) {
externalProviders.add(ExternalProvider(providerClass, packagePrefix))
}
}
}
正确的注册时机
外部插件必须在 IoC 容器初始化之前注册,使用 onInitialized 回调:
kotlin
// 外部插件的集成代码
object MyPluginIntegration {
init {
// 在 Kotlin object 的 init 块中注册
// 这会在类加载时执行,早于 IoC 容器初始化
IoCAPI.onInitialized {
// 注册插件作为外部提供者
IoCAPI.registerExternalProvider(
providerClass = MyPlugin::class.java,
packagePrefix = "com.example.myplugin"
)
}
}
}
// Java 版本
public class MyPluginIntegration {
static {
IoCAPI.onInitialized(() -> {
IoCAPI.registerExternalProvider(MyPlugin.class, "com.example.myplugin");
});
}
}
扫描流程
IoC 容器初始化流程:
1. 触发 onInitialized 回调
└── 外部插件注册 ExternalProvider
2. 扫描内部类
└── 主项目的所有类
3. 扫描外部提供者
└── 遍历 externalProviders
└── ClassScanner.scanProvider(provider)
└── 返回外部插件的所有类
4. 合并所有类
└── internalClasses + externalClasses
5. 处理注解
└── 对每个类执行注解处理器
6. 初始化 Bean
└── 创建所有非懒加载的单例
5.4 本章小结
在本章中,我们设计了:
- 类扫描器:从 JAR 文件和文件系统扫描类
- 注解处理器:灵活的注解处理机制,支持 DSL 风格注册
- 外部类注册:允许外部插件/模组接入 IoC 容器
关键设计决策:
| 设计点 | 决策 | 原因 |
|---|---|---|
| 扫描时机 | 启动时一次性扫描 | 避免运行时开销 |
| 类加载 | initialize=false |
避免触发静态初始化 |
| 外部注册 | 回调机制 | 确保在扫描前完成注册 |
| 处理器注册 | DSL 风格 | 代码简洁,易于扩展 |
全部评论 (0)
暂无评论,快来抢沙发吧~