共计 7728 个字符,预计需要花费 20 分钟才能阅读完成。
1.3。Bean 总览
Spring IoC 容器治理一个或多个 bean。这些 bean 是应用您提供给容器的配置元数据创立的(例如,以 XML <bean/> 定义的模式)。
在容器自身内,这些 bean 定义示意为 BeanDefinition 对象,其中蕴含(除其余信息外)以下元数据:
包限定的类名:通常,定义了 Bean 的理论实现类。
Bean 行为配置元素,用于申明 Bean 在容器中的行为(作用域,生命周期回调等)。
援用其余 bean 进行其工作所需的 bean。这些援用也称为协作者或依赖项。
要在新创建的对象中设置的其余配置设置 - 例如,池的大小限度或在治理连接池的 Bean 中应用的连接数。
该元数据转换为形成每个 bean 定义的一组属性。下表形容了这些属性:
属性 | 解释 |
---|---|
类 | 实例化 bean |
名称 | bean 名称 |
生命周期 | bean 生命周期 |
结构函数参数 | 依赖注入 |
属性 | 依赖注入 |
主动注入模式 | 主动注入的合作者 |
提早初始化模式 | 懒初始化 bean |
初始化办法 | 初始化回调 |
销毁形式 | 销毁回调 |
除了蕴含无关如何创立特定 bean 的信息的 bean 定义外,这些 ApplicationContext 实现还容许注册在容器内部(由用户)创立的现有对象。这是通过办法拜访 ApplicationContext 的 BeanFactory 的 getBeanFactory()来实现,该办法返回 BeanFactory 的 DefaultListableBeanFactory 实现。DefaultListableBeanFactory 通过 registerSingleton(..)和 registerBeanDefinition(..)办法反对此注册。然而,典型的应用程序仅应用通过惯例 bean 定义元数据定义的 bean。
自定义类 不带任何注解
public class LearnBean {public LearnBean(String name) { }
public String getString(){return "learnSpring";}
}
第一种:AnnotationConfigApplicationContext 自带的 registerBean 办法,能够传入 class 和结构参数
AnnotationConfigApplicationContext annotationConfigApplicationContext =
new AnnotationConfigApplicationContext();
annotationConfigApplicationContext.registerBean(LearnBean.class,"");
annotationConfigApplicationContext.refresh();
LearnBean bean = (LearnBean) annotationConfigApplicationContext.getBean("learnBean");
System.out.println(bean.getString());
第二种:还能够传入 class 和 beanDefinition 也就是配置元数据 这个和 getBeanFactory().registerBeanDefinition(..)是一个意思
AnnotationConfigApplicationContext annotationConfigApplicationContext =
new AnnotationConfigApplicationContext();
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(LearnBean.class);
ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
constructorArgumentValues.addIndexedArgumentValue(0,"111");
rootBeanDefinition.setConstructorArgumentValues(constructorArgumentValues);
annotationConfigApplicationContext.registerBeanDefinition("learnBean",rootBeanDefinition);
annotationConfigApplicationContext.refresh();
LearnBean bean = (LearnBean) annotationConfigApplicationContext.getBean("learnBean");
System.out.println(bean.getString());
留神:如果不写
annotationConfigApplicationContext.refresh();
就会报错
Exception in thread "main" java.lang.IllegalStateException: org.springframework.context.annotation.AnnotationConfigApplicationContext@68de145 has not been refreshed yet
at org.springframework.context.support.AbstractApplicationContext.assertBeanFactoryActive(AbstractApplicationContext.java:1096)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1108)
at org.springframework.example.DebuggerSpringMain.main(DebuggerSpringMain.java:40)
Bean 元数据和手动提供的单例实例须要尽早注册,以便容器在主动拆卸和其余自省步骤中正确地推理它们。尽管在某种程度上反对笼罩现有元数据和现有单例实例,然而在运行时(与对工厂的实时拜访同时)对新 bean 的注册不被正式反对,并且可能导致并发拜访异样,bean 容器中的状态不统一或都。
很显然注册太晚,就无奈和 Spring 根底的步骤交融,一些依赖注入无奈实现
1.3.1。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 提供 name 或 id。如果不提供 name 或 id 显式提供,则容器将为该 bean 生成一个惟一的名称。
然而,如果您心愿通过应用 ref 元素或服务定位器款式查找通过名称援用那个 bean,那么您必须提供一个名称。应用外部 bean 和 主动装配合的时候 通常不须要应用名称。
###### Bean 命名约定
约定是在命名 bean 时将规范 Java 约定用于实例字段名称。也就是说,bean 名称以小写字母结尾,并从那里用驼峰式大小写。这样的名字的例子包含 accountManager,accountService,userDao,loginController,等等。统一地命名 Bean 使您的配置更易于浏览和了解。另外,如果您应用 Spring AOP,则在将切点利用于名称相干的一组 bean 时,它会很有帮忙。通过在类门路中进行组件扫描,Spring 会依照后面形容的规定为未命名的组件生成 Bean 名称:实质上,采纳简略的类名称并将其初始字符转换为小写。然而,在(不寻常的)非凡状况下,如果有多个字符并且第一个和第二个字符均为大写字母,则会保留原始大小写。这些规定与 java.beans.Introspector.decapitalize(由 Spring 在此处应用)定义的规定雷同。
在 Bean 定义之外别名 Bean
在 bean 定义自身中,能够通过应用 id 属性指定的最多一个名称和属性中任意数量的其余名称的组合来为 bean 提供多个名称 name。这些名称能够是同一个 bean 的等效别名,并且在某些状况下很有用,例如,通过应用特定于该组件自身的 bean 名称,让应用程序中的每个组件都援用一个公共依赖项。
然而,在理论定义 bean 的中央指定所有别名并不总是足够的。有时须要为在别处定义的 bean 引入别名。在大型零碎中通常是这种状况,在大型零碎中,配置在每个子系统之间调配,每个子系统都有本人的对象定义集。在基于 XML 的配置元数据中,您能够应用 <alias/> 元素来实现此工作。以下示例显示了如何执行此操作:
<alias name="fromName" alias="toName"/>
在这种状况下,(在同一个容器中)名为 fromName 的 bean 在应用了这个别名定义之后,也能够被称为 toName。
例如,
子系统 A 的配置元数据可能援用一个名为 subsystem-DataSource 的数据源。
子系统 B 的配置元数据能够援用名为 subsystembl-DataSource 的数据源。
在编写应用这两个子系统的主应用程序时,主应用程序以 myApp-dataSource 的名称援用数据源。
要让这三个名称援用同一个对象,您能够在配置元数据中增加以下别名定义:
<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>
当初,每个组件和主应用程序都能够通过惟一的名称援用数据源,并且能够保障不与任何其余定义抵触(无效地创立名称空间),然而它们援用的是同一 bean。
Java-configuration
如果应用 Java-configuration,则 @Bean 注解可用于提供别名。无关详细信息,请参见应用 @Bean 注解。
1.3.2。实例化 bean
bean 定义实质上是创立一个或多个对象的窍门。当被申请时,容器查看已命名 bean 的配方,并应用该 bean 定义封装的配置元数据来创立 (或获取) 理论对象。
如果应用基于 XML 的配置元数据,则能够在元素的 class 属性中指定要实例化的对象的类型(或类)<bean/>。此 class 属性(在外部是实例的 Class 属性 BeanDefinition)通常是必须的。
以下两种状况会用到 Class 属性:
通常,在容器自身通过反射调用其构造函数间接创立 bean 的状况下,指定要结构的 bean 类,这在某种程度上等同于应用 new 操作符的 Java 代码。要指定蕴含为创建对象而调用的动态工厂办法的理论类,在容器调用类上的动态工厂办法来创立 bean 的状况下就不太常见了。从动态工厂办法调用返回的对象类型能够是雷同的类,也能够齐全是另一个类。
外部类名称
如果心愿为动态嵌套类配置 bean 定义,则必须应用嵌套类的二进制名称。例如,如果你在 com 中有一个叫做什么的类。这个货色类有一个名为 OtherThing 的动态嵌套类,bean 定义上的 class 属性的值将是 com.example.SomeThing$OtherThing。请留神名称中应用了 $ 字符来分隔嵌套的类名和外部类名。<bean id="innerClass1" class="org.springframework.example.config.MyBean$InnerClass"/>
用构造函数实例化
当通过构造方法创立一个 bean 时,所有一般类都能够被 Spring 应用并与之兼容。也就是说,正在开发的类不须要实现任何特定的接口或以特定的形式进行编码。只需指定 bean 类就足够了。然而,依据您用于该特定 bean 的 IoC 的类型,您可能须要一个默认(空)构造函数。
Spring IoC 容器简直能够治理您要治理的任何类。它不仅限于治理真正的 JavaBean。大多数 Spring 用户更喜爱理论的 JavaBean,它仅具备默认(无参数)构造函数,并具备依据容器中的属性建模的适当的 setter 和 getter。您还能够在容器中具备更多奇异的非 bean 款式类。例如,如果您须要应用相对不合乎 JavaBean 标准的新式连接池,则 Spring 也能够对其进行治理。
应用基于 XML 的配置元数据,您能够如下指定 bean 类:
<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
无关用于向构造函数提供参数(如果须要)并在结构对象之后设置对象实例属性的机制的详细信息,请参见 注入依赖项。
用动态工厂办法实例化
在定义应用动态工厂办法创立的 bean 时,请应用 class 属性指定蕴含 static 工厂办法的类,并应用命名 factory-method 为属性的属性来指定工厂办法自身的名称。您应该可能调用此办法(应用可选参数,如稍后所述)并返回一个流动对象,该对象随后将被视为已通过构造函数创立。这种 bean 定义的一种用法是 static 用旧代码调用工厂。
以下 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:
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
</bean>
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
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"/>
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 实现类。
确定 Bean 的运行时类型
确定特定 bean 的运行时类型并非易事。Bean 元数据定义中的指定类只是初始类援用,可能与申明的工厂办法联合应用,或者是 FactoryBean 可能导致 Bean 的运行时类型不同的类,或者在实例的状况下基本不设置 - 级别工厂办法(通过指定 factory-bean 名称解析)。另外,AOP 代理能够应用基于接口的代理包装 bean 实例,而指标 Bean 的理论类型(仅是其实现的接口)的裸露水平无限。
找出特定 bean 的理论运行时类型的举荐办法是 BeanFactory.getType 调用指定的 bean 名称。这思考了上述所有状况,并返回了 BeanFactory.getBean 要针对雷同 bean 名称返回的对象的类型。