我正在将一些基于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中?
答案 0 :(得分:3)
我从您的评论中了解到,您正在寻找一种如何在Java配置中实现构造函数自动装配(带有类型或注释自动装配)的方法...即相当于:
<bean class="com.example.Foo" autowire="constructor" />
我已经检查了Spring的源代码,找到了实际负责构造函数解析和自动装配的人,它是包私有类ConstructorResolver
和AbstractAutowireCapableBeanFactory
的组合。所以这是你自己无法使用的东西。
如果你真的想在没有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());
}
}
然后需要通过@Bean
中的静态@Configuration
方法注册此bean工厂后处理器:
@Configuration
public class MyAppConfig {
public static BeanFactoryPostProcessor customBeanDefinitionRegistrar() {
return new CustomBeanDefinitionRegistrar();
}
}
我承认它有点“狂野的方法”,但它似乎是如何用纯Java配置实现构造函数自动装配的唯一方法。
答案 1 :(得分:2)
您需要通知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或首先检索它。
参考:
答案 2 :(得分:1)
这样的设施是否存在于Spring中?
答案很简单,但不是很干净。
Spring中的所有bean定义都取决于BeanDefinition
中的ApplicationContext
对象。
使用类似
的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
,但具体说明应该从@Configuration
bean生成bean作为工厂bean,并将someType()
方法作为工厂方法生成。 ApplicationContext
因此不必自行决定,它只是执行BeanDefinition
所说的。
(不完整,我稍后会回来完成这个。)
您必须了解XML和Java配置不同,但尝试实现相同的目标。
当您声明XML <bean>
元素时,如此
<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仍然可以像其他任何人一样自动装配。如果他们有@Autowired
个字段或方法,则会注入这些字段或方法。
所有上述配置都告诉Spring如何创建bean。像这样的东西
@Bean
public MyFunkySpringBean funkyBean; // No instance -- it's up to Spring to build
除非设置了约定,否则实际上没有说什么。我认为使用经典@Bean
方法无法获得该功能的任何优势。
答案 3 :(得分:1)
在较新的Spring版本(从4.2版本开始)中,您可以使用org.springframework.context.annotation.Import
例如
@Configuration
@Import(MyFunkySpringBean.class)
public class MyAppConfig {
... other config ...
}