如何创建工厂,避免初始化未使用的对象及其依赖
问题场景
我有两个实现了同一MyInterface的类A和B,希望根据功能开关(布尔值)将其中一个注入到Target类中。相关代码如下:
Java代码
public class A implements MyInterface { public A(C obj1, D obj2); ... } public class B implements MyInterface { public B(E obj1, F obj2); ... } public class Target { Target(MyInterface obj1); ... }
XML配置(原配置)
<bean id="myAclass" class="com.project.A" c:obj1-ref="project-C" c:obj2-ref="project-D" /> <bean id="myBclass" class="com.project.B" c:obj1-ref="project-E" c:obj2-ref="project-F" /> <bean id="targetClass" class="com.project.Target" c:obj-ref="????" />
原工厂方法写法存在错误(静态方法无法使用this),且会提前初始化A、B及其所有依赖,不符合需求:
public class Factory { public Factory(A a, B b){ ... } private static MyInterface build(boolean toggleValue) { return toggleValue ? this.a : this.b; } }
需要实现仅初始化实际使用的对象及其依赖,以下是几种优雅的实现方式:
解决方案
方案1:Spring条件化Bean + 懒加载(推荐)
利用Spring的条件注解或XML条件属性,结合懒加载特性,只创建符合开关条件的Bean,未选中的Bean及其依赖不会被初始化。
步骤1:定义条件判断类
public class UseACondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // 从配置文件读取开关值,例如application.properties中的feature.useA=true String toggle = context.getEnvironment().getProperty("feature.useA"); return Boolean.parseBoolean(toggle); } } public class UseBCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { String toggle = context.getEnvironment().getProperty("feature.useA"); return !Boolean.parseBoolean(toggle); } }
步骤2:给A、B添加条件与懒加载注解
@Component @Conditional(UseACondition.class) @Lazy public class A implements MyInterface { public A(C obj1, D obj2); ... } @Component @Conditional(UseBCondition.class) @Lazy public class B implements MyInterface { public B(E obj1, F obj2); ... }
步骤3:注入到Target类
直接注入MyInterface,Spring会自动选择符合条件的Bean:
@Component public class Target { private final MyInterface myBean; public Target(MyInterface myBean) { this.myBean = myBean; } ... }
如果用XML配置,写法如下:
<bean id="useACondition" class="com.project.UseACondition"/> <bean id="useBCondition" class="com.project.UseBCondition"/> <bean id="myAclass" class="com.project.A" c:obj1-ref="project-C" c:obj2-ref="project-D" lazy-init="true" condition="useACondition" /> <bean id="myBclass" class="com.project.B" c:obj1-ref="project-E" c:obj2-ref="project-F" lazy-init="true" condition="useBCondition" /> <bean id="targetClass" class="com.project.Target" c:obj-ref="#{myAclass ?: myBclass}" />
方案2:改进工厂方法,结合延迟加载代理
如果必须使用工厂模式,通过Provider延迟获取Bean,避免提前初始化A、B及其依赖:
改进后的Factory类
@Component public class Factory { private final Provider<A> aProvider; private final Provider<B> bProvider; private final Environment environment; // 用Provider包装Bean,延迟实际创建时机 public Factory(Provider<A> aProvider, Provider<B> bProvider, Environment environment) { this.aProvider = aProvider; this.bProvider = bProvider; this.environment = environment; } public MyInterface build() { boolean useA = Boolean.parseBoolean(environment.getProperty("feature.useA")); return useA ? aProvider.get() : bProvider.get(); } }
Target类注入Factory并获取Bean
@Component public class Target { private final MyInterface myBean; public Target(Factory factory) { this.myBean = factory.build(); } ... }
方案3:Spring表达式+懒加载
无需额外编写条件类,直接通过SpEL结合懒加载实现动态选择:
<bean id="myAclass" class="com.project.A" c:obj1-ref="project-C" c:obj2-ref="project-D" lazy-init="true" /> <bean id="myBclass" class="com.project.B" c:obj1-ref="project-E" c:obj2-ref="project-F" lazy-init="true" /> <!-- 从环境变量读取开关,动态选择Bean --> <bean id="targetClass" class="com.project.Target" c:obj-ref="#{environment.getProperty('feature.useA', 'true') == 'true' ? myAclass : myBclass}" />
核心是给myAclass和myBclass设置lazy-init="true",只有被选中的Bean才会被初始化,其依赖也会随之创建。
内容的提问来源于stack exchange,提问作者Joaquim Oliveira
相关产品推荐
相关产品推荐

