在社区中似乎存在很多关于如何表达bean配置的困惑,或者更确切地说如何将bean配置从XML转换为Java(或Groovy,或Kotlin,或......),因为它看起来好像不同的配置方式产生的是完全不同的东西。
但事实上,它们或多或少是以格式(Java,XML,Properties)表示的配置,以指示Spring ApplicationContext
(实际上是BeanFactory
)根据这些定义(aka BeanDefinition
)创建bean。A BeanDefinition
基本上是如何准备/创建bean的方法。
让我们使用几个类创建一个小样本,来深入了解这些类的内容。
public class Person {
private String name;
public void setName(String name) {
this.name=name;
}
public String getName() {
return this.name;
}
}
服务的接口问候Person
。
public interface Greeter {
void greet(Person person);
}
将问候语打印到标准输出的实现。
public class SystemOutGreeter implements Greeter {
public void greet(Person person) {
System.out.printf("Greetings %s!%n", person.getName());
}
}
使用Property文件进行配置
现在让我们进入一个时间机器,回到Spring的开始15年。在XML和注释之出现之前使用属性配置文件的时候。创建一个application-context.properties
并让我们使用它来引导我们的ApplicationContext
。是的,今天仍然使用Spring 5,我们可以使用属性文件来创建ApplicationContext
。它甚至可以通过类等方式在框架内使用ResourceBundleViewResolver
person.(class)=biz.deinum.blog.context.springcontexts.Person
person.name=Marten Deinum
greeter.(class)=biz.deinum.blog.context.springcontexts.SystemOutGreeter
现在我们已经拥有了所有移动部件,让我们用它创建一个ApplicationContext。
public class SpringContextsApplication {
public static void main(String[] args) throws Exception {
GenericApplicationContext contextFromProperties =
new GenericApplicationContext();
BeanDefinitionReader reader =
new PropertiesBeanDefinitionReader(contextFromProperties);
reader.loadBeanDefinitions("classpath:application-context.properties");
contextFromProperties.refresh();
doGreeting(contextFromProperties);
contextFromProperties.stop();
}
private static void doGreeting(ApplicationContext ctx) {
Greeter greeter = ctx.getBean(Greeter.class);
Person person = ctx.getBean(Person.class);
greeter.greet(person);
}
}
那么所有这一切都做到了。首先我们需要一个ApplicationContext
,为此我们可以使用GenericApplicationContext这个
类。然后我们需要一些东西加载我们的BeanDefinition
s,这是BeanDefinitionReader
发挥作用的地方。因为我们需要使用属性文件PropertiesBeanDefinitionReader
。PropertiesBeanDefinitionReader获取属性文件并将其转换为BeanDefinition实例。在我们的例子中,它将创建2个BeanDefinition
实例,一个是 Person
另一个实例SystemOutGreeter
。特殊.(class)
符号指定了bean的类型(有更多内容可以看到javadoc)。还有person.name = Marten Deinum,这会将Person的name属性设置为指定值。 它将通过使用反射来完成Spring中数据的绑定与支持。
加载的BeanDefinitions被添加到BeanFactory(the ApplicationContext is a BeanFactory on steroids),因此它可以创建实际的bean实例。
在我们使用ApplicationContext之前,我们必须在其上调用refresh()方法。 注意:在大多数情况下,这是自动完成的!
之后,我们得到了Greeter
与Person
从ApplicationContext
和打印问候。
输出: Greetings, Marten Deinum!
创建person
和greeter
bean的最终结果(通过前面提到的反射)如下:
Person person = new Person();
person.setName("Marten Deinum");
和
Greeter greeter = new SystemOutGreeter();
使用XML进行配置
现在让我们对XML做同样的事情。创建一个applicationContext.xml
配置Person
和SystemOutGreeter
。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="greeter" class="biz.deinum.blog.context.springcontexts.SystemOutGreeter" />
<bean id="person" class="biz.deinum.blog.context.springcontexts.Person">
<property name="name" value="Marten Deinum" />
</bean>
</beans>
XML比属性文件更详细。我们有一个bean
元素,该元素又有一个class
属性来指定bean的类型。在bean
元素内部,我们可以指定property
元素来指定要设置的属性,在我们的示例中,我们将name
属性再次设置为给定值。
要加载此文件,我们需要一个BeanDefinitionReader
可以加载XML文件的文件,因此要用到XmlBeanDefinitionReader
。
public class SpringContextsApplication {
public static void main(String[] args) throws Exception {
GenericApplicationContext contextFromXml =
new GenericApplicationContext();
BeanDefinitionReader reader =
new XmlBeanDefinitionReader(contextFromXml);
reader.loadBeanDefinitions("classpath:applicationContext.xml");
contextFromXml.refresh();
doGreeting(contextFromXml);
contextFromXml.stop();
}
private static void doGreeting(ApplicationContext ctx) {
Greeter greeter = ctx.getBean(Greeter.class);
Person person = ctx.getBean(Person.class);
greeter.greet(person);
}
}
代码与具有属性文件的代码大致相同,除了突出显示的行,而不是加载属性文件现在使用XML文件进行配置。输出仍然相同。
创建person
和greeter
仍然是相同的,并再次归结为:
Person person = new Person();
person.setName("Marten Deinum");
和
Greeter greeter = new SystemOutGreeter();
但是通常你不会编写这段代码,而是你会用它ClassPathXmlApplicationContext
来为你完成所有这些工作。
public class SpringContextsApplication {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext contextFromXml =
new ClassPathXmlApplicationContext("applicationContext.xml");
doGreeting(contextFromXml);
contextFromXml.stop();
}
... // Method omitted
}
此代码和上面的代码在内部发生的情况基本相同,但是这要短得多。
使用Java进行配置
最后,我们可以使用Java代码来进行配置。在Spring中,Java配置类是一个使用注释@Configuration
的方法注释的常规类@Bean
。
@Configuration
public class ApplicationContext {
@Bean
public Person person() {
Person person = new Person();
person.setName("Marten Deinum");
return person;
}
@Bean
public SystemOutGreeter greeter() {
return new SystemOutGreeter();
}
}
注意:以下代码将不会编译,因为它使用Spring中不可访问的类,它仅仅是为了显示它在Spring内部是如何工作的(以简化形式!)。
public class SpringContextsApplication {
public static void main(String[] args) throws Exception {
GenericApplicationContext contextFromJava =
new GenericApplicationContext();
ConfigurationClassBeanDefinitionReader reader =
new ConfigurationClassBeanDefinitionReader(contextFromJava);
reader.loadBeanDefinitions(Collections.singletonSet(
new ConfigurationClass(ApplicationContext.class, "applicationContext")));
contextFromJava.refresh();
doGreeting(contextFromJava);
contextFromJava.stop();
}
private static void doGreeting(ApplicationContext ctx) {
Greeter greeter = ctx.getBean(Greeter.class);
Person person = ctx.getBean(Person.class);
greeter.greet(person);
}
}
这或多或少是当Spring找到Java配置类时发生的事情。然而,所有这些都隐藏在AnnotationConfigApplicationContext Class
内。
public class SpringContextsApplication {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext contextFromJava =
new AnnotationConfigApplicationContext(ApplicationContext.class);
doGreeting(contextFromJava);
contextFromJava.stop();
}
private static void doGreeting(ApplicationContext ctx) {
Greeter greeter = ctx.getBean(Greeter.class);
Person person = ctx.getBean(Person.class);
greeter.greet(person);
}
结论
现在看到所有3种不同的配置机制的结论是什么。前两个机制转换为与@Bean
注释方法中发生的相同。
基于.(class)
for属性文件和基于class
XML的配置的属性,您可以得出结论Spring new <class-value-here>()
自己。然后它将set<property-name-here>(<property-value-here>)
像在基于Java的配置中一样调用,或者<constructor-arg>
在XML配置中使用时传递构造函数参数)。
Properties | XML | Java |
---|---|---|
.(class)=Person | class=Person | Name of the class |
person.name=Marten Deinum | <property name="name" value="Marten Deinum" | setName("Marten Deinum") |
对于那些感兴趣的人,可以在GitHub上找到源代码。
https://github.com/mdeinum/blog-spring-contexts
本文翻译自谷歌社群。原文地址:https://mdeinum.github.io/2018-04-12-on-spring-applicationcontext-and-bean-creation/