还是举个例子,我有一个朋友小汪他远赴南方某城市打工。然后安定下来后他的朋友很想来家里玩,但是呢我这个朋友家里搞的很乱,所以他不好意思请朋友来家里玩。这时我的另一个朋友说那请一个保姆把家里好好整理一下就可以了,然后给他介绍了一个保姆大S(PS:本文无意指向任何人,因为Spring的前缀是S)然后就把家里整理得井井有条,就请朋友来家里玩了。
好了引入正文,很早很早的Java开发者应该熟悉,最早的时候我们前端访问后端都是需要自己写Servlet的,大概是一个接口写一个Servlet。Java开发又是面向对象的编程,我们程序里面写了new 了很多的对象。写了很多个Servlet,对象很难管理造成我们的程序很乱,都看不下去。后面Spring来了对象都交给了Spring管理,Servlet相关的也都交给了SpringMVC,这样我们开发就顺利多了。好了这下懂我上面举的例子了吧,懂得保姆是什么意思了吧【Spring就像一个管家,一个保姆】。所以多了解Spring相关知识我们提高开发效率有很大的帮助。既然我们的对象交给了Spring管理,那我们的对象怎么生成的呢,就让我们一起看下。
我们在使用Spring的时候,容器中的Bean在我们项目启动的时候都已经给我们生成了,直接使用就行了。容器启动的时候会调用这个方法:
AbstractApplicationContext.refresh()
然后就会调用下面这个方法:
// Instantiate all remaining (non-lazy-init) singletons. // 翻译一下就是 实例化所有非懒加载的Bean finishBeanFactoryInitialization(beanFactory);
如图refresh中的方法,它再次调用的每个方法都很重要,实例化所有单例Bean的方法在这个方法的最后调用
我们写的对象基本都在这个方法内进行实例化。【PS方法只讲一些很重要的,具体的更详细方法调用我会在文章后面的流程图中展示出来。】
DefaultListableBeanFactory.preInstantiateSingletons()。 @Override public void preInstantiateSingletons() throws BeansException { // Iterate over a copy to allow for init methods which in turn register new bean definitions. // While this may not be part of the regular factory bootstrap, it does otherwise work fine. // 获取所有的要实例化的Bean的名称 List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); // Trigger initialization of all non-lazy singleton beans... // 开始初始化单例的Bean for (String beanName : beanNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); // Bean 不是抽象的,是单例的,不是懒加载的进入如下分支 if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { // 如果是FactoryBean进入此分支。本次只聊自己开发写的非FactoryBean // 所以聊else下面的分支。 if (isFactoryBean(beanName)) { // FactoryBean的名称很特别 Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); if (bean instanceof FactoryBean) { final FactoryBean<?> factory = (FactoryBean<?>) bean; boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit, getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { getBean(beanName); } } } else { // 非 FactoryBean进入此分支 getBean(beanName); } } } }
然后会进入如下方法。
AbstractBeanFactory.doGetBean() 的方法。 protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { // 这个方法主要是获取Bean的名称,一些Bean的名称可能命名的比较特别 // 需要进行转换。 final String beanName = transformedBeanName(name); Object bean; // Eagerly check singleton cache for manually registered singletons. // 首先先从容器的缓存中获取Bean,如果容器中已经存在,直接返回。 Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { // Fail if we're already creating this bean instance: // We're assumably within a circular reference. // 先检查这个Bean是否在创建中,如果在创建中抛出异常 if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // 标记Bean为正在创建中。 if (!typeCheckOnly) { markBeanAsCreated(beanName); } try { // Create bean instance. // 如果Bean是单例开始创建Bean . // 后面判断还有Prototype(多例)不是要讲的重点,代码删除了。 if (mbd.isSingleton()) { // 这个方法是Java 8的 lambda 写法,这个方法里面会把创建好的 // Bean放到Spring容器中,后面再获取这个Bean直接从容器中获取了。 sharedInstance = getSingleton(beanName, () -> { try { // 正式开始创建Bean 。 return createBean(beanName, mbd, args); } catch (BeansException ex) { // 创建过程出现异常,销毁Bean destroySingleton(beanName); throw ex; } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } return (T) bean; }
然后是正式真正的创建Bean的方法如下:
AbstractAutowireCapableBeanFactory.createBean() 的方法。 @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { RootBeanDefinition mbdToUse = mbd; try { // doCreateBean 是Spring正在做事的方法。 Object beanInstance = doCreateBean(beanName, mbdToUse, args); } protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. // 实例化Bean 先创建一个BeanWrapper .这个方法里面Spring 一般为默认 // 无参的构造方法创建对象,所以大家如果重写对象的构造方法的时候,一定 // 要把无参构造方法也写出来。要不然某些情况下启动Spring容器可能会报错。 if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } // Initialize the bean instance. Object exposedObject = bean; try { // 为Bean的属性赋值。 populateBean(beanName, mbd, instanceWrapper); // 初始化Bean 。 exposedObject = initializeBean(beanName, exposedObject, mbd); } return exposedObject; } // 初始化Bean。 protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { // 如果你的Bean实现了Spring内置的Aware方法,会在这里执行 invokeAwareMethods(beanName, bean); Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { // 执行Bean的初始化前置处理器,很重要也就是Spring的钩子函数 wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { // 执行Bean的初始化方法 invokeInitMethods(beanName, wrappedBean, mbd); } if (mbd == null || !mbd.isSynthetic()) { // 执行Bean的后置处理器,也很重要。 // 很多写底层架构的人都会对此钩子方法灵活应用 wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
PS:下面是Bean实例化的详细的流程图,由于画好后的整个流程图无法完全保存,只有一张一张的截屏了。图片一张一张往下看就是整个完整的流程,自己可以找着图片一步一步看,就会对Bean的整个流程很清楚了。
读完熟悉了Spring实例化的流程你能做些什么呢?
1:比如实现BeanPostProcessor。A初始化前和后分别会执行下面2个方法。
@Component public class A implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println(beanName); return null; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println(beanName); return null; } }
2:实现InitializingBeanA初始化的时候会执行以下方法。
@Component public class A implements InitializingBean { @Override public void afterPropertiesSet() throws Exception { System.out.println("执行InitializingBean的afterPropertiesSet方法"); } }
上面实现的方法都会在A实例化的时候执行,如果你写的业务逻辑有需要在A实例化时候执行的就可以使用上面的方法完成。
欢迎跟着图中走一遍源码,相信你会对Spring创建Bean的流程更加了解。看一些源码你的思路会更清晰,写代码也更得心应手,写代码的时候你可能不自己觉的就按照这些大神写代码的思路去完成高质量的代码。