(spring-第10回【IoC基础篇】)InstantiationStrategy--实例化Bean的第三大利器

Bean的实例化整个过程如下图:

其中,BeanDefinition加入到注册表中,并由BeanFactoryPostProcessor的实现类处理后,需要由InstantiationStrategy负责实例化。实例化仅仅是调用构造函数,相当于new了一个对象而已,bean的具体的属性在此时并未赋值(当然,一开始在XML中配置了Bean属性的值,或者在构造函数中有赋值语句的话,相关属性才会在实例化的时候便有了值。)。InstantiationStrategy负责由Bean类的默认构造函数、带参构造函数或者工厂方法等来实例化Bean。下面是Instantiation

Strategy的继承结构(注意下面是父类,上面是子类,实线是继承,虚线是实现):

InstantiationStrategy只是一个策略性的接口。

SimpleInstantiationStrategy是InstantiationStrategy的实现类,该类是一个简单的用于Bean实例化的类,比如,由Bean类的默认构造函数、带参构造函数或者工厂方法等来实例化Bean。从上图中可以看出,该类有一个instantiationWithMethodInjection方法,但是实际上这只是个钩子(hook),并非真正支持方法注入功能。

方法注入:在大部分情况下,容器中的bean都是singleton类型的(默认),单例类型是指spring只会实例化一次bean,并将bean放到缓冲池中,把bean的引用(地址)返回给调用者。如果一个singleton bean要引用另外一个singleton bean,或者一个prototype的bean引用另外一个prototype的bean时,通常情况下将一个bean定义为另一个bean的property值就可以了。就像下面这样:

<bean id="boss" class="com.baobaotao.attr.Boss">
        <property name="car">
            <ref parent="car" />
        </property>
    </bean>

不过对于具有不同生命周期的bean来说这样做就会有问题了,比如在调用一个singleton类型bean A的某个方法时,需要引用另一个prototype类型(每次调用都会重新实例化bean)的bean B,对于bean A来说,容器只会创建一次,这样就没法在需要的时候每次让容器为bean A提供一个新的的bean B实例。也就是说,每次调用A时,我需要一个重新实例化的B。而由于A只会实例化一次,并且B是随着A的实例化而实例化的,导致我得到的B也是没有再次实例化的。这个时候就要使用方法注入。举个简单例子:

1 <bean id="car" class="com.baobaotao.injectfun.Car"
2         p:brand="红旗CA72" p:price="2000" scope="prototype"/>
3
4   <bean id="magicBoss" class="com.baobaotao.injectfun.MagicBoss" >
5     <lookup-method name="getCar" bean="car"/>
6   </bean>

使用lookup-method标签,这样,每次实例化magicBoss时就会加载它的getCar方法,如下:

public interface MagicBoss {
   Car getCar();
}

由于lookup-method里面定义了bean="car",spring会自动实例化car。相当于在getCar()里面写了一个实例化car的方法。

真正支持方法注入功能的是SimpleInstantiationStrategy的继承类:CglibSubclassingInstantiationStrategy。它继承了SimpleInstantiationStrategy并覆盖了instantiationWithMethodInjection方法。不过使用这个方法必须用到cglib 类库。它利用cglib为bean动态生成子类,这个类叫代理类,在子类中生成方法注入的逻辑,然后使用这个动态生成的子类创建bean的实例。(具体了解该技术,请学习spring的AOP,面向切面编程。后面章节我会详细讲到)。

下面大概看一下默认调用的SimpleInstantiationStrategy的instantiate方法:

 1 public Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) {
 2         // Don‘t override the class with CGLIB if no overrides.
 3         if (beanDefinition.getMethodOverrides().isEmpty()) {
 4             Constructor<?> constructorToUse;
 5             synchronized (beanDefinition.constructorArgumentLock) {
 6                 constructorToUse = (Constructor<?>) beanDefinition.resolvedConstructorOrFactoryMethod;
 7                 if (constructorToUse == null) {
 8                     final Class clazz = beanDefinition.getBeanClass();
 9                     if (clazz.isInterface()) {
10                         throw new BeanInstantiationException(clazz, "Specified class is an interface");
11                     }
12                     try {
13                         if (System.getSecurityManager() != null) {
14                             constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor>() {
15                                 public Constructor run() throws Exception {
16                                     return clazz.getDeclaredConstructor((Class[]) null);
17                                 }
18                             });
19                         }
20                         else {
21                             constructorToUse =    clazz.getDeclaredConstructor((Class[]) null);
22                         }
23                         beanDefinition.resolvedConstructorOrFactoryMethod = constructorToUse;
24                     }
25                     catch (Exception ex) {
26                         throw new BeanInstantiationException(clazz, "No default constructor found", ex);
27                     }
28                 }
29             }
30             return BeanUtils.instantiateClass(constructorToUse);
31         }
32         else {
33             // Must generate CGLIB subclass.
34             return instantiateWithMethodInjection(beanDefinition, beanName, owner);
35         }
36     }

由于前期帖不会过多去讲源码,所以只是大概了解一下,从第7行和第21行可以看出:如果bean没有自己的构造函数,那么使用反射机制调用默认的无参构造函数去实例化bean。最后,30行,拿到这个构造函数,执行BeanUtils.instantiateClass方法。下面是该方法:

 1 public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
 2         Assert.notNull(ctor, "Constructor must not be null");
 3         try {
 4             ReflectionUtils.makeAccessible(ctor);
 5             return ctor.newInstance(args);
 6         }
 7         catch (InstantiationException ex) {
 8             throw new BeanInstantiationException(ctor.getDeclaringClass(),
 9                     "Is it an abstract class?", ex);
10         }
11         catch (IllegalAccessException ex) {
12             throw new BeanInstantiationException(ctor.getDeclaringClass(),
13                     "Is the constructor accessible?", ex);
14         }
15         catch (IllegalArgumentException ex) {
16             throw new BeanInstantiationException(ctor.getDeclaringClass(),
17                     "Illegal arguments for constructor", ex);
18         }
19         catch (InvocationTargetException ex) {
20             throw new BeanInstantiationException(ctor.getDeclaringClass(),
21                     "Constructor threw exception", ex.getTargetException());
22         }
23     }

第四行和第五行就是创建实例了(首先需要把得到的构造函数强设为可访问)。

由InstantiationStrategy实例化的bean只是相当于生成了一个新对象,具体的属性赋值工作还要由BeanWrapper结合属性编辑器来完成。BeanWrapper和属性编辑器将会在接下来的博文中详细介绍。

          学而不知道,与不学同;知而不能行,与不知同。

                                ——黄睎

时间: 2024-10-19 01:18:50

(spring-第10回【IoC基础篇】)InstantiationStrategy--实例化Bean的第三大利器的相关文章

(spring-第9回【IoC基础篇】)BeanFactoryPostProcessor,实例化Bean之前的第二大利器

继承结构图如上.在加载XML,注册bean definition之后,在实例化bean definition之前,必要的时候要用到BeanFactoryPostProcessor.它负责把XML中有些占位符式的属性还原成真实值.意思是说,有时候,XML中<bean>的属性值不固定,会随着外界因素变化,这时候,在<bean>中配置占位符,而另外定义一个属性文件来控制<bean>的属性.比如下面是一个数据库连接的XML配置: 1 <bean id="data

(spring-第13回【IoC基础篇】)PropertyEditor(属性编辑器)--实例化Bean的第五大利器

上一篇讲到JavaBeans的属性编辑器,编写自己的属性编辑器,需要继承PropertyEditorSupport,编写自己的BeanInfo,需要继承SimpleBeanInfo,然后在BeanInfo中把特定的属性编辑器和需要编辑的属性绑定起来(详情请查看上一篇). Spring的属性编辑器仅负责将配置文件中的字面值转换成Bean属性的对应值.(而JavaBean的属性编辑器能够通过界面来手动设置bean属性的值).如果属性的类型不同,转换的方法就不同.正如javabean的属性编辑器一样,

(spring-第12回【IoC基础篇】)JavaBean的属性编辑器

在spring实例化bean的最后阶段,spring利用属性编辑器将配置文件中的文本配置值转换为bean属性的对应值,例如: 代码0011 <bean id="car" class="com.mesopotamia.test1.Car" 2 p:name="汽车" 3 p:brand="宝马" 4 p:maxSpeed="200"/> 上面是文本配置, 再看bean: 代码0021 public

(spring-第6回)BeanDefinition——实例化Bean的第一大利器。

上节讲了Bean实例化的内部机制,这里再复述一遍: ResourceLoader从系统中加载XML配置信息,并由Resource来表示. BeanDefinitionReader从Resource中读取配置信息,把配置文件中的<bean>解析成一个BeanDefinition对象,然后把BeanDefinition对象放到BeanDefinitionRegistry注册表中. 容器从BeanDefinitionRegistry注册表中扫描出Bean工厂后处理器的Bean(该Bean实现了Bea

(spring-第7回【IoC基础篇】)BeanDefinition的载入与解析&amp;&amp;spring.schemas、spring.handlers的使用

报错信息:Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/schema/p], 一桩事故引发的连锁思考...开幕—— ----------------------------------------------------------------------------------------------

(spring-第14回【IoC基础篇】)国际化信息 (转)

国际化又称为本地化. 当你把手机的language由中文切换到英文时,你的微信也相应改用英语,这就是i18n国际化.一般来说,应用软件提供一套不同语言的资源文件,放到特定目录中,应用根据不同语言的操作系统决定使用哪一种语言. 一般由两个条件限定一个国际化类型:语言类型和国家/地区类型.比如: 中文:语言类型:zh,国家/地区类型:CN(中国大陆)/HK(中国香港)/TW(中国台湾). 英语:语言类型:en,国家类型:EN. ------------------------------------

(spring-第14回【IoC基础篇】)国际化信息

国际化又称为本地化. 当你把手机的language由中文切换到英文时,你的微信也相应改用英语,这就是i18n国际化.一般来说,应用软件提供一套不同语言的资源文件,放到特定目录中,应用根据不同语言的操作系统决定使用哪一种语言. 一般由两个条件限定一个国际化类型:语言类型和国家/地区类型.比如: 中文:语言类型:zh,国家/地区类型:CN(中国大陆)/HK(中国香港)/TW(中国台湾). 英语:语言类型:en,国家类型:EN. ------------------------------------

(spring-第15回【IoC基础篇】)容器事件

五个人在报社订阅了报纸.报社一旦有了新报纸,就派员工分别送到这五个人手里.在这个例子中,“报纸”就是事件,“报社”就是广播器,五个订阅者就是监听器.广播器收到事件,把事件传给监听器,监听器对事件做一些事情.这样的例子属于观察者模式. 观察者模式:A类负责接收新信息,B.C.D类一直关注着A.A类一旦有了新信息,就发送给B.C.D类,B.C.D类收到信息作出不同的操作.这就是观察者模式.具体到代码中,A中设置一个变量message(这就是信息),当message改变时,调用B.C.D的perfor

(spring-第8回【IoC基础篇】)BeanDefinition在IoC容器中的注册

在spring中,所有的bean都是由BeanFactory进行管理的.下面是BeanFactory的类体系结构: 我们清楚的看到,DefaultListableBeanFactory继承了BeanFactory的优良传统,同时又实现了BeanDefinitionRegistry这个注册器,那么无疑,BeanDefinition在容器中的注册任务,非他莫属.事实上,DefaultListableBeanFactory拥有一个私有的BeanDefinitonMap属性,这个属性是个哈希Map,通过