spring学习(八)——spring官方文档阅读(5.0.7)——Bean的继承关系、Spring IOC容器的扩展

Bean的继承关系

bean的定义包括许多配置信息,例如构造参数、属性值、静态工厂方法名等,子bean定义从父bean定义继承配置数据,子bean可以覆盖或是添加某些值,子bean的定义由ChildBeanDefinition类决定,如果我们使用XML作为配置,方式如下:
 

<bean id="inheritedTestBean" abstract="true"
        class="org.springframework.beans.TestBean">
    <property name="name" value="parent"/>
    <property name="age" value="1"/>
</bean>

<bean id="inheritsWithDifferentClass"
        class="org.springframework.beans.DerivedTestBean"
        parent="inheritedTestBean" init-method="initialize">
    <property name="name" value="override"/>
    <!-- the age property value of 1 will be inherited from parent -->
</bean>

通过parent属性指定父bean,子bean会继承父bean的scope、父类构造函数参数值、属性值、方法等,子类指定的scope、初始化方法、销毁方法、静态工厂方法等将会覆盖父类中的相应方法或属性(若不覆盖,则默认使用父类的相应方法),依赖、自动装配、依赖检查、懒加载将直接从子bean中获取

我们也可以将上述例子改为:

<bean id="inheritedTestBeanWithoutClass" abstract="true">
    <property name="name" value="parent"/>
    <property name="age" value="1"/>
</bean>

<bean id="inheritsWithClass" class="org.springframework.beans.DerivedTestBean"
        parent="inheritedTestBeanWithoutClass" init-method="initialize">
    <property name="name" value="override"/>
    <!-- age will inherit the value of 1 from the parent bean definition-->
</bean>

此时spring应该会默认组织一个类名,如果我们将abstract=true的类作为某个bean的依赖,或是使用getBean()显示获得abstract=true的bean,则会出现错误提示,容器内部的preInstantiateSingletons()方法会忽略abstract为true的类的定义,abstract为true的类只作为模板(用于继承),不会初始化为bean

Spring IOC容器的扩展

BeanPostProcessor

BeanPostProcessor接口定义了回调方法,我们可以实现这些方法来提供自己的(或覆盖容器的默认)实例化逻辑、依赖解析逻辑等等,我们可以为一个bean定义多个BeanPostProcessor实例,为了控制多个实例执行的顺序,需要继承Ordered接口,接口中的order属性决定了实例执行的顺序,BeanPostProcessor只在一个容器内中有效,

BeanPostProcessor定义了两个回调方法:

public interface BeanPostProcessor {
    //bean初始化方法调用前被调用,bean为当前初始化完毕的bean,beanName为bean的名字
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    //bean初始化方法(不是构造函数)调用后被调用
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}

BeanPostProcessor接口的实现类必须比其他bean优先实例化,实例化后将作用于所有的Bean,ApplicationContext自动检测继承了BeanPostProcessor接口的类,如果我们用工厂方法返回BeanPostProcessor,那么返回的类型应该是BeanPostProcessor或是其实现类,不论如何,必须让springIOC意识到这是一个继承了BeanPostProcessor的类

除了上述自动检测的方式外,我们也可以通过硬编码的方式显式注册BeanPostProcessor:使用ConfigurableBeanFactory的addBeanPostProcessor方法,用这种方式不会遵循Order接口的order属性,显式注册的顺序就是执行的顺序,硬编码方式比自动检测优先级高

举个例子:

package scripting;

import org.springframework.beans.factory.config.BeanPostProcessor;

public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {

    // simply return the instantiated bean as-is
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean; // we could potentially return any object reference here...
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("Bean '" + beanName + "' created : " + bean.toString());
        return bean;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:lang="http://www.springframework.org/schema/lang"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/lang
        http://www.springframework.org/schema/lang/spring-lang.xsd">

    <lang:groovy id="messenger"
            script-source="classpath:org/springframework/scripting/groovy/Messenger.groovy">
        <lang:property name="message" value="Fiona Apple Is Just So Dreamy."/>
    </lang:groovy>

    <!--
    when the above bean (messenger) is instantiated, this custom
    BeanPostProcessor implementation will output the fact to the system console
    -->
    <bean class="scripting.InstantiationTracingBeanPostProcessor"/>

</beans>

注意到InstantiationTracingBeanPostProcessor没有定义id、name等,但是它仍然可以作为其他bean的依赖,这里简单起见就没写,上面用到了Groovy(官方文档这么写的),暂时不用管

启动类:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scripting.Messenger;

public final class Boot {

    public static void main(final String[] args) throws Exception {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("scripting/beans.xml");
        Messenger messenger = (Messenger) ctx.getBean("messenger");
        System.out.println(messenger);
    }

}

执行结果:

Bean 'messenger' created : org.springframework.scripting.groovy.GroovyMessenger@272961
org.springframework.scripting.groovy.GroovyMessenger@272961

另外一个例子是Spring已经实现的RequiredAnnotationBeanPostProcessor类,这个类负责保证用@Required注释的属性一定获得了值,否则会抛出异常

 

使用BeanFactoryPostProcessor自定义配置元数据

在IOC容器初始化任何bean之前(除了BeanFactoryPostProcessor),org.springframework.beans.factory.config.BeanFactoryPostProcessor可以读取配置元数据,并且可以更改其中的值,如果我们想更改bean的实例,那最好使用BeanPostProcessor,因为BeanFactoryPostProcessor比任何Bean都要早实例化(包括BeanPostProcessor),这将导致bean过早实例化,会产生一些负面影响,比如绕过了BeanPostProcessor的处理(此时BeanPostProcessor还没有实例化),BeanFactoryPostProcessor作用的范围为单个容器,spring提供了预先定义好的BeanFactoryPostProcessor的实现类,例如PropertyOverrideConfigurer、PropertyPlaceholderConfigurer,任何BeanFactoryProcessor的实现类都会被spring自动检测

public interface BeanFactoryPostProcessor {  
      
        /** 
         * Modify the application context's internal bean factory after its standard 
         * initialization. All bean definitions will have been loaded, but no beans 
         * will have been instantiated yet. This allows for overriding or adding 
         * properties even to eager-initializing beans. 
         * @param beanFactory the bean factory used by the application context 
         * @throws org.springframework.beans.BeansException in case of errors 
         */  
        void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;  
      
    }

懒加载对于BeanFactoryPostProcessor和BeanPostProcessor的实现类来说是无效的

例子:PropertyPlaceholderConfigurer

PropertyPlaceholderConfigurer可以在运行前将占位符改为实际值:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations" value="classpath:com/foo/jdbc.properties"/>
</bean>

<bean id="dataSource" destroy-method="close"
        class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

实际值来自其他文件夹:

jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root

在运行的时候,PropertyPlaceholderConfigurer会在创建任何实例之前将占位符变为实际值

PropertyPlaceholderConfigurer除了检查命名空间指定的文件外,还可以指定在文件没有匹配值的情况下是否查看系统属性,通过设置systemPropertiesMode属性来控制该行为,该属性有三种可选值:

1、never(0):不查询系统属性

2、fallback(1):在文件没有匹配值的情况下查找系统属性,这是默认值

3、override(2):首先检查系统属性,然后检查文件指定的值,此时系统属性优先级比文件指定的值高

PropertyPlaceholderConfigurer也可以用来替换类名

例子:PropertyOverrideConfigurer

不同于通过占位符工作的PropertyPlaceholderConfigurer,PropertyOverrideConfigurer是通过覆盖机制工作的,如果配置文件没有对应bean的属性的值,则使用配置(注解、xml、java配置)中指定的值,否则覆盖配置的值,如果有多个PropertyPlaceholderConfigurer实例,基于覆盖机制,最后进行匹配的PropertyPlaceholderConfigurer将获得胜利,PropertyPlaceholderConfigurer的属性文件中的配置格式如下:

beanName.property=value

复合属性名也可以支持,但是属性的每个组成部分(除了最后一部分)都必须非空,例如:

foo.fred.bob.sammy=123

foo、fred、bob都不允许为空,使用PropertyOverrideConfigurer,在xml文件的配置如下:

<bean id="propertyConfigure" class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
     <property name="locations" value="classpath:Bean/propertytwo/person.properties">
        
     </property>
  </bean>

使用FactoryBean定制实例化逻辑

该接口是一个泛型接口:

org.springframework.beans.factory.FactoryBean接口提供了三种方法:

1、Object getObject():返回该工厂生产的产品

2、boolean isSingleton():判断工厂生产的产品是否为单例(文档说有,但实测没有)

3、Class getObjectType():返回工厂生产的产品的类型

假设有一个实现了org.springframework.beans.factory.FactoryBean接口的类myBean,当我们调用getBean("myBean")方法时,返回的是getObject()返回的对象,如果调用getBean("&myBean"),则会返回myBean

猜你喜欢

转载自blog.csdn.net/dhaiuda/article/details/81949249
今日推荐