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

如何在工厂/策略模式运行时创建的对象中实现@Autowired服务注入?

在Spring中使用策略模式的优雅解决方案

嘿,这问题我太有发言权了——Spring里用策略模式完全没问题!你遇到的空指针问题,根本不是策略模式的锅,只是因为你手动new出来的SmartAction不在Spring容器的管理范围内,所以@Autowired自然没法帮你注入依赖。

先聊聊你提到的两个方案为啥不够优雅:

  • 把所有Action都声明成Service:虽然能解决注入问题,但如果你的Action需要根据动态业务逻辑创建不同实例,或者Action数量很多,这种方式会让灵活性大打折扣;
  • 把依赖服务作为方法参数传入:这会导致方法参数越来越多,违反单一职责原则,而且每次调用都要传依赖,代码会变得很臃肿。

下面给你几个更优雅的解决方案,按推荐程度排序:

方案一:用策略工厂封装(最推荐)

这是策略模式结合Spring的最佳实践,把所有策略实现交给Spring管理,再通过一个工厂类统一获取,完全符合开闭原则。

步骤1:给所有Action实现类加Spring注解

先让Spring接管这些策略类:

public interface Action { void doSomething(); }

@Component // 加@Component让Spring管理
public class StupidAction implements Action{
    @Autowired private MyService myService;
    public void doSomething(){ myService.changeSomething(); }
}

@Component // 同样加注解
public class SmartAction implements Action{
    @Autowired private OtherService otherService;
    public void doSomething(){ otherService.doSmartThing(); }
}

步骤2:创建策略工厂

Spring会自动把所有Action类型的Bean注入到Map中,key是Bean的默认名称(类名首字母小写),value是实例:

@Component
public class ActionFactory {
    private final Map<String, Action> actionMap;

    // Spring 4.3+支持无@Autowired的构造方法注入
    public ActionFactory(Map<String, Action> actionMap) {
        this.actionMap = actionMap;
    }

    public Action getAction(String actionType) {
        Action action = actionMap.get(actionType);
        if (action == null) {
            throw new IllegalArgumentException("不存在对应的Action类型: " + actionType);
        }
        return action;
    }
}

步骤3:在控制器中使用工厂

@RestController
@RequestMapping(path = "/")
public class WorkflowController {
    @Autowired
    private ActionFactory actionFactory;

    @GetMapping("")
    ResponseEntity someAPI() {
        // 这里可以根据业务逻辑动态传入actionType,比如从请求参数、数据库获取
        Action action = actionFactory.getAction("smartAction");
        action.doSomething();
        return ResponseEntity.ok().build();
    }
}

以后新增Action实现,只需要给类加@Component,完全不用修改工厂和控制器代码,完美适配开闭原则。

方案二:用ApplicationContext直接获取Bean

如果你的场景比较简单,不想写工厂类,可以直接在控制器中注入ApplicationContext来获取Bean:

@RestController
@RequestMapping(path = "/")
public class WorkflowController {
    @Autowired
    private ApplicationContext context;

    @GetMapping("")
    ResponseEntity someAPI() {
        // 根据类型获取Bean,要是有多个同类型Bean,可以用getBean(String name, Class<T> type)
        Action action = context.getBean(SmartAction.class);
        action.doSomething();
        return ResponseEntity.ok().build();
    }
}

注意:这种方式适合简单场景,不要在业务代码中大量使用,不然会让代码和Spring容器耦合度太高。

方案三:用ObjectFactory/Provider获取原型Bean

如果你的Action是有状态的(比如需要每次创建新实例),可以用ObjectFactory或者javax.inject.Provider来延迟获取实例:

@RestController
@RequestMapping(path = "/")
public class WorkflowController {
    @Autowired
    private ObjectFactory<SmartAction> smartActionFactory;

    @GetMapping("")
    ResponseEntity someAPI() {
        // 每次调用getObject()都会创建新实例(前提是SmartAction的作用域是prototype)
        Action action = smartActionFactory.getObject();
        action.doSomething();
        return ResponseEntity.ok().build();
    }
}

记得给有状态的Action加上原型作用域:

@Component
@Scope("prototype")
public class SmartAction implements Action{
    // ...
}

关键注意点

  • 所有策略实现类必须被Spring管理:要么加@Component/@Service,要么在配置类中用@Bean声明;
  • 如果Action是有状态的,一定要设置作用域为prototype,避免单例Bean的线程安全问题;
  • 尽量用工厂模式封装策略获取逻辑,减少代码耦合,提升可维护性。

内容的提问来源于stack exchange,提问作者ym_wp

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 18:22:37