如何在Spring中通过静态工厂方法同时注入依赖与动态属性?
解决Spring依赖注入与动态属性设置共存的问题
这个问题我之前也碰到过,核心问题在于你写的静态工厂方法of是直接通过私有无参构造器创建了Service实例——这个实例完全脱离了Spring容器的管理,Spring根本没机会帮你注入repository依赖,自然会出现未实例化的问题。
下面给你几种可行的解决方案,既能保留Spring的依赖注入,又能实现动态属性的设置:
方案一:利用Spring原型Bean + ApplicationContext持有者
这种方式适合你希望通过静态方法直接创建实例的场景,核心思路是让Service成为原型作用域的Bean(每次获取都是新实例),然后通过ApplicationContext从容器中获取已经注入好依赖的实例,再设置动态属性。
步骤1:创建ApplicationContext持有者
首先定义一个工具类,用于持有Spring的ApplicationContext:
import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @Component public class ApplicationContextHolder implements ApplicationContextAware { private static ApplicationContext context; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { context = applicationContext; } public static ApplicationContext getContext() { return context; } }
步骤2:修改Service类
将Service标记为原型Bean,并调整静态工厂方法:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @Component @Scope("prototype") // 标记为原型作用域,每次获取都是新实例 class Service { private final Repository repository; private String field; // Spring注入Repository的构造器 @Autowired public Service(Repository repository) { this.repository = repository; } public static Service of(String field) { // 从容器获取已注入依赖的Service实例 Service service = ApplicationContextHolder.getContext().getBean(Service.class); service.setField(field); return service; } // 设置动态属性的方法(可以是私有或公开,根据需求调整) public void setField(String field) { this.field = field; } // 你的业务方法 }
使用时直接调用Service.of("动态属性值")即可,返回的实例既包含Spring注入的repository,又设置了动态的field属性。
方案二:使用建造者模式(更优雅的方式)
这种方式避免了直接操作ApplicationContext,符合Spring的依赖注入设计理念,适合需要灵活构建实例的场景。
步骤1:创建ServiceBuilder
让Builder由Spring管理,注入所需的Repository依赖,同时提供设置动态属性的方法:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component class ServiceBuilder { private final Repository repository; private String field; @Autowired public ServiceBuilder(Repository repository) { this.repository = repository; } // 设置动态属性的链式方法 public ServiceBuilder withField(String field) { this.field = field; return this; } // 构建Service实例 public Service build() { return new Service(repository, field); } }
步骤2:调整Service类
添加一个接收Repository和动态属性的构造器:
class Service { private final Repository repository; private final String field; // 用于Spring直接注入的构造器(如果需要Spring管理默认实例的话) @Autowired public Service(Repository repository) { this.repository = repository; this.field = null; // 可设置默认值 } // 用于Builder的构造器 public Service(Repository repository, String field) { this.repository = repository; this.field = field; } // 你的业务方法 }
使用方式
在Spring管理的类中注入ServiceBuilder,然后链式构建实例:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component class SomeBusinessClass { @Autowired private ServiceBuilder serviceBuilder; public void doSomething() { // 构建带动态属性的Service实例 Service service = serviceBuilder.withField("动态属性值").build(); // 调用业务方法 service.someBusinessMethod(); } }
方案三:使用@Lookup方法(适合单例Bean中获取原型实例)
如果你的Service需要被其他单例Bean依赖,但又需要每次获取新的实例并设置动态属性,可以使用Spring的@Lookup注解:
修改Service类
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @Component @Scope("prototype") class Service { private final Repository repository; private String field; @Autowired public Service(Repository repository) { this.repository = repository; } public void setField(String field) { this.field = field; } // 业务方法 }
在依赖Service的类中使用@Lookup
import org.springframework.beans.factory.annotation.Lookup; import org.springframework.stereotype.Component; @Component class SomeSingletonBean { // @Lookup注解会自动从容器获取原型实例 @Lookup protected Service createService() { return null; // 仅作为方法签名,Spring会动态生成实现 } public void doBusiness() { Service service = createService(); service.setField("动态属性值"); service.someBusinessMethod(); } }
内容的提问来源于stack exchange,提问作者DWB
相关产品推荐
相关产品推荐

