jjzjj

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

coder 2023-05-11 原文

我有一个原型(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/

有关java - 为什么使用构造函数参数 Autowiring 原型(prototype)bean时不调用@PostConstruct方法的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  5. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  6. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  7. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  8. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  9. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  10. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

随机推荐