Spring-IOC Bean实例化基本流程 | 青训营

113 阅读9分钟

Bean 实例化的基本流程

Spring容器在进行初始化时,会将xml配置的<bean>的信息封装成一个BeanDefinition对象,所有的BeanDefinition存储到一个名为beanDefinitionMapMap集合中去,Spring框架在对该Map进行遍历,使用反射创建Bean实例对象,创建好的Bean对象存储在一个名为singletonObjectsMap集合中,当调用getBean方法时则最终从该Map集合中取出Bean实例对象返回。

Bean信息定义对象-BeanDefinition

<bean id="" 
    class="" 
    name="" 
    lazyinit="" 
    scope="" 
    init-method="" 
    destroy-method="" 
    factory-bean="" 
    factory-method="" 
    abstract="" 
    depends-on="" 
    parent="">
    <property name="" ref=""/>
    <property name="" ref=""/>
    <property name="" value=""/>
</bean>
public interface BeanDefinition {
	String SCOPE_SINGLETON = "singleton";
	String SCOPE_PROTOTYPE = "prototype";
	void setBeanClassName(@Nullable String var1);
	String getBeanClassName();
	void setScope(@Nullable String var1);
	String getScope();
	void setLazyInit(boolean var1);
	boolean isLazyInit();
	void setFactoryBeanName(@Nullable String var1);
	String getFactoryBeanName();
	void setFactoryMethodName(@Nullable String var1);
	String getFactoryMethodName();
	void setInitMethodName(@Nullable String var1);
	String getInitMethodName();
//..... 省略部分属性和方法
}

DefaultListableBeanFactory对象内部维护着一个Map用于存储封装好的BeanDefinitionMap

public class DefaultListableBeanFactory extends ... implements ... {
	//存储<bean>标签对应的BeanDefinition对象
	//key:是Bean的beanName,value:是Bean定义对象BeanDefinition
	private final Map<String, BeanDefinition> beanDefinitionMap;
}

Spring框架会取出beanDefinitionMap中的每个BeanDefinition信息,反射构造方法或调用指定的工厂方法生成Bean实例对象,所以只要将BeanDefinition注册到beanDefinitionMap这个Map中,Spring就会进行对应的Bean的实例化操作

Bean实例及单例池singletonObjectsbeanDefinitionMap中的BeanDefinition会被转化成对应的Bean实例对象 ,存储到单例池singletonObjects中去,在DefaultListableBeanFactory的上四级父类 DefaultSingletonBeanRegistry中,维护着singletonObjects,源码如下:

public class DefaultSingletonBeanRegistry extends ... implements ... {
	//存储Bean实例的单例池
	////key:是Bean的beanName,value:是Bean的实例对象
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
}
  1. 加载xml,解析获取配置中的每个<bean>的信息,封装成一个个的BeanDefinition对象;
  2. BeanDefinition存储在一个名为beanDefinitionMapMap<String,BeanDefinition>中;
  3. ApplicationContext底层遍历beanDefinitionMap,创建Bean实例对象;
  4. 创建好的Bean实例对象,被存储到一个名为singletonObjectsMap<String,Object>中;
  5. 当执行applicationContext.getBean(beanName)时,从singletonObjects去匹配Bean实例返回。

image.png

Spring的后处理器

Spring的后处理器是Spring对外开发的重要扩展点,允许我们介入到Bean的整个实例化流程中来,以达到动态注册BeanDefinition,动态修改BeanDefinition,以及动态修改Bean的作用。Spring主要有两种后处理器:

BeanFactoryPostProcessor

Bean工厂后处理器,在BeanDefinitionMap填充完毕,Bean实例化之前执行;

BeanPostProcessor

Bean后处理器,一般在Bean实例化之后,填充到单例池singletonObjects之前执行。

BeanFactoryPostProcessor

BeanFactoryPostProcessor是一个接口规范,实现了该接口的类只要交由Spring容器管理的话,那么Spring就会回调该接口的方法,用于对BeanDefinition注册和修改的功能。

public interface BeanFactoryPostProcessor {
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory);
}

编写BeanFactoryPostProcessor

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) 
	throws BeansException{
		System.out.println("MyBeanFactoryPostProcessor执行了...");
	}
}

配置BeanFactoryPostProcessor

<bean class="com.itheima.processor.MyBeanFactoryPostProcessor"/>

BeanDefinition修改操作

postProcessBeanFactory参数本质就是 DefaultListableBeanFactory,拿到BeanFactory的引用,自然就可以对beanDefinitionMap中的BeanDefinition进行操作了 ,例如对UserDaoImplBeanDefinition进行修改操作

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) 
			throws BeansException {
		BeanDefinition userDaoBD = beanFactory.getBeanDefinition(“userDao”);//获得UserDao定义对象
		userDaoBD.setBeanClassName("com.itheima.dao.impl.UserDaoImpl2"); //修改class
		//userDaoBD.setInitMethodName(methodName); //修改初始化方法
		//userDaoBD.setLazyInit(true); //修改是否懒加载
		//... 省略其他的设置方式 ...
	}
}

BeanDefinition注册操作

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

	@Override
	public void postProcessBeanFactory
	(ConfigurableListableBeanFactory configurableListableBeanFactory) 
	throws BeansException {

		//强转成子类DefaultListableBeanFactory
		if(configurableListableBeanFactory instanceof DefaultListableBeanFactory){
			DefaultListableBeanFactory beanFactory = 
				(DefaultListableBeanFactory)configurableListableBeanFactory;
			BeanDefinition beanDefinition = new RootBeanDefinition();
			beanDefinition.setBeanClassName("com.itheima.dao.UserDaoImpl2");
			
			//进行注册操作
			beanFactory.registerBeanDefinition("userDao2",beanDefinition);
			}
	}
}

BeanDefinitionRegistryPostProcessor 注册

Spring提供了一个BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor专门用于注册BeanDefinition操作

public class MyBeanFactoryPostProcessor2 implements BeanDefinitionRegistryPostProcessor {

	@Override
	public void postProcessBeanFactory
	(ConfigurableListableBeanFactory configurableListableBeanFactory) 
	throws BeansException {}

	@Override
	public void postProcessBeanDefinitionRegistry
	(BeanDefinitionRegistry beanDefinitionRegistry) 
	throws BeansException {
		BeanDefinition beanDefinition = new RootBeanDefinition();
		beanDefinition.setBeanClassName("com.itheima.dao.UserDaoImpl2");
		beanDefinitionRegistry.registerBeanDefinition("userDao2",beanDefinition);
	}
}

使用SpringBeanFactoryPostProcessor扩展点完成自定义注解扫描

自定义@MyComponent注解,使用在类上;

使用资料中提供好的包扫描器工具BaseClassScanUtils完成指定包的类扫描;

自定义BeanFactoryPostProcessor完成注解@MyComponent的解析,解析后最终被Spring管理。

image.png

public class BaseClassScanUtils {

    //设置资源规则
    private static final String RESOURCE_PATTERN = "/**/*.class";

    public static Map<String, Class> scanMyComponentAnnotation(String basePackage) {

        //创建容器存储使用了指定注解的Bean字节码对象
        Map<String, Class> annotationClassMap = new HashMap<String, Class>();

        //spring工具类,可以获取指定路径下的全部类
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

        try {
            String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                    ClassUtils.convertClassNameToResourcePath(basePackage) + RESOURCE_PATTERN;
            Resource[] resources = resourcePatternResolver.getResources(pattern);
            //MetadataReader 的工厂类
            MetadataReaderFactory refractory = new CachingMetadataReaderFactory(resourcePatternResolver);
            for (Resource resource : resources) {
                //用于读取类信息
                MetadataReader reader = refractory.getMetadataReader(resource);
                //扫描到的class
                String classname = reader.getClassMetadata().getClassName();
                Class<?> clazz = Class.forName(classname);
                //判断是否属于指定的注解类型
                if(clazz.isAnnotationPresent(MyComponent.class)){
                    //获得注解对象
                    MyComponent annotation = clazz.getAnnotation(MyComponent.class);
                    //获得属value属性值
                    String beanName = annotation.value();
                    //判断是否为""
                    if(beanName!=null&&!beanName.equals("")){
                        //存储到Map中去
                        annotationClassMap.put(beanName,clazz);
                        continue;
                    }

                    //如果没有为"",那就把当前类的类名作为beanName
                    annotationClassMap.put(clazz.getSimpleName(),clazz);

                }
            }
        } catch (Exception exception) {
        }

        return annotationClassMap;
    }
}
public class MyComponentBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        //通过扫描工具去扫描指定包及其子包下的所有类,收集使用@MyComponents的注解的类
        Map<String, Class> map = BaseClassScanUtils.scanMyComponentAnnotation("com.chen");
        map.forEach((beanName, clazz) -> {
            String beanClassName = clazz.getName();
            BeanDefinition beanDefinition = new RootBeanDefinition();
            beanDefinition.setBeanClassName(beanClassName);
            beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition);

        });
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }
}

BeanPostProcessor

Bean被实例化后,到最终缓存到名为singletonObjects单例池之前,中间会经过Bean的初始化过程,例如:属性的填充、初始方法init的执行等,其中有一个对外进行扩展的点BeanPostProcessor,我们称为Bean后处理。跟上面的Bean工厂后处理器相似,它也是一个接口,实现了该接口并被容器管理的BeanPostProcessor,会在流程节点上被Spring自动调用。

配置MyBeanPostProcessor

<bean class="com.itheima.processors.MyBeanPostProcessor"></bean>

测试台打印结果

userDao实例化
userDao:postProcessBeforeInitialization
属性设置之后执行:afterPropertiesSet
init初始化方法执行
userDao:postProcessAfterInitialization

对Bean方法进行执行时间日志增强

编写BeanPostProcessor,增强逻辑编写在 after方法中

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
	//对Bean进行动态代理,返回的是Proxy代理对象
	Object proxyBean = Proxy.newProxyInstance(
		bean.getClass().getClassLoader(),
		bean.getClass().getInterfaces(),
			new InvocationHandler() {
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //输出开始时间
        System.out.println("method:" + method.getName() + " start time:" + new Date());
        //执行目标方法
        Object result = method.invoke(bean, args);
        //输出结束时间
        System.out.println("method:" + method.getName() + " end time" + new Date());
        return result;
        }
      }
	});
	//返回代理对象
	return proxyBean;
}

Bean后处理器 – BeanPostProcessor

image.png

Spring Bean的生命周期

Spring Bean的生命周期是从 Bean实例化之后,即通过反射创建出对象之后,到Bean成为一个完整对象,最终存储到单例池中,这个过程被称为Spring Bean的生命周期。Spring Bean的生命周期大体上分为三个阶段:

Bean的实例化阶段:Spring框架会取出BeanDefinition的信息进行判断当前Bean的范围是否是singleton的,是否不是延迟加载的,是否不是FactoryBean等,最终将一个普通的singletonBean通过反射进行实例化;

Bean的初始化阶段:Bean创建之后还仅仅是个"半成品",还需要对Bean实例的属性进行填充、执行一些Aware接口方法、执行BeanPostProcessor方法、执行InitializingBean接口的初始化方法、执行自定义初始化init方法等。该阶段是Spring最具技术含量和复杂度的阶段,Aop增强功能,后面要学习的Spring的注解功能等、spring高频面试题Bean的循环引用问题都是在这个阶段体现的;

Bean的完成阶段:经过初始化阶段,Bean就成为了一个完整的Spring Bean,被存储到单例池 singletonObjects中去了,即完成了Spring Bean的整个生命周期。

Bean的初始化阶段的步骤比较复杂,所以着重研究Bean的初始化阶段

Spring Bean的初始化过程涉及如下几个过程: Bean实例的属性填充 Aware接口属性注入 BeanPostProcessorbefore()方法回调 InitializingBean接口的初始化方法回调 自定义初始化方法init回调 BeanPostProcessorafter()方法回调

Bean实例属性填充

BeanDefinition 中有对当前Bean实体的注入信息通过属性propertyValues进行了存储,例如UserService的属性信息如下

image.png

Spring在进行属性注入时,会分为如下几种情况:

注入普通属性,Stringint或存储基本类型的集合时,直接通过set方法的反射设置进去;

注入单向对象引用属性时,从容器中getBean获取后通过set方法反射设置进去,如果容器中没有,则先创建被注入对象Bean实例(完成整个生命周期)后,在进行注入操作;

注入双向对象引用属性时,就比较复杂了,涉及了循环引用(循环依赖)问题,下面会详细阐述解决方案。

循环引用

public class UserServiceImpl implements UserService{
	public void setUserDao(UserDao userDao) {}
}
public class UserDaoImpl implements UserDao{
	public void setUserService(UserService userService){}
}
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
	<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
	<property name="userService" ref="userService"/>
</bean>

Spring提供了三级缓存存储 完整Bean实例 和 半成品Bean实例 ,用于解决循环引用问题 在DefaultListableBeanFactory的上四级父类DefaultSingletonBeanRegistry中提供如下三个Map

public class DefaultSingletonBeanRegistry ... {
	//1、最终存储单例Bean成品的容器,即实例化和初始化都完成的Bean,称之为"一级缓存"
	Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
	//2、早期Bean单例池,缓存半成品对象,且当前对象已经被其他对象引用了,称之为"二级缓存"
	Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
	//3、单例Bean的工厂池,缓存半成品对象,对象未被引用,使用时在通过工厂创建Bean,称之为"三级缓存"
	Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
}

UserServiceUserDao循环依赖的过程结合上述三级缓存描述一下

UserService 实例化对象,但尚未初始化,将UserService存储到三级缓存;

UserService 属性注入,需要UserDao,从缓存中获取,没有UserDao

UserDao实例化对象,但尚未初始化,将UserDao存储到到三级缓存;

UserDao属性注入,需要UserService,从三级缓存获取UserServiceUserService从三级缓存移入二级缓存;

UserDao执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存;

UserService 注入UserDao

UserService执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存。

常用的Aware接口

Aware接口是一种框架辅助属性注入的一种思想,其他框架中也可以看到类似的接口。框架具备高度封装性,我们接触到的一般都是业务代码,一个底层功能API不能轻易的获取到,但是这不意味着永远用不到这些对象,如果用到了,就可以使用框架提供的类似Aware的接口,让框架给我们注入该对象。

Aware接口回调方法作用
ServletContextAwaresetServletContext(ServletContext context)Spring框架回调方法注入ServletContext对象, web环境下才生效
BeanFactoryAwaresetBeanFactory(BeanFactory factory)Spring框架回调方法注入beanFactory对象
BeanNameAwaresetBeanName(String beanName)Spring框架回调方法注入当前Bean在容器中 的beanName
ApplicationContextAwaresetApplicationContext(ApplicationContext applicationContext)Spring框架回调方法注入applicationContext 对象

image.png

image.png