一起来读官方文档-----SpringIOC(02)

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名称返回的对象的类型。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342