编译时初始化工厂:如何优化基于名称返回实现的工厂实例管理
实现编译时初始化的模块工厂
针对你想把模块工厂的初始化从运行时bootstrap移到编译时的需求,在Scala里有几种实用的方案,我给你详细拆解下:
方案1:Scala 3 用 inline + 编译时反射(推荐)
这个方案利用Scala 3的inline和编译时API,自动在编译阶段收集所有标记过的模块实现,生成对应的moduleMap,完全不需要手动维护映射关系。
步骤与代码示例
- 定义注册模块的注解和基础特质
import scala.annotation.StaticAnnotation // 用于标记模块实现的注解,指定模块名称 class RegisterModule(name: String) extends StaticAnnotation // 模块的基础特质 trait Module // 自定义异常类 class ModuleNotFoundException(message: String) extends Exception(message)
- 编写你的模块实现,用注解标记
// 标记这个实现对应名称"module1" class ModuleImpl extends Module @RegisterModule("module1") // 另一个模块实现,标记对应"module2" class AnotherModuleImpl extends Module @RegisterModule("module2")
- 实现编译时工厂
import scala.compiletime.* import scala.quoted.* object ModuleFactory { // 编译时生成的模块映射,运行时直接使用 inline val moduleMap: Map[String, Module] = ${collectModules} // 编译时扫描并收集所有带@RegisterModule注解的Module实现 private def collectModules(using Quotes): Expr[Map[String, Module]] = { import quotes.reflect.* // 遍历当前编译环境中的所有类,筛选符合条件的模块 val modules = Symbol.requiredModule("").declarations.collect { case cls if cls.isClassDef && cls.annotations.exists(_.annotationType <:< TypeRepr.of[RegisterModule]) => val annotation = cls.annotations.find(_.annotationType <:< TypeRepr.of[RegisterModule]).get // 提取注解中的模块名称 val nameExpr = annotation.args.head.asExpr[String] // 获取模块实例的表达式(确保类实现了Module特质) val instanceExpr = Expr.summon[Module](using TypeRepr.of[Module].substituteParams(Nil, List(cls.typeRef))) .getOrElse(report.errorAndAbort(s"${cls.name} 未实现Module特质,无法注册")) (nameExpr, instanceExpr) } // 构建Map的编译时表达式 Expr.ofMap(modules.toMap) } def getModule(moduleName: String): Module = moduleMap.get(moduleName) match { case Some(m) => m case _ => throw new ModuleNotFoundException(s"$moduleName - 模块未找到") } }
核心优势:编译时自动扫描所有标记的模块,不需要手动更新moduleMap,所有实例在编译阶段就已确定,运行时直接读取,完全避免了运行时初始化的开销。
方案2:Scala 2 用宏注解
如果你的项目还在使用Scala 2,可以借助宏注解来实现编译时生成moduleMap,需要依赖scalamacros-paradise插件。
步骤与代码示例
- 添加宏依赖(build.sbt)
libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full)
- 定义宏注解和基础类
import scala.annotation.{StaticAnnotation, compileTimeOnly} import scala.language.experimental.macros import scala.reflect.macros.blackbox // 用于生成工厂的宏注解 @compileTimeOnly("请启用macro paradise插件来展开此注解") class GenerateModuleFactory extends StaticAnnotation { def macroTransform(annottees: Any*): Any = macro ModuleFactoryMacro.generate } // 宏实现逻辑 object ModuleFactoryMacro { def generate(c: blackbox.Context)(annottees: c.Tree*): c.Tree = { import c.universe._ // 扫描所有实现Module且带RegisterModule注解的类 val moduleEntries = c.classSymbol.tpe.decls.collect { case cls: ClassSymbol if cls.isConcrete && cls.toType <:< typeOf[Module] => cls.annotations.collectFirst { case annot if annot.tree.tpe =:= typeOf[RegisterModule] => val moduleName = annot.tree.children.tail.head.asInstanceOf[Literal].value.value.asInstanceOf[String] val instance = q"new ${cls.toType}" (moduleName, instance) } }.flatten.toMap // 生成工厂对象的代码 annottees match { case q"object $obj extends ..$parents { ..$body }" :: Nil => q""" object $obj extends ..$parents { private val moduleMap = Map(..${moduleEntries.map { case (k, v) => q"$k -> $v" }}) def getModule(moduleName: String): Module = moduleMap.get(moduleName) match { case Some(m) => m case _ => throw new ModuleNotFoundException(s"$$moduleName - 模块未找到") } ..$body } """ case _ => c.abort(c.enclosingPosition, "此注解仅能应用于object") } } } // 模块标记注解与基础特质 class RegisterModule(name: String) extends StaticAnnotation trait Module class ModuleNotFoundException(message: String) extends Exception(message)
- 使用宏注解生成工厂
// 用注解自动生成工厂逻辑 @GenerateModuleFactory object ModuleFactory // 模块实现 class ModuleImpl extends Module @RegisterModule("module1") class AnotherModuleImpl extends Module @RegisterModule("module2")
核心优势:Scala 2环境下的替代方案,同样实现编译时生成映射,无需手动维护moduleMap。
方案3:用Scala枚举(适合模块固定的场景)
如果你的模块数量固定、提前已知,用Scala枚举是最简洁的方案,枚举的所有值在编译时就已确定,天然满足编译时初始化的需求。
代码示例
// 模块基础特质 trait Module class ModuleImpl extends Module class AnotherModuleImpl extends Module // 自定义异常 class ModuleNotFoundException(message: String) extends Exception(message) // 枚举定义,每个case对应一个模块实例 enum ModuleEnum(val moduleName: String, val instance: Module): case Module1 extends ModuleEnum("module1", new ModuleImpl) case Module2 extends ModuleEnum("module2", new AnotherModuleImpl) // 工厂实现 object ModuleFactory { // 编译时生成的映射 private val moduleMap: Map[String, Module] = ModuleEnum.values.map(e => e.moduleName -> e.instance).toMap def getModule(moduleName: String): Module = moduleMap.get(moduleName) match { case Some(m) => m case _ => throw new ModuleNotFoundException(s"$moduleName - 模块未找到") } }
核心优势:代码简洁易懂,完全不需要反射或宏,适合模块数量少且固定的场景。
内容的提问来源于stack exchange,提问作者user2607928
相关产品推荐
相关产品推荐

