You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何创建工厂,避免初始化未使用的对象及其依赖

问题场景

我有两个实现了同一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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 06:17:32