gpt4 book ai didi

java - 为什么使用构造函数参数 Autowiring 原型(prototype)bean时不调用@PostConstruct方法

转载 作者:IT老高 更新时间:2023-10-28 13:51:04 27 4
gpt4 key购买 nike

我有一个原型(prototype)范围的bean,我想通过@Autowired 注解注入(inject)它。在这个 bean 中,还有 @PostConstruct 方法,它没有被 Spring 调用,我不明白为什么。

我的 bean 定义:

package somepackage;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;

@Component
@Scope("prototype")
public class SomeBean {

public SomeBean(String arg) {
System.out.println("Constructor called, arg: " + arg);
}

@PostConstruct
private void init() {
System.out.println("Post construct called");
}

}

我要注入(inject) bean 的 JUnit 类:

package somepackage;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@ContextConfiguration("classpath*:applicationContext-test.xml")
public class SomeBeanTest {

@Autowired
ApplicationContext ctx;

@Autowired
@Value("1")
private SomeBean someBean;

private SomeBean someBean2;

@Before
public void setUp() throws Exception {
someBean2 = ctx.getBean(SomeBean.class, "2");
}

@Test
public void test() {
System.out.println("test");
}
}

Spring 配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<context:component-scan base-package="somepackage"/>

</beans>

执行的输出:

Constructor called, arg: 1
Constructor called, arg: 2
Post construct called
test

当我通过从 ApplicationContext 调用 getBean 来初始化 bean 时,一切都按预期工作。我的问题是为什么通过 @Autowire@Value 组合注入(inject) bean 不调用 @PostConstruct 方法

最佳答案

为什么使用@Value 而不是@Autowired?

@Value 注释用于注入(inject)值,通常作为目标字符串、原语、装箱类型和 java 集合。

根据Spring's documentation :

The @Value annotation can be placed on fields, methods and method/constructor parameters to specify a default value.

Value 接收一个字符串表达式,spring 使用它来处理到目标对象的转换。这种转换可以通过Spring's type conversion , java bean property editor ,以及 Spring's SpEL expresions .原则上,这种转换的结果对象不由 spring 管理(即使您可以从任何这种方法返回一个已经管理的 bean)。

另一方面,AutowiredAnnotationBeanPostProcessor是一个

BeanPostProcessor implementation that autowires annotated fields, setter methods and arbitrary config methods. Such members to be injected are detected through a Java 5 annotation: by default, Spring's @Autowired and @Value annotations.

该类处理字段注入(inject),解析依赖,最终调用方法doResolveDependency , 是在解决注入(inject)的“优先级”的这个方法中,springs 检查是否存在通常是表​​达式字符串的建议值,这个建议值是注释 Value 的内容,所以如果存在,则调用类 SimpleTypeConverter已完成,否则 spring 会寻找候选 bean 并解析 Autowiring 。

@Autowired 被忽略而使用@Value 的原因仅仅是因为首先检查了 value 的注入(inject)策略。显然总是要优先考虑,当使用多个冲突的注解时,spring 也可能会抛出异常,但在这种情况下,是由先前对 sugested 值的检查确定的。

我找不到任何与这个“优先级”相关的东西是 spring,但简单是因为不打算一起使用这个注解,就像它不打算使用 @Autowired@Resource 一起。


@Value 为什么会创建对象的新实例

前面说过类SimpleTypeConverter是在建议值出现的时候调用的,具体调用的是方法convertIfNecessary ,这是执行将字符串转换为目标对象的方法,同样可以使用属性编辑器或自定义转换器来完成,但这里没有使用这些。也不使用 SpEL 表达式,仅使用字符串文字。

Spring首先检查目标对象是字符串还是集合/数组(可以转换例如逗号分隔的列表),然后检查目标是否是枚举,如果是,它会尝试转换字符串,如果是不是,不是接口(interface)而是类,它检查 Constructor(String) 的存在以最终创建对象(不由 spring 管理)。基本上,这个转换器尝试了许多不同的方法来将字符串转换为最终的对象。

此实例化只能使用字符串作为参数,例如,如果您使用 SpEL 表达式来返回长 @Value("#{2L}"),并使用带有一个 Constructor(Long) 它将抛出一个带有类似消息的 IllegalStateException:

Cannot convert value of type 'java.lang.Long' to required type 'com.fiberg.test.springboot.object.Hut': no matching editors or conversion strategy found


可能的解决方案

使用简单的@Configuration 类作为供应商。

public class MyBean {
public MyBean(String myArg) { /* ... */ }
// ...
@PostConstruct public init() { /* ... */ }
}

@Configuration
public class MyBeanSupplier {
@Lazy
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE,
proxyMode = ScopedProxyMode.NO)
public MyBean getMyBean(String myArg) {
return new MyBean(myArg);
}
}

您可以将 MyBean 定义为 MyBeanSupplier 类中的静态类,如果它是唯一的方法。此外,您不能使用代理模式 ScopedProxyMode.TARGET_CLASS,因为您需要将参数作为 bean 提供,而传递给 getMyBean 的参数将被忽略。

使用这种方法,您将无法 Autowiring bean 本身,而是 Autowiring 供应商,然后调用 get 方法。

// ...
public class SomeBeanTest {
@Autowired private MyBeanSupplier supplier;
// ...
public void setUp() throws Exception {
someBean = supplier.getMyBean("2");
}
}

您还可以使用应用程序上下文创建 bean。

someBean = ctx.getBean(SomeBean.class, "2");

而且@PostConstruct方法不管你用哪一个都应该被调用,但是@PreDestroy is not called in prototype beans .

关于java - 为什么使用构造函数参数 Autowiring 原型(prototype)bean时不调用@PostConstruct方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49007559/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com