【问题标题】:Spring Java config : Specify class onlySpring Java 配置:仅指定类
【发布时间】:2014-06-12 08:34:04
【问题描述】:

我正在将一些基于 Xml 的 spring 配置转移到基于 Java 的过程中。

以前,我有意识地将 Xml <bean class='foo' /> 声明与 @Component 映射根据具体情况混合在一起,作为记录不明显 bean 的一种方法。

一个典型的用例是,如果我声明将修改 spring 本身行为的 bean,我将显式声明它们,而不是让它们被发现,纯粹是为了提高配置文件的清晰度。

但是,这些 bean 通常需要@Autowiring 的度数。如果我在 Java 配置中自己 new bean,我将负责执行此连接 - 这是没有意义的。

是否有一个纯 Java 配置选项可以显式地向 Spring 提供一个类,并让它管理实例化?

即:

给定班级:

public class MyFunkySpringBean implements InitializingBean {
    @Autowired Foo foo;
}

在 XML 配置中,这将被简单地声明为:

<bean class="MyFunkySpringBean" />

在 Java 语法中是否有等价物,我可以将 bean 类显式声明给 Spring,但不负责提供实际实例 - 将其留给 Spring。

例如:

@Configuration 
public class MyAppConfig {

    // Explictly provide the class, not the instance to Spring
    @Bean
    public MyFunkySpringBean funkyBean; // No instance -- it's up to Spring to build
}

明确地说,我不想必须这样做:

@Configuration 
public class MyAppConfig {

    @Bean
    public MyFunkySpringBean funkyBean() {
         MyFunkySpringBean bean = new MyFunkySpringBean();
         bean.foo = new Foo();
         // other dependencies go here
         return bean;
    }

}

这样的设施是否存在于 Spring 中?

【问题讨论】:

  • 我不明白这个问题。您想提供一个类并让 Spring 为您管理其生命周期(这是常见行为),或者您想定义应如何生成该类并让 Spring 在自动装配时管理此逻辑?
  • 第一个。我想要一种无 XML 的方式来提供一个类并让 Spring 管理它的生命周期。我不想对newing 负责。但是,我不想通过@Component 扫描发现它——我想明确说明这个类的声明。
  • 您改写的问题仍然没有使用构造函数自动装配!我可以看到您不了解 Spring 中的属性自动装配是如何工作的……有 bean 后处理器,它检测带有 @Autowired 注释的属性和方法。这些后处理器在使用 Java 配置时仍然处于活动状态。您不需要手动进行自动装配。它将在春天完成!您需要致电bean.foo = new Foo() 的假设不正确...再次检查Sotirios 答案(主要是强调的陈述)。
  • 更清楚一点 - bean 实例化和依赖注入(包括自动装配)是两个独立的过程......除非您使用构造函数注入。 构造函数自动装配 是目前 Java 配置中唯一无法实现的。在 Java 配置中,您负责创建实例......但是,一旦从 @Bean 方法返回实例,它将进行额外处理 - 代理、自动装配、特殊接口和注释处理(工厂 bean、初始化bean,常见注释,...)。所有的春天魔法仍然存在。

标签: java spring


【解决方案1】:

在较新版本的 Spring(从 4.2 开始)中,您可以使用 org.springframework.context.annotation.Import 例如

@Configuration
@Import(MyFunkySpringBean.class)
public class MyAppConfig {
   ... other config ...
}

【讨论】:

    【解决方案2】:

    这样的设施是否存在于 Spring 中?

    答案有点意思,但不干净。

    Spring 中的所有 bean 定义都依赖于 ApplicationContext 中的 BeanDefinition 对象。

    使用类似的 XML 配置

    <bean class="com.example.SomeType">
    </bean>
    

    Spring 将生成具有该类类型的 RootBeanDefinition 并作为单例,但没有关于如何构造它的其他信息。相反,ApplicationContext 将决定如何创建 bean。

    与Java配置类似

    @Bean
    public com.example.SomeType someType() {
        return new com.example.SomeType();
    }
    

    Spring 将创建一个 RootBeanDefinition,但特别声明该 bean 应从 @Configuration bean 作为工厂 bean 及其 someType() 方法作为工厂方法生成。因此,ApplicationContext 不必自行决定,它只是按照BeanDefinition 的指示行事。

    (不完整,我稍后会回来完成。)


    您必须了解 XML 和 Java 配置是不同的,但尝试实现相同的目标。

    当你像这样声明一个 XML &lt;bean&gt; 元素时

    <bean class="com.example.SomeType">
    </bean>
    

    您是在告诉 Spring 使用 SomeType 的无参数构造函数来实例化 bean 类并创建一个 bean。

    在 Java 中,等价于

    @Bean
    public com.example.SomeType someType() {
        return new com.example.SomeType();
    }
    

    同样,

    <bean class="com.example.SomeType">
        <constructor-arg type="java.lang.String" value="hello world"/>
        <property name="some" value="some value" />
    </bean>
    

    告诉 Spring 使用带有 String 类型参数的构造函数和该给定值来实例化 bean 类。然后设置一些属性。相当于

    @Bean
    public com.example.SomeType someType() {
        com.example.SomeType someType = new com.example.SomeType("hello world");
        someType.setSome("some value");
        return someType;
    }
    

    这些 bean 仍将像其他任何 bean 一样成为自动装配的候选者。如果他们有@Autowired 字段或方法,这些将被注入。

    以上所有配置都告诉 Spring 如何创建 bean。像这样的

    @Bean
    public MyFunkySpringBean funkyBean; // No instance -- it's up to Spring to build
    

    除非设置了约定,否则不会真正说什么。我认为该功能没有任何使用经典 @Bean 方法无法获得的优势。

    【讨论】:

    • 在 XML 示例中,如果com.example.SomeType 没有提供无参数构造函数,而是声明了一个参数化构造函数,并带有 @Autowired 注释,则会调用它,并由 Spring 注入相应的值。 Java 等价物是什么?
    • 在 XML 示例中,您没有告诉 Spring 要调用哪个构造函数。你在告诉它:“这是一堂课——使用反射来确定下一步该做什么”。不保证存在无参数构造函数。在 XML 中,您可以选择通过提供构造函数参数来获得更明确的信息。但是,省略它们并在 bean 上提供 @Autowired 参数化构造函数也是有效的。而且,如果你没有提供有效的配置,Spring 也会告诉你。没有明确的 Java 等价物。 (仅限 隐式 @Component 注释)
    • @MartyPitt 那么您要寻找的是通过 Java 配置进行的隐式构造函数调用吗?
    • @MartyPitt 但还可以像在 @Bean 方法中一样在 bean 上使用属性设置器(或任何其他方法)?
    • 我正在寻找一种显式的 java 机制,它可以将类型提交到与 xml 版本相同的实例化标准。 (即,使用反射,调用无参数构造函数(如果存在),或调用@Autowired 参数化构造函数,并将其连接起来)。
    【解决方案3】:

    我从您的 cmets 了解到,您正在寻找一种如何在 Java 配置中实现构造函数自动装配(使用类型或注释自动装配)的方法......即相当于:

    <bean class="com.example.Foo" autowire="constructor" />
    

    我已经检查了 Spring 的源代码,以找到真正负责构造函数解析和自动装配的人,它是包私有类 ConstructorResolverAbstractAutowireCapableBeanFactory 的组合。所以这是你自己无法使用的东西。

    如果您真的想在没有 XML 的情况下实现构造函数自动装配,您可以实现自己的 BeanDefinitionRegistryPostProcessor 并自己手动注册该 bean 定义:

    public class CustomBeanDefinitionRegistrar implements BeanDefinitionRegistryPostProcessor {
    
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
            // Not interested in this method
        }
    
        @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
            registry.registerBeanDefinition("foo", BeanDefinitionBuilder.
                    genericBeanDefinition(Foo.class).
                    setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR).
                    getBeanDefinition());
        }
    
    }
    

    然后需要通过 @Configuration 中的静态 @Bean 方法注册此 bean 工厂后处理器:

    @Configuration
    public class MyAppConfig {
    
        public static BeanFactoryPostProcessor customBeanDefinitionRegistrar() {
            return new CustomBeanDefinitionRegistrar();
        }
    
    }
    

    我承认这有点“野蛮”,但它似乎是使用纯 Java 配置实现构造函数自动装配的唯一方法。

    【讨论】:

      【解决方案4】:

      您需要通知 Spring 一种方法来发现将要管理的 bean。你只有三种方式(还有第四种,但它更复杂,也需要注释,解释here):

      • 基于 XML 的配置:

        <bean id="foo" class="some.package.Foo" />
        
      • 注解配置:

        @Component
        public class Foo {
        }
        
      • 创建一个明确声明 bean 的 @Configuration 类:

        @Configuration
        public class MyConfiguration {
            @Bean
            public Foo getFoo() {
                return new Foo(); //foo doesn't need any annotation at all.
            }
        }
        

      AFAIK 你可以使用 CDI 做一些类似的事情:

      class MyConfiguration {
      
          @Produces
          Foo foo = new Foo();
      }
      

      或者通过从另一个容器注入资源,比如 JPA:

      class MyConfiguration {
      
          //JPA will provide the object reference based on the persistence unit
          //CDI will just work as a by-pass to inject this reference in other beans
          @PersistenceContext //JPA annotation
          @Produces
          EntityManaged em;
      }
      

      但即使是 CDI 也需要一种方法来知道如何初始化 bean 或首先在哪里检索它。

      参考:

      【讨论】:

      • @MartyPitt 我真的错过了拥有它的意义。 Spring 如何知道要创建什么实例以及如何创建它?它在管理什么?
      • @MartyPitt 我对此表示怀疑。如果你的 bean 不提供默认构造函数会发生什么,Spring 应该如何管理它?或者如果你声明一个抽象类或接口,Spring 应该如何创建这样的实例?
      • @SotiriosDelimanolis & LuiggiMendonza : 绅士——这里表达的内容与通过 Xml 配置可以表达的内容没有区别。在 XML 中,可以(尽管无效)声明抽象类、具有非默认构造函数的类、模棱两可的类、原始类以及大量其他无效配置的定义。有一个丰富的分辨率算法可以评估并拒绝或允许这些配置。我认为应该允许 Java 配置明确包含在同一个解析过程中——仅此而已。
      • @MartyPitt 是的,Java 替代方法是 @Bean 方法。我仍然没有看到您提出的有效用例或有效解决方案。
      • 不,它们非常不同。 @Bean 提供一个值。 (“这是foo。我为你打造的”)。 Xml 提供了一个类,但没有值,并将其留给 Container 来提供。 (“给我建一个foo”)。两者的责任和义务相距甚远。与此概念最接近的 Java 等效项是 @Component。但是,这是隐式/发现的,而不是像 xml 版本那样显式/标定的。
      猜你喜欢
      • 2015-08-29
      • 2018-04-01
      • 2014-02-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-20
      • 2014-04-18
      • 1970-01-01
      相关资源
      最近更新 更多