Spring IOC容器管理一个或多个bean。这些bean是使用你提供给容器的配置元数据创建的(例如,以xml定义的形式)。
在容器本身中,这些bean表示为BeanDefinition对象,其中包含(除其他信息外)以下元数据:
这个些元数据包含了构造Bean定义的一组属性。下表描述了这些属性:
属性名 | 解释于... |
---|---|
Class | Instantiating Beans |
Name | Naming Beans |
Scope | Bean Scopes |
Constructor arguments | Dependency Injection |
Properties | Dependency Injection |
Autowiring mode | Autowiring Collaborators |
Lazy initialization mode | Lazy-initialized Beans |
Initialization method | Initialization Callbacks |
Destruction method | Destruction Callbacks |
除了包含有关如何创建特定bean的信息的bean定义之外,ApplicationContext实现还允许注册容器外部(由用户)创建的现有对象。 通过getBeanFactory()方法访问ApplicationContext的BeanFactory,该方法返回BeanFactory接口的DefaultListableBeanFactory实现方法。 DefaultListableBeanFactory通过registerSingleton(..)和registerBeanDefinition(..)这两个方法来注册外部Bean。 但是,应用程序通常只使用常规bean定义元数据来定义bean。
bean元数据和手动提供的单例实例需要尽早注册,以便容器在自动装配和其他自省步骤中正确地解析它们。
虽然在Spring某种程度上支持覆盖现有元数据和现有的单例实例,但是官方并不支持在运行时注册新bean(与工厂的实时访问同时进行),因为这可能会导致并发访问异常或者bean容器中的状态不一致。
每个bean都有一个或多个标识符。这些标识符在承载bean的容器中必须是唯一的。bean通常只有一个标识符。但是,如果它需要多个标志符,则可以将额外的标志符视为别名。
在基于XML的配置元数据中,可以使用id属性或name属性或者同时使用来指定bean标识符。ID属性允许你仅指定一个ID。通常来说,这些名称是字母数字(“mybean”、“someservice”等),但它们也可以包含特殊字符。如果要为bean引入其他别名,还可以在name属性中指定它们,用逗号(,),分号(;)或空格分隔。
在Spring3.1之前的版本中,id属性被定义为xsd:id类型,这限制了可能的字符。从3.1开始,它被定义为xsd:string类型。注意,bean id的唯一性仍然由容器强制实现,尽管不再由XML解析器实现。
你不需要强制的为bean提供名称或ID。如果不显式地提供名称或ID,容器将为该bean生成一个唯一的名称。但是,如果你希望通过使用ref元素或服务定位器风格来查找该bean,则必须提供一个名称。不提供名称仅用在使用内部bean和自动装配协作者的情况。
Bean命名规范
在命名bean时使用标准Java规范,例如字段名。也就是说,bean名称以一个小写字母开头的驼峰写法。比如:accountManager, accountService, userDao, loginController等。
bean命名的一致性使你的配置更易于阅读和理解。另外,如果你使用SpringAOP,那么一致性的Bean命名会在通知一组名字相关的Bean时很有用。
通过在类路径中扫描组件,Spring为未命名的组件生成bean名称,遵循前面描述的规则:本质上,取简单的类名并将其初始字符改为小写。但是,在特殊情况下,当有多个字符并且第一个和第二个字符都是大写时,原始的大小写将被保留。这些规则与java.beans.introspector.decapitalize定义的规则相同。
在Bean的定义外使用别名
在bean定义中,你可以为bean提供多个名称,可以通过id属性指定最多一个名称或者通过name属性指定任何数量的其他名称。 这些名称是同一Bean的等效别名,在某些情况下非常有用,例如,可以为一个通用的组件指定名字,并在应用程序的其他每个组件引用它。
只在bean实际定义的地方指定所有别名并不够用,有时需要为在别处定义的bean引入别名。在大型系统中,通常把系统拆分成多个子系统,每个子系统都有自己的对象定义集。在基于XML的配置元数据中,可以使用元素来实现这一点。以下示例显示了如何执行此操作:
<alias name="fromName" alias="toName"/>
在这种情况下,名为fromName的bean(在同一容器中)在使用这个别名定义之后,也可以称为toName。
例如,子系统A的配置元数据引用了名为subsystemA-dataSource的DataSource。子系统B的配置元数据引用了名为subsystemB-dataSource的DataSource。 当组成使用这两个子系统的主应用程序时,主应用程序以myApp-dataSource的名称引用DataSource。要使所有三个名称都指向同一对象,可以将以下别名定义添加到配置元数据中:
<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>
现在,每个组件和主应用程序都可以通过唯一的名称引用数据源,并保证不会与任何其他定义冲突(有效地创建了一个名称空间),但它们引用的是同一个bean。
JAVA配置
如果你使用JAVA配置,@Bean可以用来提供别名。
bean定义本质上是创建一个或多个对象的方法。当需要创建Bean的时候,容器会查看命名bean的创建方法,并使用由该bean定义封装的配置元数据来创建(或获取)实际对象。
如果使用基于XML的配置元数据,则需要指定要在< bean/>元素的class属性中实例化的对象的类型(或类名)。这个class属性(在内部是BeanDefinition实例上的Class属性)通常是强制的。(或者请参见使用实例工厂方法和bean定义继承进行实例化。)可以通过以下两种方式之一使用Class属性:
内部类名
如果要为静态内部类配置bean定义,则必须使用嵌套类的二进制名称。
例如,如果在com.example包中有一个名为SomeThing的类,而这个SomeThing类有一个名为OtherThing的静态内部类,bean定义中class属性的值将是com.example.SomeThing$OtherThing。
注意,在名称中使用$字符将内部类名与外部类名分开。
通过构造函数实例化
当使用构造函数创建bean时,所有普通类都可以被Spring使用并兼容。也就是说,正在开发的类不需要实现任何特定的接口或以特定的方式进行编码。仅仅指定bean类就足够了。但是,根据你对特定bean使用的IOC类型,你可能需要一个默认(空)构造函数。
SpringIOC容器几乎可以管理你想要管理的任何类。它不仅限于管理真正的JavaBeans。大多数Spring用户更喜欢实际的JavaBean,它只有一个默认(无参数)构造函数,以及根据容器中的属性创建的setter和getter方法。你还可以在容器中拥有更多其他非bean风格的类。例如,如果你需要使用完全不符合JavaBean规范的历史的连接池,那么Spring也可以对其进行管理。
使用基于XML的配置元数据,可以按如下方式指定bean类:
<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
有关向构造函数提供参数(如果需要)和在构造对象后设置对象实例属性的机制的详细信息,请参阅注入依赖。
使用静态工程方法实例化
定义使用静态工厂方法创建的bean时,使用class属性指定包含静态工厂方法的类,使用名为factory-method的属性指定工厂方法本身的名称。你应该能够调用这个方法(使用可选的参数,如后文所述),并返回一个活动对象,然后将其视为是通过构造函数创建的。这种bean定义的一个用途是调用遗留代码中的静态工厂。
下面的bean定义指定通过调用工厂方法来创建bean。定义没有指定返回对象的类型(类),只指定包含工厂方法的类。在本例中,createInstance()方法必须是静态方法。以下示例显示如何指定工厂方法:
<bean id="clientService"
class="examples.ClientService"
factory-method="createInstance"/>
下面是这个工厂类:
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
有关向工厂方法提供(可选)参数和在从工厂返回对象后设置对象实例属性的机制的详细信息,请参阅依赖和配置。
使用实例里面的工厂方法创建实例
与通过静态工厂方法进行实例化类似,使用实例工厂方法进行实例化会从容器中调用现有bean的非静态方法来创建新bean。要使用此机制,请将class属性保留为空,并在factory-bean属性中,在包含要调用以创建对象的实例方法的当前(或父或父)容器中指定bean的名称。使用factory-method属性设置工厂方法本身的名称。下面的示例演示如何配置这样的bean:
<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<!-- the bean to be created via the factory bean -->
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
下面列出了相应的Java类:
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
}
如下所示,一个工厂类可以包括多个工厂方法:
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
<bean id="accountService"
factory-bean="serviceLocator"
factory-method="createAccountServiceInstance"/>
下面列出了相应的Java类:
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
private static AccountService accountService = new AccountServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
public AccountService createAccountServiceInstance() {
return accountService;
}
}
这种方法表明,工厂bean本身可以通过依赖注入(DI)进行管理和配置。请参阅依赖和配置。
在Spring文档中,“factory bean”是指在Spring容器中配置的bean,它通过实例或静态工厂方法创建对象。 相比之下,FactoryBean(注意大写)是指Spring中的FactoryBean类。