spring---InitializingBean接口源码分析

作用:

实现了InitializingBean接口的类,可以在该类被注入到spring容器时达到 某些属性先装配完成后,再去装配另一些属性 的能力。Initializ ingBean接口只包含一个方法afterPropertiesSet(),凡是继承了InitializingBean接口的类,在初始化时都会调用这方法。而initMethod和@PostConstruct也可以达到相同的目的。

上文是一种用法,但思维不要局限。比如说我们的一个类里有一个属性,但是该属性不支持Spring注入,只能通过Build或者new的方式创建,而我们又想在spring装配Bean的时候一起将该属性注入进来,那使用InitializingBean、initMethod或@PostConstruct再合适不过了。

关键信息都隐藏在源码中看下源码中的大致流程:

AbstractAutowireCapableBeanFactory中的doCreateBean方法:
到这一步bean开始初始化,接着往下看,InitializingBean接口中的方法是在那个阶段被调用的。包括与initMethod执行的先后顺序源码中都有清晰的体现。

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			// 给我们的属性进行赋值(用set方法进行)
			// 这一步也是非常关键的,这一步负责属性装配,因为前面的实例只是实例化了,并没有设值,这里就是设值
			populateBean(beanName, mbd, instanceWrapper);
			// 进行对象初始化操作这里可能生成代理对象
			// 还记得 init-method 吗?还有 InitializingBean 接口?还有 BeanPostProcessor 接口?
			// 这里就是处理 bean 初始化完成后的各种回调
			// 执行后置处理器,AOP就是在这里完成的
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
				throw (BeanCreationException) ex;
			}
			else {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
			}
		}

这里我们需要注意下populateBean(beanName, mbd, instanceWrapper);方法,这一步是给bean属性注入的过程。

initializeBean():

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareMethods(beanName, bean);
				return null;
			}, getAccessControlContext());
		}
		else {
			// 如果 bean 实现了 BeanNameAware、BeanClassLoaderAware 或 BeanFactoryAware 接口,回调
			invokeAwareMethods(beanName, bean);
		}

		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
			// 调用我们bean的后置处理器的PostProcessorsAfterInitialization方法
			// BeanPostProcessor 的 postProcessBeforeInitialization 回调
			// 第七次调用后置处理器,处理AOP
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
			// BeanPostProcessor 的 postProcessBeforeInitialization 回调(@postConstruct)
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					(mbd != null ? mbd.getResourceDescription() : null),
					beanName, "Invocation of init method failed", ex);
		}
		if (mbd == null || !mbd.isSynthetic()) {
			// BeanPostProcessor 的 postProcessAfterInitialization 回调,执行后置处理器的after方法
			// 第八次
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

		return wrappedBean;
	}

Spring 在完成实例化后,设置完所有属性,进行 “Aware 接口” 和 “BeanPostProcessor 前置处理”之后,会接着检测当前 bean 对象是否实现了 InitializingBean 接口,如果是,则会调用其 afterPropertiesSet() 进一步调整 bean 实例对象的状态。invokeInitMethods(beanName, wrappedBean, mbd);就是InitializingBean接口的关键所在,我们清楚的看到在bean属性注入后,又调用了invokeInitMethods方法,正式在这个方法中对当前bean是否实现InitializingBean接口进行了判断。继续往下看

invokeInitMethods()

protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
			throws Throwable {

		boolean isInitializingBean = (bean instanceof InitializingBean);
		if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
			if (logger.isTraceEnabled()) {
				logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
			}
			if (System.getSecurityManager() != null) {
				try {
					AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
						((InitializingBean) bean).afterPropertiesSet();
						return null;
					}, getAccessControlContext());
				}
				catch (PrivilegedActionException pae) {
					throw pae.getException();
				}
			}
			else {
				((InitializingBean) bean).afterPropertiesSet();
			}
		}

		if (mbd != null && bean.getClass() != NullBean.class) {
			String initMethodName = mbd.getInitMethodName();
			 // 判断是否指定了 init-method(),
            // 如果指定了 init-method(),则再调用制定的init-method
			if (StringUtils.hasLength(initMethodName) &&
					!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
					!mbd.isExternallyManagedInitMethod(initMethodName)) {
					// 利用反射机制执行
				invokeCustomInitMethod(beanName, bean, mbd);
			}
		}
	}

boolean isInitializingBean = (bean instanceof InitializingBean);这里对是否实现接口进行了判断
通过一系列判断后((InitializingBean) bean).afterPropertiesSet();调用了我们自定义的afterPropertiesSet方法。至此一套完整的流程就基本走完了,这里还涉及到一个老生常谈的问题,init-method与isInitializingBean谁优先执行。上面源码中已经看的很清楚,invoke Custom Init Method发生在isInitializingBean之后。

invokeCustomInitMethod:

protected void invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd)
			throws Throwable {

		String initMethodName = mbd.getInitMethodName();
		Assert.state(initMethodName != null, "No init method set");
		Method initMethod = (mbd.isNonPublicAccessAllowed() ?
				BeanUtils.findMethod(bean.getClass(), initMethodName) :
				ClassUtils.getMethodIfAvailable(bean.getClass(), initMethodName));

		if (initMethod == null) {
			if (mbd.isEnforceInitMethod()) {
				throw new BeanDefinitionValidationException("Could not find an init method named '" +
						initMethodName + "' on bean with name '" + beanName + "'");
			}
			else {
				if (logger.isTraceEnabled()) {
					logger.trace("No default init method named '" + initMethodName +
							"' found on bean with name '" + beanName + "'");
				}
				// Ignore non-existent default lifecycle methods.
				return;
			}
		}

		if (logger.isTraceEnabled()) {
			logger.trace("Invoking init method  '" + initMethodName + "' on bean with name '" + beanName + "'");
		}
		Method methodToInvoke = ClassUtils.getInterfaceMethodIfPossible(initMethod);

		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				ReflectionUtils.makeAccessible(methodToInvoke);
				return null;
			});
			try {
				AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () ->
						methodToInvoke.invoke(bean), getAccessControlContext());
			}
			catch (PrivilegedActionException pae) {
				InvocationTargetException ex = (InvocationTargetException) pae.getException();
				throw ex.getTargetException();
			}
		}
		else {
			try {
				ReflectionUtils.makeAccessible(methodToInvoke);
				methodToInvoke.invoke(bean);
			}
			catch (InvocationTargetException ex) {
				throw ex.getTargetException();
			}
		}
	}
  • 实现InitializingBean接口是直接调用afterPropertiesSet方法,比通过反射调用init-method指定的方法效率相对来说要高点。但是init-method方式消除了对spring的依赖
  • 如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法。

@PostConstruct注解好多人以为是Spring提供的。其实是Java自己的注解。Java中该注解的说明:@PostConstruct该注解被用来修饰一个非静态的void()方法。被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。通常我们会是在Spring框架中使用到@PostConstruct注解 该注解的方法在整个Bean初始化中的执行顺序:Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)

@PostConstruct单独开一篇学习下

发布了29 篇原创文章 · 获赞 11 · 访问量 1852

猜你喜欢

转载自blog.csdn.net/chihaihai/article/details/104737829