在运行时通过构造函数通过 @AutoWired 初始化对象
Initiate object via constructor through @AutoWired during runtime
我是 Springboot 应用程序的新手,使用 @Autowired 执行依赖注入。我们可以通过为具有 none 或默认无参数构造函数的 class 启动 class 对象来直接使用 @Autowired。但是,如果 class 在其构造函数中有一些参数,我想在运行时有条件地自动启动它,是否可以这样做?
例如
@Component
public class SomeContext {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Component
public class SomeBuilder {
private final SomeContext ctx;
@Autowired
public SomeBuilder(SomeContext ctx) {
this.ctx = ctx;
}
public void test() {
System.out.println("ctx name: " + ctx.getName());
}
}
@Service
public class SomeService {
@Autowired
SomeBuilder someBuilder;
public void run(SomeContext ctx) {
if (ctx != null) {
// I want someBuilder to be initiated here in someway with my input ctx
// but NOT doing through new with constructor like below
// someBuilder = new SomeBuilder(ctx);
someBuilder.test(); // ctx name: null, I would expect to see "ctx name: someUser", while ctx was injected into someBuilder in any possible way
}
}
}
@RestController
public class HelloWorldController
{
@Autowired
SomeService someService;
@RequestMapping("/")
public String hello() {
SomeContext someContext = new SomeContext();
someContext.setName("someUser");
someService.run(someContext);
return "Hello springboot";
}
}
我不确定我是否答对了你的问题,但从代码来看,你似乎真的想在每次调用 run
方法时创建一个 SomeBuilder
的新实例共 SomeService
.
如果是这样,我认为首先要了解的是,通常只有当 class 由 Spring 自行管理时才会发生注入魔法。阅读,如果 spring 创建 class 的对象 - 它会向其中注入内容,否则你就只能靠自己了。
接下来要理解的是,如果你有一个由spring管理的classSomeBuilder
的对象,你想将SomeContext
注入其中,这个SomeContext
实例也必须由 spring 管理。
最重要的是,spring 只能处理它管理的对象。这些对象存储在 spring.
中所有名为 ApplicationContext 的对象中的一个 'global registry'
现在 Spring 有了原型作用域与单例作用域的概念。默认情况下,所有 bean 都是单例的,但是您可以轻松地改变这种行为。这有两个有趣的结果:
- 您可以创建在每次调用时注入到单例中的原型对象(在您的情况下是方法
run
,因此 SomeBuilder
可以而且我相信应该是原型)
- 原型对象未存储在应用程序上下文中,因此在运行时向其中注入内容的能力相当有限
考虑到所有这些:
如果你想像在控制器中那样创建 SomeContext
,它不是由 spring 管理的,所以你不能像在构建器中那样使用 spring 的注入.
构建器是一个单例,因此如果您将它与常规 @Autowire
注入另一个单例(在您的情况下为 SomeService
),您将不得不处理构建器对象的相同实例 - 想想并发访问 SomeService
的方法 run
你就会明白这个解决方案并不是一个很好的解决方案。
所以这些是所提供解决方案中的“不准确之处”。
现在,在解决方案方面,您可以:
选项 1
不要在 Spring 中管理构建器,并非所有内容都应由 spring 管理,在这种情况下,您将保持代码不变。
选项 2
- 这是一个解决方案,虽然相当先进:
使用 Java 配置在运行时创建原型 bean,能够将参数注入 bean。
这是一个例子:
// Note, I've removed all the annotations, I'll use java configurations for that, read below...
public class SomeBuilder {
private final SomeContext ctx;
public SomeBuilder(SomeContext ctx) {
this.ctx = ctx;
}
public void test() {
System.out.println("ctx name: " + ctx.getName());
}
}
现在 class SomeService
也会略有变化:
public class SomeService {
private Function<SomeContext, SomeBuilder> builderFactory;
public void run(SomeContext ctx) {
SomeBuilder someBuilder = builderFactory.apply(ctx);
someBuilder.test();
}
}
现在您应该使用 Java 配置以高级方式将它“粘合”到 spring:
@Configuration
public class MyConfiguration {
@Bean
public Function<SomeContext, SomeBuilder> builderFactory() {
return ctx -> someBuilder(ctx);
}
@Bean
@Scope(value = "prototype")
public SomeBuilder someBuilder(SomeContext ctx) {
return new SomeBuilder(ctx);
}
@Bean
public SomeService someService() {
return new SomeService(builderFactory());
}
}
有关真正相似示例的更多详细信息,请参阅 this tutorial
我是 Springboot 应用程序的新手,使用 @Autowired 执行依赖注入。我们可以通过为具有 none 或默认无参数构造函数的 class 启动 class 对象来直接使用 @Autowired。但是,如果 class 在其构造函数中有一些参数,我想在运行时有条件地自动启动它,是否可以这样做?
例如
@Component
public class SomeContext {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Component
public class SomeBuilder {
private final SomeContext ctx;
@Autowired
public SomeBuilder(SomeContext ctx) {
this.ctx = ctx;
}
public void test() {
System.out.println("ctx name: " + ctx.getName());
}
}
@Service
public class SomeService {
@Autowired
SomeBuilder someBuilder;
public void run(SomeContext ctx) {
if (ctx != null) {
// I want someBuilder to be initiated here in someway with my input ctx
// but NOT doing through new with constructor like below
// someBuilder = new SomeBuilder(ctx);
someBuilder.test(); // ctx name: null, I would expect to see "ctx name: someUser", while ctx was injected into someBuilder in any possible way
}
}
}
@RestController
public class HelloWorldController
{
@Autowired
SomeService someService;
@RequestMapping("/")
public String hello() {
SomeContext someContext = new SomeContext();
someContext.setName("someUser");
someService.run(someContext);
return "Hello springboot";
}
}
我不确定我是否答对了你的问题,但从代码来看,你似乎真的想在每次调用 run
方法时创建一个 SomeBuilder
的新实例共 SomeService
.
如果是这样,我认为首先要了解的是,通常只有当 class 由 Spring 自行管理时才会发生注入魔法。阅读,如果 spring 创建 class 的对象 - 它会向其中注入内容,否则你就只能靠自己了。
接下来要理解的是,如果你有一个由spring管理的classSomeBuilder
的对象,你想将SomeContext
注入其中,这个SomeContext
实例也必须由 spring 管理。
最重要的是,spring 只能处理它管理的对象。这些对象存储在 spring.
中所有名为 ApplicationContext 的对象中的一个 'global registry'现在 Spring 有了原型作用域与单例作用域的概念。默认情况下,所有 bean 都是单例的,但是您可以轻松地改变这种行为。这有两个有趣的结果:
- 您可以创建在每次调用时注入到单例中的原型对象(在您的情况下是方法
run
,因此SomeBuilder
可以而且我相信应该是原型) - 原型对象未存储在应用程序上下文中,因此在运行时向其中注入内容的能力相当有限
考虑到所有这些:
如果你想像在控制器中那样创建 SomeContext
,它不是由 spring 管理的,所以你不能像在构建器中那样使用 spring 的注入.
构建器是一个单例,因此如果您将它与常规 @Autowire
注入另一个单例(在您的情况下为 SomeService
),您将不得不处理构建器对象的相同实例 - 想想并发访问 SomeService
的方法 run
你就会明白这个解决方案并不是一个很好的解决方案。
所以这些是所提供解决方案中的“不准确之处”。
现在,在解决方案方面,您可以:
选项 1 不要在 Spring 中管理构建器,并非所有内容都应由 spring 管理,在这种情况下,您将保持代码不变。
选项 2
- 这是一个解决方案,虽然相当先进:
使用 Java 配置在运行时创建原型 bean,能够将参数注入 bean。
这是一个例子:
// Note, I've removed all the annotations, I'll use java configurations for that, read below...
public class SomeBuilder {
private final SomeContext ctx;
public SomeBuilder(SomeContext ctx) {
this.ctx = ctx;
}
public void test() {
System.out.println("ctx name: " + ctx.getName());
}
}
现在 class SomeService
也会略有变化:
public class SomeService {
private Function<SomeContext, SomeBuilder> builderFactory;
public void run(SomeContext ctx) {
SomeBuilder someBuilder = builderFactory.apply(ctx);
someBuilder.test();
}
}
现在您应该使用 Java 配置以高级方式将它“粘合”到 spring:
@Configuration
public class MyConfiguration {
@Bean
public Function<SomeContext, SomeBuilder> builderFactory() {
return ctx -> someBuilder(ctx);
}
@Bean
@Scope(value = "prototype")
public SomeBuilder someBuilder(SomeContext ctx) {
return new SomeBuilder(ctx);
}
@Bean
public SomeService someService() {
return new SomeService(builderFactory());
}
}
有关真正相似示例的更多详细信息,请参阅 this tutorial