如何让工厂与Guice高效协作?解决两类工厂使用痛点
嘿,你在Guice里用工厂的这两个场景挺典型的,我来给你分享下更优雅的解决方案,顺便说说子注入器方案的问题~
问题1:让Guice接管工厂创建的单例管理
你现在需要手动存储工厂创建的WidgetA再通过@Provides提供,其实完全可以让Guice直接接管这个单例的生命周期,不用自己维护:
在Module里写一个带@Provides @Singleton注解的方法,在里面调用工厂的创建逻辑,Guice会自动缓存这个实例,作为单例供其他地方注入:
public class AppModule extends AbstractModule { @Override protected void configure() { // 其他绑定配置 } @Provides @Singleton WidgetA provideWidgetA(Container container) { // 在这里调用工厂创建WidgetA,Guice会负责缓存这个实例 return WidgetAFactory.make(container); } }
这样一来,任何需要WidgetA的地方直接@Inject WidgetA widgetA就行,Guice会保证只调用一次provideWidgetA方法,把创建好的WidgetA作为单例管理,完全不用你自己存储和维护。而且如果WidgetA的创建依赖容器的副作用,这个方法会在第一次注入WidgetA时执行,也能保证创建时机的控制。
问题2:避免工厂维护长列表的注入依赖
这个场景刚好是Guice的AssistedInject扩展专门解决的问题!它就是为了处理**构造函数混合注入依赖(由Guice管理)和运行时参数(调用时传入)**的情况,完美匹配你的需求。
步骤如下:
- 修改WidgetC的构造函数:用
@Assisted标记运行时参数,其他依赖保持原有的注入注解:
public class WidgetC { @Inject public WidgetC( Container container, @WidgetCFont Font font, @WidgetCColor Color color, @Named("flag") String flag, @Assisted String runtimeFlag // 这个是运行时才确定的参数 ) { // 构造逻辑 } }
- 定义工厂接口:只需要声明接收运行时参数的方法,不用自己实现:
public interface WidgetCFactory { WidgetC create(String runtimeFlag); // 只传运行时参数,其他依赖由Guice自动注入 }
- 在Module中配置AssistedInject:
public class AppModule extends AbstractModule { @Override protected void configure() { install(new FactoryModuleBuilder() .implement(WidgetC.class, WidgetC.class) .build(WidgetCFactory.class)); } }
这样Guice会自动帮你实现WidgetCFactory类,当你注入WidgetCFactory并调用create(runtimeFlag)时,Guice会自动把所有非@Assisted的依赖(container、font、color、flag)都填进去,完全不用你手动维护工厂的依赖列表,依赖变更时也不用修改工厂代码,注解校验也能正常生效,彻底解决你的痛点。
关于子注入器方案的问题
你用子注入器的方案确实能实现功能,但有两个明显的问题:
- 性能开销:每次调用
make方法都会创建一个新的子注入器,子注入器的创建是有一定成本的,如果频繁调用这个工厂,会对性能产生影响。 - 不符合最佳实践:子注入器通常用于隔离独立的依赖上下文(比如不同的用户请求、不同的租户环境),而不是用来创建单个对象,有点大材小用了。
所以相比之下,AssistedInject才是这个场景的最优解,既轻量又符合依赖注入的设计原则。
内容的提问来源于stack exchange,提问作者Jerome

