文章目录

  • 1 概述
  • 1.1 容器启动阶段
  • 1.2 实例化阶段
  • 1.3 容器扩展机制
  • 1.4 常用的BeanFactoryPostProcessor
  • 1.4.1 PropertyPlaceholderConfigurer
  • 1.4.2 PropertyOverrideConfigurer
  • 1.4.3 CustomEditorConfigurer
  • 1.5 bean的流程
  • 1.5.1 BeanWrapper
  • 1.5.2 Aware接口
  • 1.5.3 BeanPostProcessor
  • 1.5.4 BeanPostProcessor 自定义注解
  • 1.5.4 InitializingBean和init-method
  • 1.5.5 DisposableBean与destory-method


1 概述

spring 容器实现的功能,可以分为两个阶段,即容器启动阶段和bean实例化阶段。

spring 容器bean冲突 spring容器bean的注入过程_实例化

1.1 容器启动阶段

首先会通过某种途径加载相关配置并进行解析和分析,并将分析后的信息编组为相应的BeanDefinition**,并把这些BeanDefinition注册到相应的BeanDefinitionRegistry。**

spring 容器bean冲突 spring容器bean的注入过程_java_02

1.2 实例化阶段

在所有bean信息都通过BeanDefinition的方式注册BeanDefinitionRegistry中时,当请求通过容器的getBean方法明确了请求对象时(getbean调用)将会触发第二阶段。
首先容器会检查所请求的对象之前是否已经初始化。如果没有则会根据注册的BeanDefinition所提供的信息实例化被请求对象,并注入依赖

1.3 容器扩展机制

spring 提供了 一种为 BeanFactoryPostProcessor容器扩展机制,该机制允许我们在实例化前,**对注册到BeanDefinition所保存的信息进行修改。**而对于ApplicationContext,其会自动识别配置文件中的BeanFactoryPostProcessor并应用.

xml 中的配置使用

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations"> 
		<!--获取配置文件位置-->
            <list>
                <value>conf/mysql.properties</value>
            </list>
        </property>
    </bean>

1.4 常用的BeanFactoryPostProcessor

1.4.1 PropertyPlaceholderConfigurer

PropertyPlaceholderConfigurer 允许我们在xml配置文件中使用占位符,并将这些占位符所代表的资源单独配置到简单的properties文件中加载。

properties配置文件

jdbc.url=jdbc:mysql://127.0.0.1:3306/jdbc
jdbc.driver=com.mysql.jdbc.Driver
jdbc.username=root
jdbc.password=yourPassword

xml配置文件

<!--加载PropertyPlaceholderConfigurer-->
 <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <!--获取配置文件位置-->
            <list>
                <value>conf/mysql.properties</value>
            </list>
        </property>
    </bean>
   <!--使用占位符进行替换-->
  <bean  id ="tm" class="org.example.Day01.TestMysql">
      <property name="driver">
          <value> ${jdbc.driver}</value>
      </property>
      <property name="password">
          <value> ${jdbc.password}</value>
      </property>
      <property name="url">
          <value>${jdbc.url}</value>
      </property>
      <property name="username">
          <value>${jdbc.username}</value>
      </property>
  </bean>

当BeanFactory在第一阶段加载完成的所有配置信息时,BeanFactory中保存的属性信息还只是以占位符的形式存在如${jdbc.url},当PropertyPlaceholderConfigurer作为BeanFactoryPostProcessor应用时,它会使用properties配置文件中的配置信息来替换相应的BeanDefination中占位符所表示的属性值。

1.4.2 PropertyOverrideConfigurer

PropertyOverrideConfigurer的配置项,会覆盖掉原来xml中的bean定义的property信息.
properties中的格式beanName.propertyName=value,其中的xml配置中bean定义的是以beanName(id的值)开头,后面对应着被覆盖的值。

<!--引入PropertyOverrideConfigurer-->
   <bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
        <property name="location">
            <value>conf/test.properties</value>
        </property>
    </bean>
    <!--进行加载-->
    <bean id ="t2" class="org.example.Day01.Test02">
        <property name="name" value="1"></property>
    </bean>

properties 配置

t2.name=EFG@AB

最后加载的效果不是1 而是efg@ab

对配置文件进行加密的方法

这两者的父类propertyResourceConfigure提供了一个方法convertProperty(String,String)的方法,可以进行覆盖并重写。因此我们可以在配置文件中加密,解析时进行解密。

public class Test01 extends PropertyOverrideConfigurer {

    //重点在于实现此方法
    @Override
    public String convertProperty(String propertyName,String originalValue) {
         if(isEncryptPropertyVal(propertyName)){
             //进行解密
             return  MD5Test.convertMD5(originalValue);
         }
        return originalValue;
    }


    public boolean isEncryptPropertyVal(String value){
        //判断是否满足解密条件
         if(value.startsWith("t2")){
             return  true;
         }else{
             return  false;
         }
    }
}
1.4.3 CustomEditorConfigurer

customeditorConfigurer是将后期使用到的数据注册到容器中,其中在xml中都是读取的字符串形式,最终的应用类型确实对象的各种类型,因此需要通过propertyEditor进行转换。而CustomEditorConfigure是帮助我们传达类似的信息。重写propertyEditor如果单向的从string 到相应的类型转换只需实现setAsTest,支持双向的则需要同时重写getAsTest

1.5 bean的流程

在借助BeanFactoryPostProcessor第一阶段执行后,后面就是第二阶段bean实例化的实现逻辑。
在第一阶段,容器仅仅拥有对象的beandefinition来保存实例化阶段将要用的必要信息。当通过getbean方法内部发现该bean定义前还没有被实例化之后,会通过createBean()方法来进行具体的对象实例化

spring 容器bean冲突 spring容器bean的注入过程_bc_03

容器只要根据相应bean定义的BeanDefintion取得实例化信息,结合CglibSubclassingInstantiationStrategy以及不同的bean定义类型,就可以返回实例化完成的对象实例,但是所返回的是以Beanwrapper对构造完成对象实例进行包装,返回相应的beanwrapper实例。、

1.5.1 BeanWrapper

其作用是对某个bean进行包裹,然后对这个包裹的bean进行操作,进行设置或者获取bean的相应属性值。
beanwrapper继承了PropertyAccessor接口,可以统一的方式对对象属性进行访问,同时又继承了propertyEditorRegistry,当BeanWrapper转换类型,设置对象属性值时,就不会无从下手。

ect  per;
    Object dog;
   per = Class.forName("org.example.Person").getDeclaredConstructor().newInstance();
            dog = Class.forName("org.example.Dog").getDeclaredConstructor().newInstance();
            BeanWrapper bw = new BeanWrapperImpl(per);
            bw.setPropertyValue("dog",dog);
            //断言错误会抛出异常
            assertSame(dog,bw.getPropertyValue("dog"));

使用了beanwrapper就不需要在使用反射进行注入属性了。

1.5.2 Aware接口

当对象实例化完成,并且相关属性以及依赖设置完成后,spring容器会检测到当前对象实例是否实现了Aware命名结尾的接口定义,如果有则将这些Aware接口定义中规定的依赖注入到当前实例。

  • BeannameAware
    如果spring容器检测到当前对象实例实现了接口,会将该对象实例的bean定义对应的beanName设置到当前对象实例。
  • BeanClassLoaderAware
    如果容器检测到当前对象实例实现了该接口,会将对应加载当前bean的ClassLoader注入当前对象实例
  • BeanFactoryAware
    如果对象声明了beanFactoryAware,BeanFactory 会将自身设置到当前容器。

对于ApplicationContext容器还会检查一下的几种Aware

  • ResourceLoaderAware
    当容器检测到时,会将当前applicationContext 自身设置到对象实例
  • ApplicationEventPublisherAware
    ApplicaotionContext还实现了ApplicationEventPublisher接口,当其检测到此Aware时也会将自身注入到当前对象
  • MessageSourceAware
    ApplicationContext 同时也实现了国际化信息支持,所以当检测到时,也会将其自身注入
  • ApplicationContextAware
    会将其容器自身注入对象实例
1.5.3 BeanPostProcessor

** BeanPostProcessor存在于对象实例化阶段,而BeanFactoryPostProcessor则是存在于容器启动阶段**
BeanFactoryPostProcessor通常会处理所有符合条件的BeanDefinition,而BeanPostProcessor会处理容器内所有符合条件的实例化对象

public interface BeanPostProcessor {
//前置处理
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
	}
	//后置处理
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
  • postProcessBeforeInitialization()
    在每一个bean对象的初始化方法调用之前回调
  • postProcessAfterInitialization()
    会在每个bean对象的初始化方法调用之后被回调。

常用于处理标记实现类或者为当前对象提供代理实现

当ApplicationContext中的每个对象的实例化走到BeanPostPeocessor前置处理时,ApplicationContext容器会检测到之前注册到容器的ApplicationContextAwareProcessor这个BeanPostProcessor实现类然后调用其前置方法为其设置Aware相关依赖。

AccessControlContext acc = null;

		if (System.getSecurityManager() != null &&
				(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
						bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
						bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
			acc = this.applicationContext.getBeanFactory().getAccessControlContext();
		}

		if (acc != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareInterfaces(bean);
				return null;
			}, acc);
		}
		else {
			invokeAwareInterfaces(bean);
		}

		return bean;
	}

	private void invokeAwareInterfaces(Object bean) {
		if (bean instanceof Aware) {
			if (bean instanceof EnvironmentAware) {
				((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
			}
			if (bean instanceof EmbeddedValueResolverAware) {
				((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
			}
			if (bean instanceof ResourceLoaderAware) {
				((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
			}
			if (bean instanceof ApplicationEventPublisherAware) {
				((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
			}
			if (bean instanceof MessageSourceAware) {
				((MessageSourceAware) bean).setMessageSource(this.applicationContext);
			}
			if (bean instanceof ApplicationContextAware) {
				((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
			}
		}
1.5.4 BeanPostProcessor 自定义注解
  • 定义注解
  • 在BeanPostProcessor中使用反射处理注解
  • 将BeanPostProcessor 注入到容器中

定义注解

/**
 * @author wfg
 */
//定义生命周期为运行期
@Retention(RetentionPolicy.RUNTIME)
//定义作用域 为成员变量
@Target({ElementType.FIELD})
@Documented
public @interface wfgValue {
    String value() default "";
}

自定义BeanPostProcess

public class WfgBeanPostProcess implements BeanPostProcessor{
    //在调用构造方法前执行
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Field[] fields = bean.getClass().getDeclaredFields();
        for(Field f:fields){
            wfgValue  wv= f.getAnnotation(wfgValue.class);
             if(wv==null){
                continue;
             }
             f.setAccessible(true);
            try {
                f.set(bean,wv.value());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

        }
        return bean;
    }

    //在调用构造方法后执行
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }


}

将其注入到spring容器中

<bean id="wfgBpp" class="org.example.Day02.WfgBeanPostProcess"/>
1.5.4 InitializingBean和init-method

initializingBean是一个对象的生命周期接口。

public interface InitializingBean {
   void afterPropertiesSet() throws Exception;
}

在调用后BeanPostProcessor前置处理后,会使用inITializingBean中的方法进一步调整对象的状态(对象执行前的初始化)

init-method是xml配置中提供的另外一种初始化方式,没有很强的侵略性。

1.5.5 DisposableBean与destory-method

当调用完成后,容器会检查singleton类型的bean实例,看其是否实现其接口或者对应的bean定义是否通过的destory-method属性指定了自定义的销毁方法,若果是则注册一个用于对象销毁的回调。但spring容器在关闭前不会自动调用这些方法,因此需要我们进行通知。
registerShutdownHook方法注册并触发对象销毁逻辑的调用。
xmlbeeans 配置

default-destroy-method="destory1"

手动调用

AbstractApplicationContext  ac= new ClassPathXmlApplicationContext("applicationContext.xml");
       Dog dog = (Dog) ac.getBean("dog");
      //registerShutdownHook方法注册并处发对象销毁逻辑回调
       ac.registerShutdownHook();
       System.out.println(dog.getName());