【问题标题】:Spring context dynamic changeSpring上下文动态变化
【发布时间】:2014-01-19 18:55:16
【问题描述】:

我已经阅读了动态 bean 定义更改。我在一个简单的代码示例中尝试了它(参见下面的代码),我发现它在我不想停止服务器但添加/更改 bean 定义的情况下非常有吸引力。

问题:

  1. 这样做安全吗(见下面的代码)?
  2. 我读到有可能在 StaticApplicationContexBeanPostProcessorBeanFactoryPostProcessor 的帮助下在运行时实现 bean 定义更改?那么有什么区别呢?

    public class Main {
    final static String header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
            "<beans xmlns=\"http://www.springframework.org/schema/beans\"\n" +
            "       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
            "       xmlns:context=\"http://www.springframework.org/schema/context\"\n" +
            "       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\">\n" +
            "    <context:annotation-config />\n" +
            "    <context:component-scan base-package=\"vbah\"/>";
    
    final static String contextA =
            "<bean id=\"test\" class=\"java.lang.String\">\n" +
                    "\t\t<constructor-arg value=\"fromContextA\"/>\n" +
                    "</bean></beans>";
    
    final static String contextB =
            "<bean id=\"test\" class=\"java.lang.String\">\n" +
                    "\t\t<constructor-arg value=\"fromContextB\"/>\n" +
                    "</bean></beans>";
    
    public static void main(String[] args) throws IOException {
        //create a single context file
        final File contextFile = new File("src/resources/spring-config.xml");
    
        //write the first context into it
        FileUtils.writeStringToFile(contextFile, header + contextA);
    
        //create a spring context
        FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext(
                new String[]{contextFile.getPath()}
        );
    
        //echo "fromContextA"
        System.out.println(context.getBean("test"));
    
        //write the second context into it
        FileUtils.writeStringToFile(contextFile, header + contextB);
    
        //refresh the context
        context.refresh();
    
        //echo "fromContextB"
        System.out.println(context.getBean("test"));
    }
    }
    

编辑:

你能回答以下问题吗:

  1. 据我了解,BeanPostProcess 允许您在运行时通过使用代理包装对象来修改已存在的 bean 实例。我说的对吗?
  2. AbstractApplicationContext#refresh() 删除所有单例 bean 并重新创建它们。

    • 但是如果我想更改原型/自定义范围 bean 的定义?
    • 如果我有两个 bean:A 和 B。A 引用 B。如果我以不包含 B 定义的方式更改 bean 定义。B 实例将被销毁,但新实例不会被创建。比 A 会得到一个 null 依赖。我说的对吗?
  3. StaticApplicationContextBeanFactoryPostProcessor 都允许我在运行时更改 bean 定义。但是有什么区别,优点/缺点?

  4. [主要问题] 为什么 Spring 有 3 种机制来实现相同的目标。请在AbstractApplicationContext#refresh()StaticApplicationContextBeanFactoryPostProcessor 之间做一个简短的比较(或用例示例)。

【问题讨论】:

    标签: java spring


    【解决方案1】:

    这样做安全吗(见下面的代码)?

    您必须定义安全

    AbstractApplicationContext#refresh() 方法 javadoc 声明

    由于这是一个启动方法,它应该销毁已经创建的 如果失败,则为单例,以避免悬空资源。换句话说, 调用该方法后,全部或根本没有单例 应该被实例化。

    基本上,上下文中的每个 bean 都将被销毁,并且对它们的所有引用都将被删除,从而使它们成为垃圾回收的候选对象。您需要确保这些 bean 有适当的方法来释放它们可能拥有的任何资源。 There are different ways to do that

    • 让你的类实现DisposableBean 接口。
    • destroy-method 属性添加到您的&lt;bean&gt;@Bean 定义中。
    • @PreDestroy注释方法。

    请注意,refresh() 通常会急切地刷新您的ApplicationContext,即。立即重新实例化所有 bean。发生这种情况时,您可能会注意到您的应用程序速度有所放缓。

    我已经读到可以实现 bean 定义更改 在 StaticApplicationContextBeanPostProcessor 的帮助下运行时 BeanFactoryPostProcessor?那么有什么区别呢?

    StaticApplicationContext 是您自己注册 bean 定义的 ApplicationContext 类之一。在您的示例中,bean 定义是从您的 XML 文件中解析并在幕后注册的。使用StaticApplicationContext,您可以使用registerBeanDefinition(..) 或其他registerXxx() 方法显式注册bean 定义。

    BeanFactoryPostProcessor 可以访问正在使用的BeanFactory,因此可以访问所有已注册的 bean 定义。因此,您可以检索任何您想要的BeanDefinition 并对其进行修改。作为BeanFactoryPostProcess#postProcessBeanFactory(..) 的javadoc 状态

    所有 bean 定义都将被加载,但没有 bean 会加载 还没有被实例化。这允许覆盖或添加属性 甚至是渴望初始化的 bean。

    您可以在 ApplicationContext 实际使用它之前更改 bean 定义。

    最后,BeanPostProcessor 不会更改 bean 定义。您可以使用 BeanPostProcessor 更改 bean 的创建方式,但底层 BeanDefinition 将保持不变。


    供您编辑(比实际答案大:))

    据我了解 BeanPostProcess 允许您修改已经存在的 bean 实例在运行时通过用代理包装对象。我是不是 对吧?

    这不仅仅是为了代理,你可以对对象做任何你想做的事情:修改它的属性,在其他上下文中注册它,使它成为null,等等。这围绕着 bean 定义。

    AbstractApplicationContext#refresh() 删除所有单例 bean 和 重新创建它们。

    但如果我想更改原型/自定义范围的定义 豆?如果我有两个豆子:A 和 B。A 引用了 B。如果我 以不包含的方式更改 bean 定义 B的定义。比B实例将被销毁,但新的实例 不会被创建。比 A 会得到一个空依赖。我说的对吗?

    ApplicationContext 中,您声明了您的bean 定义。如果您要更改 bean 定义,请在 BeanFactoryPostProcessor 中更改它或在上下文配置中以不同方式声明它。

    对于依赖关系,如果你破坏了B bean 定义,就没有bean 可以注入A,Spring 会抱怨,抛出NoSuchBeanDefinitionException。除非您明确告诉它,否则 Bean 注入永远不会注入 null

    StaticApplicationContextBeanFactoryPostProcessor 都允许我 在运行时更改 bean 定义。但是有什么区别, 优点/缺点?

    两者的用途完全不同。 StaticApplicationContext 是一个 ApplicationContext 实现。在这里,您声明 bean 定义。 BeanFactoryPostProcessor 用于根据您希望实现的任何条件以任何方式修改这些 bean 定义。

    为什么 Spring 有 3 种机制来实现相同的目标。你能做一个 之间的简要比较(或用例示例) AbstractApplicationContext#refresh()StaticApplicationContextBeanFactoryPostProcessor 请。

    目标不一样。 ApplicationContextBeanFactoryPostProcessor 不同,并且在上下文生命周期中的不同时间发挥作用(请参阅上一个问题中的漂亮图表)。

    我没有适合你的用例。了解以上各项的作用,当您有具体要求时,您就会知道何时应用它们。

    【讨论】:

    • 感谢您提供如此完整的答案。我希望你能回答我问题的EDIT部分的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多