Spring 相信 Java 程序员都很熟悉,甚至于有人说 Java 开发就是面向 Spring 开发。由此可见,Spring 在 Java 领域的地位是举足轻重的。
Spring 有很多模块,常用的有 spring-core、spring-beans、spring-aop、spring-context、spring-expression 以及 spring-test 等,模块太多,不可能一次聊完。Bean 的概念在 Spring 中是非常重要的。这篇狗哥先聊聊 Bean 相关的内容。
面试中常问 Bean 的注册方式、作用域、同名 Bean、Bean 的生命周期等等问题。
Bean 的注册方式
Spring 中 Bean 的注册方式有三种:
- XML 配置文件的注册方式
- Java 注解的注册方式
- Java API 的注册方式
XML 方式
这种方式已经不常用了,原因是维护过于繁琐。
<bean id="user" class="com.nasus.spring.beans.User"> <property name="id" value="1"/> <property name="name" value="Spring"/> </bean>
如上面代码所示,只需要指定注入的类以及类下定义的属性即可。
注解方式
用 Java 注解方式现在很常见,基本都是这种方式。注解又分为两种方式:@Component 和 @Bean 方式。
@Component 方式注册 Bean,代码如下:
@Component public class User { private Integer id; private String name // 忽略其他方法 }
@Bean 方式注册 Bean,常与 @Configuration 结合使用。**@Configuration 可理解为 XML 配置里的标签,而 @Bean 可理解为用 XML 配置里面的标签。** 代码如下:
@Configuration public class User { @Bean public User user() { return new User(); } // 忽略其他方法 }
你说到这里面试官肯定会问 @Component 和 @Bean 二者有啥区别。区别就在于:「如果想将第三方的类变成组件,没有源代码,也就没办法使用 @Component 进行自动配置,这时就可以使用 @Bean (当然,也可以用 XML 方式)。」 比如下面的代码:
@Configuration public class WireThirdLibClass { // 假设 ThirdLibClass 是第三方库中的类,我们没源码 @Bean public ThirdLibClass getThirdLibClass() { return new ThirdLibClass(); } }
API 方式
这种方式用的很少,实现容易出错,代码写起来也繁琐,增加了维护的时间成本。代码如下:
public class CustomBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {} @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { RootBeanDefinition personBean = new RootBeanDefinition(User.class); // 新增 Bean registry.registerBeanDefinition("user", userBean); } }
Bean 的作用域
一共 5 个:
作用域 | 描述 | 用法 |
singleton | Spring 默认的作用域,单例作用域。表示在 Spring 中只会有一个 Bean 实例 | 默认 |
prototype | 原型作用域,每次调用 Bean 都会新建一个。多线程场景下常用。 | 类上加 @Scope ("prototype") |
request | 该作用域将 bean 的定义限制为 HTTP 请求。只在 web-aware Spring ApplicationContext 的上下文中有效。 | 类上加 @Scope (WebApplicationContext.SCOPE_REQUEST) |
session | 该作用域将 bean 的定义限制为 HTTP 会话。只在 web-aware Spring ApplicationContext 的上下文中有效。 | 类上加 @Scope (WebApplicationContext.SCOPE_SESSION) |
global-session | 该作用域将 bean 的定义限制为全局 HTTP 会话。只在 web-aware Spring ApplicationContext 的上下文中有效。 | 类上加 @Scope (WebApplicationContext.SCOPE_APPLICATION) |
怎么解决同名 Bean 的问题?
Spring 对同名 Bean 的处理分两种情况:
- 同一个 Spring 配置文件中 Bean 的 id 和 name 是不能够重复的,否则 Spring 容器启动时会报错
- 要是不同配置文件,id 和 name 允许重复。Spring 处理规则是后引入的覆盖前面引用的。
所以,我们自己定义的时候尽量使用长命名方式,避免重复。
Bean 的生命周期
我们知道 getBean () 是 Bean 对象的入口,它属于 BeanFactory 接口,而它的真正实现是 AbstractAutowireCapableBeanFactory 的 createBean () 方法,最终调用的是 doCreateBean (),源码如下:
@Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { if (logger.isTraceEnabled()) { logger.trace("Creating instance of bean '" + beanName + "'"); } RootBeanDefinition mbdToUse = mbd; // 确定并加载 Bean 的 class Class < ? > resolvedClass = resolveBeanClass(mbd, beanName); if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) { mbdToUse = new RootBeanDefinition(mbd); mbdToUse.setBeanClass(resolvedClass); } // 验证以及准备需要覆盖的方法 try { mbdToUse.prepareMethodOverrides(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(), beanName, "Validation of method overrides failed", ex); } try { // 给 BeanPostProcessors 一个机会来返回代理对象来代替真正的 Bean 实例,在这里实现创建代理对象功能 Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } } catch (Throwable ex) { throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "BeanPostProcessor before instantiation of bean failed", ex); } try { // 创建 Bean Object beanInstance = doCreateBean(beanName, mbdToUse, args); if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException( mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex); } }
doCreateBean 方法的源码如下:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { // 实例化 bean,BeanWrapper 对象提供了设置和获取属性值的功能 BeanWrapper instanceWrapper = null; // 如果 RootBeanDefinition 是单例,则移除未完成的 FactoryBean 实例的缓存 if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { // 创建 bean 实例 instanceWrapper = createBeanInstance(beanName, mbd, args); } // 获取 BeanWrapper 中封装的 Object 对象,其实就是 bean 对象的实例 final Object bean = instanceWrapper.getWrappedInstance(); // 获取 BeanWrapper 中封装 bean 的 Class Class < ? > beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { mbd.resolvedTargetType = beanType; } // 应用 MergedBeanDefinitionPostProcessor 后处理器,合并 bean 的定义信息 // Autowire 等注解信息就是在这一步完成预解析,并且将注解需要的信息放入缓存 synchronized(mbd.postProcessingLock) { if (!mbd.postProcessed) { try { applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.postProcessed = true; } } boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } // 为了避免循环依赖,在 bean 初始化完成前,就将创建 bean 实例的 ObjectFactory 放入工厂缓存(singletonFactories) addSingletonFactory(beanName, () - > getEarlyBeanReference(beanName, mbd, bean)); } // 对 bean 属性进行填充 Object exposedObject = bean; try { populateBean(beanName, mbd, instanceWrapper); // 调用初始化方法,如 init-method 注入 Aware 对象 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); } } if (earlySingletonExposure) { // 如果存在循环依赖,也就是说该 bean 已经被其他 bean 递归加载过,放入了提早公布的 bean 缓存中 Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { // 如果 exposedObject 没有在 initializeBean 初始化方法中被增强 if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { // 依赖检测 String[] dependentBeans = getDependentBeans(beanName); Set < String > actualDependentBeans = new LinkedHashSet < > (dependentBeans.length); for (String dependentBean: dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } // 如果 actualDependentBeans 不为空,则表示依赖的 bean 并没有被创建完,即存在循环依赖 if (!actualDependentBeans.isEmpty()) { throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); } } } } try { // 注册 DisposableBean 以便在销毁时调用 registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; }
从上述源码中,doCreateBean 方法,首先调用 createBeanInstance 方法 对 Bean 进行了实例化,该方法返回 BeanWrapper 对象。而 BeanWrapper 对象是 Spring 中一个基础的 Bean 结构接口,说它是基础接口是因为它连基本的属性都没有。
BeanWrapper 接口有一个默认实现类 BeanWrapperImpl,其主要作用是对 Bean 进行填充,比如填充和注入 Bean 的属性等。
完成实例化并设置完属性 & 依赖后,调用 Bean 的 initializeBean 初始化方法。初始化第一步是检查当前 Bean 对象是否实现了 BeanNameAware、BeanClassLoaderAware、BeanFactoryAware 等接口,源码如下:
private void invokeAwareMethods(final String beanName, final Object bean) { if (bean instanceof Aware) { // 设置 beanName if (bean instanceof BeanNameAware) { ((BeanNameAware) bean).setBeanName(beanName); } // 注入当前 Bean 对象相应的 ClassLoader if (bean instanceof BeanClassLoaderAware) { ClassLoader bcl = getBeanClassLoader(); if (bcl != null) { ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl); } } // 将 BeanFactory 容器注入到当前对象实例,使当前对象拥有 BeanFactory 容器的引用 if (bean instanceof BeanFactoryAware) { ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this); } } }
第二步是 BeanPostProcessor 增强处理,它主要对 Spring 容器中的 Bean 实例对象进行扩展,允许 Spring 在初始化 Bean 阶段对其进行定制化修改,比如为其提供代理实现等等。
前置处理完事之后,检查和执行 InitializingBean 和 init-method 方法。
其中,InitializingBean 是个接口,里面有 afterPropertiesSet 方法。在 Bean 初始化时会判断 bean 是否实现了 InitializingBean,是则调用 afterPropertiesSet 进行初始化;再检查
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd) throws Throwable { // 判断当前 Bean 是否实现了 InitializingBean,如果是的话需要调用 afterPropertiesSet() 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(); } } // 判断是否指定了 init-method() if (mbd != null && bean.getClass() != NullBean.class) { String initMethodName = mbd.getInitMethodName(); if (StringUtils.hasLength(initMethodName) && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { // 利用反射机制执行指定方法 invokeCustomInitMethod(beanName, bean, mbd); } } }
完成以上工作就可以使用 Bean 对象了,在 Spring 容器关闭时执行销毁方法,但是 Spring 不会自动调用,需要我们主动调用。
「如果是 BeanFactory 容器,我们需要主动调用 destroySingletons () 方法,通知 BeanFactory 容器去执行相应的销毁方法;如果是 ApplicationContext 容器,我们需要主动调用 registerShutdownHook () 方法,告知 ApplicationContext 容器执行相应的销毁方法」。
巨人的肩膀
总结
这章聊了 Bean 的三种注册方式、五个作用域、以及同名问题的解决方法,最后还通过源码把 Bean 的生命周期走了一遍。关于生命周期的验证,之前在《Spring Bean 的生命周期》一文中写过,有兴趣的小伙伴可以看下。它的生命周期流程图如下: