zoukankan      html  css  js  c++  java
  • Spring源码系列3

    一、DefaultListableBeanFactory的使用

    我们使用IOC容器通常会使用比较高级的ApplicationContext,而非较低层的BeanFactory。但对于那些不需要使用ApplicationContext中高级功能的场景,我们可以选择使用低层的BeanFactory。上一节中提到过Spring提供了BeanFactory的默认实现类DefaultListableBeanFactory,通常会结合XmlBeanDefinitionReader一起使用。

        public static void test() {
            ClassPathResource resource = new ClassPathResource("classpath:bean.xml");
    
            DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
            XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
            //加载Bean定义
            reader.loadBeanDefinitions(resource);
    
            //获取Bean
            User user = (User) factory.getBean("user");
            System.out.println(user.getName());
        }

    跟踪一下上面loadBeanDefinitions()方法的调用,得到如下调用链。

    XmlBeanDefinitionReader#loadBeanDefinitions()
        -->loadBeanDefinitions()重载
            -->doLoadBeanDefinitions()
                -->registerBeanDefinitions 
                    -->BeanDefinitionDocumentReader#registerBeanDefinitions() #注册Bean定义
                       默认实现DefaultBeanDefinitionDocumentReader#registerBeanDefinitions()
                        -->doRegisterBeanDefinitions()
                            -->parseBeanDefinitions() #解析Bean定义
                                ……

    上面过程主要分为注册BeanDefinition解析BeanDefinition两个步骤:

    Resource资源的定位

    BeanDefinition的加载

    BeanDefinition的注册

    将BeanDefinition注册到IOC容器中。这个过程通过调用BeanDefinitionRegistry接口实现类来完成。

    (这一部分待完成) 

    二、ApplicationContext容器启动过程

    我们先来看看常用的两种ApplicationContext实现:ClassPathXmlApplicationContext和AnnotationConfigApplicationContext,其构造方法调用代码如下。

        public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
            super(parent);
            //设置xml配置文件位置。此时并未解析和加载配置文件中的Bean
            setConfigLocations(configLocations);
            if (refresh) {
                refresh();
            }
        }
        
        public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
            this();
            //注册配置类(配置类也是一个Bean)。此时并未解析和注册配置类中配置的Bean
            register(annotatedClasses);
            refresh();
        }

    上面的xml形式,由于Bean存在于xml配置文件中,所以需要先确定配置文件的位置(setConfigLocations),最终将该位置保存在configLocations数组中(因为可能有多个配置)。

    下面的注解形式,由于Bean存在于注解配置类中,所以需要先确定注解配置类的位置和其它信息(如scope等),最后将该注解类相关信息保存在beanDefinitionMap中,spring将其称为注册,该配置类本身也是一个Bean,这里只注册了该配置Bean,在后续过程中会解析配置类中的Bean并进行注册。(beanDefinitionMap属性在defaultListableBeanFactory中声明。AnnotationConfigApplicationContext从GenericApplicationContext继承了registerBeanDefinition()方法,而该方法又委托底层默认容器DefaultListableBeanFactory的registerBeanDefinition()方法将注解类的信息保存到默认容器的beanDefinitionMap属性中。)

    之后,两者的过程都是一样了,也就是最最重要的refresh()方法。该方法是定义在AbstractApplicationContext类中的模板方法,其定义了容器启动的基本流程,并预留了一些钩子方法供子类进行扩展。

        //AbstractApplicationContext
        public void refresh() throws BeansException, IllegalStateException {
            //
            synchronized (this.startupShutdownMonitor) {
                // Prepare this context for refreshing.
                // 准备刷新。准备上下文
                prepareRefresh();
    
                // Tell the subclass to refresh the internal bean factory.
                // 告诉子类刷新内部BeanFactory
                ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
                // Prepare the bean factory for use in this context.
                // 准备使用BeanFactory
                prepareBeanFactory(beanFactory);
    
                try {
                    // Allows post-processing of the bean factory in context subclasses.
                    // 执行上下文子类中BeanFactory的后置处理器
                    postProcessBeanFactory(beanFactory);
    
                    // Invoke factory processors registered as beans in the context.
                    //调用BeanFactory的后置处理器(这些后置处理器是在Bean的定义中向容器注册的)
                    invokeBeanFactoryPostProcessors(beanFactory);
    
                    // Register bean processors that intercept bean creation.
                    //为Bean注册后置处理器(Bean创建后就会调用)
                    registerBeanPostProcessors(beanFactory);
    
                    // Initialize message source for this context.
                    //初始化消息源(国际化信息)
                    initMessageSource();
    
                    // Initialize event multicaster for this context.
                    //初始化消息广播,
                    initApplicationEventMulticaster();
    
                    // Initialize other special beans in specific context subclasses.
                    //初始化其它特殊Bean(子类实现)。SpringBoot核心基类EmbeddedWebApplicationContext就是实现该方法来初始化容器的
                    onRefresh();
    
                    // Check for listener beans and register them.
                    //注册监听器
                    registerListeners();
    
                    // Instantiate all remaining (non-lazy-init) singletons.
                    //将所有非延迟加载的Bean初始化,并设置冻结标识,以防止不必要的重新实例化
                    finishBeanFactoryInitialization(beanFactory);
    
                    // Last step: publish corresponding event.
                    //注册、启动LifeCycleProcessor,并且发送启动完成事件
                    finishRefresh();
                }
    
                catch (BeansException ex) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("Exception encountered during context initialization - " +
                                "cancelling refresh attempt: " + ex);
                    }
    
                    // Destroy already created singletons to avoid dangling resources.
                    destroyBeans();
    
                    // Reset 'active' flag.
                    cancelRefresh(ex);
    
                    // Propagate exception to caller.
                    throw ex;
                }
    
                finally {
                    // Reset common introspection caches in Spring's core, since we
                    // might not ever need metadata for singleton beans anymore...
                    resetCommonCaches();
                }
            }
        }

    ApplicationContext容器启动流程图

    根据上面的代码逻辑,可以绘制出如下的ApplicationContext启动流程图。

    其中registerBeanPostProcessors和finishBeanFactoryInitialization这两个步骤需要重点关注一下。registerBeanPostProcessors流程如下,而finishBeanFactoryInitialization在后文中有详细解析。

    下面依次来看每一个步骤。

    2.1 prepareRefresh

        /**
         * Prepare this context for refreshing, setting its startup date and
         * active flag as well as performing any initialization of property sources.
         * 准备上下文刷新,记录启动时间和激活标识,还有属性源的初始化。
         */
        protected void prepareRefresh() {
            // Switch to active.
            //将相关标识切换到激活状态
            this.startupDate = System.currentTimeMillis();
            this.closed.set(false);
            this.active.set(true);
    
            if (logger.isInfoEnabled()) {
                logger.info("Refreshing " + this);
            }
    
            // Initialize any placeholder property sources in the context environment.
            // (子类实现)初始化上下文环境中的placeholder属性。默认空实现。
            initPropertySources();
    
            // Validate that all properties marked as required are resolvable:
            // see ConfigurablePropertyResolver#setRequiredProperties
            // 校验属性
            getEnvironment().validateRequiredProperties();
    
            // Allow for the collection of early ApplicationEvents,
            // to be published once the multicaster is available...
            // 用于早期ApplicationEvents的收集,一旦multicaster广播器可用,将会广播这些事件。
            this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
        }

    2.2 obtainFreshBeanFactory

        /**
         * Tell the subclass to refresh the internal bean factory.
         */
        protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
            refreshBeanFactory();
            //这里返回的是底层容器DefaultListableBeanFactory实例
            ConfigurableListableBeanFactory beanFactory = getBeanFactory();
            if (logger.isDebugEnabled()) {
                logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
            }
            return beanFactory;
        }

    2.3 prepareBeanFactory

        /**
         * Configure the factory's standard context characteristics,such as the context's ClassLoader and post-processors.
         */
        protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
            // Tell the internal bean factory to use the context's class loader etc.
            //告诉内部BeanFactory使用上下文类加载器
            beanFactory.setBeanClassLoader(getClassLoader());
            //设置bean表达式(spel)解析器
            beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
            beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
    
            // Configure the bean factory with context callbacks.
            //将当前的ApplicationContext对象交给ApplicationContextAwareProcessor类来处理,从而在Aware接口实现类中的注入applicationContext
            beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
            
            //设置忽略自动装配的接口
            beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
            beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
            beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
            beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
            beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
            beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
    
            // BeanFactory interface not registered as resolvable type in a plain factory.
            // MessageSource registered (and found for autowiring) as a bean.
            //注册可以解析的自动装配
            beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
            beanFactory.registerResolvableDependency(ResourceLoader.class, this);
            beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
            beanFactory.registerResolvableDependency(ApplicationContext.class, this);
    
            // Register early post-processor for detecting inner beans as ApplicationListeners.
            beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
    
            // Detect a LoadTimeWeaver and prepare for weaving, if found.
            //如果当前BeanFactory包含loadTimeWeaver Bean,说明存在类加载期织入AspectJ,则把当前BeanFactory交给类加载期BeanPostProcessor实现类LoadTimeWeaverAwareProcessor来处理,从而实现类加载期织入AspectJ的目的。
            if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
                beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
                // Set a temporary ClassLoader for type matching.
                beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
            }
    
            // Register default environment beans.
            //注册当前容器环境environment组件Bean
            if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
                beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
            }
            //注册系统配置systemProperties组件Bean
            if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
                beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
            }
            //注册系统环境systemEnvironment组件Bean
            if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
                beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
            }
        }

    2.4 postProcessBeanFactory

        /**
         * 在内部BeanFactory进行标准初始化之后,修改应用上下文。所有的Bean定义都会被加载,但都还未被初始化。
         * 这考虑到的是为某种ApplicationContext实现版本中用来注册特殊的Bean后置处理器
         */
        protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        }

    2.5 invokeBeanFactoryPostProcessors

        /**
         * Instantiate and invoke all registered BeanFactoryPostProcessor beans,respecting explicit order if given.
         * <p>Must be called before singleton instantiation.
         * 实例化并调用所有已注册的BeanFactoryPostProcessor(它们也都是Bean),遵循显式的顺序(如果给定了的话)。必须在单例实例化之前调用。
         */
        protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
            PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
    
            // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
            // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
            if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
                beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
                beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
            }
        }

    2.6 registerBeanPostProcessors

        /**
         * 实例化并注册所有BeanPostProcessor(它们也都是Bean),遵循显式的顺序(如果给定了的话)。必须在所有应用Bean实例化之前调用。
         */
        protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
            PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
        }
        
        public static void registerBeanPostProcessors(
                ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
    
            String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
    
            // Register BeanPostProcessorChecker that logs an info message when
            // a bean is created during BeanPostProcessor instantiation, i.e. when
            // a bean is not eligible for getting processed by all BeanPostProcessors.
            int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
            beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
    
            // Separate between BeanPostProcessors that implement PriorityOrdered,
            // Ordered, and the rest.
            List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanPostProcessor>();
            List<BeanPostProcessor> internalPostProcessors = new ArrayList<BeanPostProcessor>();
            List<String> orderedPostProcessorNames = new ArrayList<String>();
            List<String> nonOrderedPostProcessorNames = new ArrayList<String>();
            for (String ppName : postProcessorNames) {
                if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                    BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
                    priorityOrderedPostProcessors.add(pp);
                    if (pp instanceof MergedBeanDefinitionPostProcessor) {
                        internalPostProcessors.add(pp);
                    }
                }
                else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
                    orderedPostProcessorNames.add(ppName);
                }
                else {
                    nonOrderedPostProcessorNames.add(ppName);
                }
            }
    
            // First, register the BeanPostProcessors that implement PriorityOrdered.
            //首先,注册实现了PriorityOrdered接口的BanPostProcessors
            sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
            registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
    
            // Next, register the BeanPostProcessors that implement Ordered.
            //然后,注册实现了Ordered接口的BanPostProcessors
            List<BeanPostProcessor> orderedPostProcessors = new ArrayList<BeanPostProcessor>();
            for (String ppName : orderedPostProcessorNames) {
                BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
                orderedPostProcessors.add(pp);
                if (pp instanceof MergedBeanDefinitionPostProcessor) {
                    internalPostProcessors.add(pp);
                }
            }
            sortPostProcessors(orderedPostProcessors, beanFactory);
            registerBeanPostProcessors(beanFactory, orderedPostProcessors);
    
            // Now, register all regular BeanPostProcessors.
            //注册所有常规的BeanPostProcessors
            List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanPostProcessor>();
            for (String ppName : nonOrderedPostProcessorNames) {
                BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
                nonOrderedPostProcessors.add(pp);
                if (pp instanceof MergedBeanDefinitionPostProcessor) {
                    internalPostProcessors.add(pp);
                }
            }
            registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
    
            // Finally, re-register all internal BeanPostProcessors.
            //最后,重新注册所有内部BeanPostProcessors
            sortPostProcessors(internalPostProcessors, beanFactory);
            registerBeanPostProcessors(beanFactory, internalPostProcessors);
    
            // Re-register post-processor for detecting inner beans as ApplicationListeners,
            // moving it to the end of the processor chain (for picking up proxies etc).
            //重新注册后置处理器作为应用监听器,用于监听内部Bean。移动到处理器链的尾部
            beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
        }

    2.7 initMessageSource

        /**
         * Initialize the MessageSource.
         * Use parent's if none defined in this context.
         * 初始化MessageSource。如果上下文中未定义,则使用父级的。
         */
        protected void initMessageSource() {
            ConfigurableListableBeanFactory beanFactory = getBeanFactory();
            //如果已经配置了MessageSource,则从容器中直接获取使用。
            if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
                this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
                // Make MessageSource aware of parent MessageSource.
                if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
                    HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
                    if (hms.getParentMessageSource() == null) {
                        // Only set parent context as parent MessageSource if no parent MessageSource
                        // registered already.
                        hms.setParentMessageSource(getInternalParentMessageSource());
                    }
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Using MessageSource [" + this.messageSource + "]");
                }
            }
            else {
                // Use empty MessageSource to be able to accept getMessage calls.
                DelegatingMessageSource dms = new DelegatingMessageSource();
                dms.setParentMessageSource(getInternalParentMessageSource());
                this.messageSource = dms;
                beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
                if (logger.isDebugEnabled()) {
                    logger.debug("Unable to locate MessageSource with name '" + MESSAGE_SOURCE_BEAN_NAME +
                            "': using default [" + this.messageSource + "]");
                }
            }
        }

    2.8 initApplicationEventMulticaster

        /**
         * Initialize the ApplicationEventMulticaster.
         * Uses SimpleApplicationEventMulticaster if none defined in the context.
         * 初始化事件广播器。如果没有定义,则创建一个impleApplicationEventMulticaster
         */
        protected void initApplicationEventMulticaster() {
            ConfigurableListableBeanFactory beanFactory = getBeanFactory();
            //容器中定义了事件广播器,则直接从容器中获取使用。
            if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
                this.applicationEventMulticaster =
                        beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
                if (logger.isDebugEnabled()) {
                    logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
                }
            }
            //否则,创建一个impleApplicationEventMulticaster
            else {
                this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
                beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
                if (logger.isDebugEnabled()) {
                    logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
                            APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
                            "': using default [" + this.applicationEventMulticaster + "]");
                }
            }
        }

    2.9 onRefresh

        /**
         * Template method which can be overridden to add context-specific refresh work.
         * Called on initialization of special beans, before instantiation of singletons.
         * <p>This implementation is empty.
         */
        protected void onRefresh() throws BeansException {
            // For subclasses: do nothing by default.
        }

    模板方法,在子类中实现该方法用于子类上下文相关的刷新操作。默认是空实现。

    2.10 registerListeners

        /**
         * Add beans that implement ApplicationListener as listeners.
         * Doesn't affect other listeners, which can be added without being beans.
         */
        protected void registerListeners() {
            // Register statically specified listeners first.
            for (ApplicationListener<?> listener : getApplicationListeners()) {
                getApplicationEventMulticaster().addApplicationListener(listener);
            }
    
            // Do not initialize FactoryBeans here: We need to leave all regular beans
            // uninitialized to let post-processors apply to them!
            String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
            for (String listenerBeanName : listenerBeanNames) {
                getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
            }
    
            // Publish early application events now that we finally have a multicaster...
            Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
            this.earlyApplicationEvents = null;
            if (earlyEventsToProcess != null) {
                for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
                    getApplicationEventMulticaster().multicastEvent(earlyEvent);
                }
            }
        }

    2.11 finishBeanFactoryInitialization

        /**
         * Finish the initialization of this context's bean factory,
         * initializing all remaining singleton beans.
         * 完成当前上下文BeanFactory的初始化。初始化所有剩余的单例bean。
         */
        protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
            // Initialize conversion service for this context.
            // 初始化类型转换服务(比如string转date类型,实现Converter接口的服务类)
            if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
                    beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
                beanFactory.setConversionService(
                        beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
            }
    
            // Register a default embedded value resolver if no bean post-processor
            // (such as a PropertyPlaceholderConfigurer bean) registered any before:
            // at this point, primarily for resolution in annotation attribute values.
            //如果之前没有bean后置处理器(例如PropertyPlaceholderConfigurer)被注册,则注册一个默认内置的值解析器:此时,主要用于注释属性值的解析
            if (!beanFactory.hasEmbeddedValueResolver()) {
                beanFactory.addEmbeddedValueResolver(new StringValueResolver() {
                    @Override
                    public String resolveStringValue(String strVal) {
                        return getEnvironment().resolvePlaceholders(strVal);
                    }
                });
            }
    
            // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
            //初始化LoadTimeWeaverAware(用于动态转换类)
            String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
            for (String weaverAwareName : weaverAwareNames) {
                getBean(weaverAwareName);
            }
    
            // Stop using the temporary ClassLoader for type matching.
            //停用用于类型匹配的临时类加载器
            beanFactory.setTempClassLoader(null);
    
            // Allow for caching all bean definition metadata, not expecting further changes.
            //冻结所有bean的定义,不期望再进行修改。(设置configurationFrozen = true,将所有注册的bean名称到frozenBeanDefinitionNames中)
            beanFactory.freezeConfiguration();
    
            // Instantiate all remaining (non-lazy-init) singletons.
            //【重点】实例化所有剩余(非懒加载)单例
            beanFactory.preInstantiateSingletons();
        }

    完成BeanFactory的初始化工作。包括ConversionService的设置、注册默认的内置值解析器、初始化LoadTimeWeaverAware、停用用于类型匹配的临时类加载器、配置冻结、以及实例化所有非懒加载的单例最后一步实例化非懒加载的单例Bean在文章后面会重点进行分析。

    2.12 finishRefresh

        /**
         * Finish the refresh of this context, invoking the LifecycleProcessor's
         * onRefresh() method and publishing the{@link org.springframework.context.event.ContextRefreshedEvent}.
         * 完成上下文的刷新,调用生命周期处理器的onRefresh()方法,并发布ContextRefreshEvent事件
         */
        protected void finishRefresh() {
            // Initialize lifecycle processor for this context.
            //初始化上下文生命周期处理器
            initLifecycleProcessor();
    
            // Propagate refresh to lifecycle processor first.
            //将刷新传播到生命周期处理器
            getLifecycleProcessor().onRefresh();
    
            // Publish the final event.
            //发布最终的事件
            publishEvent(new ContextRefreshedEvent(this));
    
            // Participate in LiveBeansView MBean, if active.
            LiveBeansView.registerApplicationContext(this);
        }

    Spring提供了Lifecycle接口,该接口中包含了start和stop方法。实现了Lifecycle接口后,Spring会保证在启动时调用其start方法开始生命周期,并在关闭时调用stop方法来结束生命周期。通常用于配置后台程序,启动后一直会运行

    2.13 resetCommonCaches

        /**
         * Reset Spring's common core caches, in particular the ReflectionUtils,ResolvableType and CachedIntrospectionResults caches.
         */
        protected void resetCommonCaches() {
            ReflectionUtils.clearCache();
            ResolvableType.clearCache();
            CachedIntrospectionResults.clearClassLoader(getClassLoader());
        }

    清除创建单例Bean过程中的一些缓存。如反射时的缓存,解析Bean过程中的缓存等。因为单例Bean一旦创建,就不用再次创建了,缓存信息也就不再需要了。

    三、(非懒加载)Bean的创建流程分析

    容器启动阶段,仅非懒加载的bean才会创建。我们继续追踪上面refresh步骤中的倒数第二步finishBeanFactoryInitialization()

    refresh()
      -->finishBeanFactoryInitialization()
        -->beanFactory.preInstantiateSingletons()
          -->DefaultListableBeanFactory#preInstantiateSingletons()
        @Override
        public void preInstantiateSingletons() throws BeansException {
            if (logger.isDebugEnabled()) {
                logger.debug("Pre-instantiating singletons in " + this);
            }
    
            // 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.
            List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
    
            // Trigger initialization of all non-lazy singleton beans...
            // 触发非lazy-init的单例Bean的初始化
            for (String beanName : beanNames) {
                RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
                // 如果是FactoryBean
                if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                    if (isFactoryBean(beanName)) {
                        final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
                        boolean isEagerInit;
                        if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                            isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
                                @Override
                                public Boolean run() {
                                    return ((SmartFactoryBean<?>) factory).isEagerInit();
                                }
                            }, getAccessControlContext());
                        }
                        else {
                            isEagerInit = (factory instanceof SmartFactoryBean &&
                                    ((SmartFactoryBean<?>) factory).isEagerInit());
                        }
                        if (isEagerInit) {
                            getBean(beanName);
                        }
                    }
                    //非FactoryBean(普通Bean)
                    else {
                        getBean(beanName);
                    }
                }
            }
    
            // Trigger post-initialization callback for all applicable beans...
            // 所有单例Bean创建后,依次触发后置处理
            for (String beanName : beanNames) {
                //此时由于所有单例都已经创建并初始化好了,且被缓存在SingletonObjects中,所以直接(通过一级缓存)就能获取到
                Object singletonInstance = getSingleton(beanName);
                //SmartInitalizingSingleton接口用于在所有(非懒加载的)单例创建后的回调
                if (singletonInstance instanceof SmartInitializingSingleton) {
                    final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
                    if (System.getSecurityManager() != null) {
                        AccessController.doPrivileged(new PrivilegedAction<Object>() {
                            @Override
                            public Object run() {
                                smartSingleton.afterSingletonsInstantiated();
                                return null;
                            }
                        }, getAccessControlContext());
                    }
                    else {
                        smartSingleton.afterSingletonsInstantiated();
                    }
                }
            }
        }

    继续往下追踪其中的getBean方法:

    getBean-->AbstractBeanFactory#getBean-->doGetBean
        @SuppressWarnings("unchecked")
        protected <T> T doGetBean(
                final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
                throws BeansException {
    
            final String beanName = transformedBeanName(name);
            Object bean;
    
            // Eagerly check singleton cache for manually registered singletons.
            // 1.三级缓存方式获取Bean
            Object sharedInstance = getSingleton(beanName);
            if (sharedInstance != null && args == null) {
                if (logger.isDebugEnabled()) {
                    if (isSingletonCurrentlyInCreation(beanName)) {
                        logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                                "' that is not fully initialized yet - a consequence of a circular reference");
                    }
                    else {
                        logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
                    }
                }
                //返回bean实例,或者如果是FactoryBean则进行一些额外处理
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
            }
            //通过三级缓存方式未获取到Bean,或者args==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);
                }
    
                // Check if bean definition exists in this factory.
                // 检查工厂中是否存在Bean的定义
                BeanFactory parentBeanFactory = getParentBeanFactory();
                if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                    // Not found -> check parent.
                    String nameToLookup = originalBeanName(name);
                    if (args != null) {
                        // Delegation to parent with explicit args.
                        // 带参 -> 委派给父类带参的getBean
                        return (T) parentBeanFactory.getBean(nameToLookup, args);
                    }
                    else {
                        // No args -> delegate to standard getBean method.
                        // 无参 -> 委派给标准的getBean
                        return parentBeanFactory.getBean(nameToLookup, requiredType);
                    }
                }
    
                if (!typeCheckOnly) {
                    markBeanAsCreated(beanName);
                }
    
                try {
                    final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                    checkMergedBeanDefinition(mbd, beanName, args);
    
                    // Guarantee initialization of beans that the current bean depends on.
                    // 保证使用depends-on属性(xml形式)或@DepdensOn依赖的Bean初始化
                    String[] dependsOn = mbd.getDependsOn();
                    if (dependsOn != null) {
                        for (String dep : dependsOn) {
                            if (isDependent(beanName, dep)) {
                                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                        "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                            }
                            registerDependentBean(dep, beanName);
                            try {
                                getBean(dep);
                            }
                            catch (NoSuchBeanDefinitionException ex) {
                                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                        "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                            }
                        }
                    }
    
                    // 2.Create bean instance.创建Bean实例
                    if (mbd.isSingleton()) { //创建单例Bean
                        sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                            @Override
                            public Object getObject() throws BeansException {
                                try {
                                    return createBean(beanName, mbd, args);
                                }
                                catch (BeansException ex) {
                                    // Explicitly remove instance from singleton cache: It might have been put there
                                    // eagerly by the creation process, to allow for circular reference resolution.
                                    // Also remove any beans that received a temporary reference to the bean.
                                    destroySingleton(beanName);
                                    throw ex;
                                }
                            }
                        });
                        bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                    }
    
                    else if (mbd.isPrototype()) {// 创建多例Bean
                        // It's a prototype -> create a new instance.
                        Object prototypeInstance = null;
                        try {
                            beforePrototypeCreation(beanName);
                            prototypeInstance = createBean(beanName, mbd, args);
                        }
                        finally {
                            afterPrototypeCreation(beanName);
                        }
                        bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                    }
    
                    else { //创建其它scope类型的Bean(scope类型除了singleton和prototype,还有request、session、global session作用域)
                        String scopeName = mbd.getScope();
                        final Scope scope = this.scopes.get(scopeName);
                        if (scope == null) {
                            throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                        }
                        try {
                            Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
                                @Override
                                public Object getObject() throws BeansException {
                                    beforePrototypeCreation(beanName);
                                    try {
                                        return createBean(beanName, mbd, args);
                                    }
                                    finally {
                                        afterPrototypeCreation(beanName);
                                    }
                                }
                            });
                            bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                        }
                        catch (IllegalStateException ex) {
                            throw new BeanCreationException(beanName,
                                    "Scope '" + scopeName + "' is not active for the current thread; consider " +
                                    "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                                    ex);
                        }
                    }
                }
                catch (BeansException ex) {
                    cleanupAfterBeanCreationFailure(beanName);
                    throw ex;
                }
            }
    
            // Check if required type matches the type of the actual bean instance.
            // 检查所需的类型是否与真实Bean实例类型相匹配
            if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
                try {
                    return getTypeConverter().convertIfNecessary(bean, requiredType);
                }
                catch (TypeMismatchException ex) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Failed to convert bean '" + name + "' to required type '" +
                                ClassUtils.getQualifiedName(requiredType) + "'", ex);
                    }
                    throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
                }
            }
            return (T) bean;
        }

    大体上获取Bean的过程就是

    • 如果能从容器中获取到就返回(getSingleton)
    • 如果获取不到就创建(createBean)

    3.1 getSingleton

    首先从容器中获取Bean,代码如下。

        public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
            Assert.notNull(beanName, "'beanName' must not be null");
            synchronized (this.singletonObjects) {
                Object singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    if (this.singletonsCurrentlyInDestruction) {
                        throw new BeanCreationNotAllowedException(beanName,
                                "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                                "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
                    }
                    if (logger.isDebugEnabled()) {
                        logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
                    }
                    beforeSingletonCreation(beanName);
                    boolean newSingleton = false;
                    boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
                    if (recordSuppressedExceptions) {
                        this.suppressedExceptions = new LinkedHashSet<Exception>();
                    }
                    try {
                        singletonObject = singletonFactory.getObject();
                        newSingleton = true;
                    }
                    catch (IllegalStateException ex) {
                        // Has the singleton object implicitly appeared in the meantime ->
                        // if yes, proceed with it since the exception indicates that state.
                        singletonObject = this.singletonObjects.get(beanName);
                        if (singletonObject == null) {
                            throw ex;
                        }
                    }
                    catch (BeanCreationException ex) {
                        if (recordSuppressedExceptions) {
                            for (Exception suppressedException : this.suppressedExceptions) {
                                ex.addRelatedCause(suppressedException);
                            }
                        }
                        throw ex;
                    }
                    finally {
                        if (recordSuppressedExceptions) {
                            this.suppressedExceptions = null;
                        }
                        afterSingletonCreation(beanName);
                    }
                    if (newSingleton) {
                        addSingleton(beanName, singletonObject);
                    }
                }
                return (singletonObject != NULL_OBJECT ? singletonObject : null);
            }
        }

    上面代码逻辑可以转换为以下流程图:

    存在以下三个存储位置,网上有博客称为三级缓存。获取Bean时,会依次按照singletonObjects-->earlySingletonObjects-->singletonFactories先后顺序获取。

    • singletonObjects:用于存放完全创建完成的bean(完成了实例化,属性填充,和初始化)
    • earlySingletonObjects:提前暴露的单例缓存,存放原生(raw) bean对象(尚未填充属性,和初始化),用于解决循环依赖
    • singletonFactories:存放bean工厂对象,从该工厂可以获取原始对象的引用,也就是“早期引用”。

    由于singletonObjects是缓存完全初始化好的Bean的,所以,直接从中获取到 的Bean是可以直接使用的。如果从中获取不到,则从earlySingletonObjects获取未初始化完全的单例,之后的流程中会再经过属性赋值,最终也会成为一个完整的Bean。要是前两个过程都无法获取到Bean,则再由单例工厂Bean来获取了,获取到的Bean是原生(raw) Bean。将其移入earlySingletonObjects缓存中,便于需要时直接获取,而此时singletonFactories缓存也就不再需要了,直接从中清除工厂对象,也就是将三级缓存提升为二级缓存

    3.2 createBean

    如果getBean未获取到Bean说明容器中不存在,也就只能让容器来创建Bean了,代码如下。

        @Override
        protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
            if (logger.isDebugEnabled()) {
                logger.debug("Creating instance of bean '" + beanName + "'");
            }
            RootBeanDefinition mbdToUse = mbd;
    
            // Make sure bean class is actually resolved at this point, and
            // clone the bean definition in case of a dynamically resolved Class
            // which cannot be stored in the shared merged bean definition.
            Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
            if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
                mbdToUse = new RootBeanDefinition(mbd);
                mbdToUse.setBeanClass(resolvedClass);
            }
    
            // Prepare method overrides.
            try {
                mbdToUse.prepareMethodOverrides();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
                        beanName, "Validation of method overrides failed", ex);
            }
    
            try {
                // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
                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);
            }
            // 这里才是真正的创建Bean
            Object beanInstance = doCreateBean(beanName, mbdToUse, args);
            if (logger.isDebugEnabled()) {
                logger.debug("Finished creating instance of bean '" + beanName + "'");
            }
            return beanInstance;
        }

    createBean-->doCreateBean

        protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
                throws BeanCreationException {
    
            // Instantiate the bean.
            BeanWrapper instanceWrapper = null;
            if (mbd.isSingleton()) {
                instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
            }
            if (instanceWrapper == null) {
                //1.创建实例(返回的是包装类型)
                instanceWrapper = createBeanInstance(beanName, mbd, args);
            }
             
            final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
            Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
            mbd.resolvedTargetType = beanType;
    
            // Allow post-processors to modify the merged bean definition.
            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;
                }
            }
    
            // Eagerly cache singletons to be able to resolve circular references
            // even when triggered by lifecycle interfaces like BeanFactoryAware.
            //判断singletonsCurrentlyInCreation中是否有beanName,有则表明单例正在创建中
            boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                    isSingletonCurrentlyInCreation(beanName));
            if (earlySingletonExposure) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Eagerly caching bean '" + beanName +
                            "' to allow for resolving potential circular references");
                }
                //缓存单例工厂,该工厂用于创建指定的单例
                addSingletonFactory(beanName, new ObjectFactory<Object>() {
                    @Override
                    public Object getObject() throws BeansException {
                        return getEarlyBeanReference(beanName, mbd, bean);
                    }
                });
            }
    
            // Initialize the bean instance.
            Object exposedObject = bean;
            try {
                //2.为Bean的属性赋值
                populateBean(beanName, mbd, instanceWrapper);
                if (exposedObject != null) {
                    //3.初始化Bean
                    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) {
                Object earlySingletonReference = getSingleton(beanName, false);
                if (earlySingletonReference != null) {
                    if (exposedObject == bean) {
                        exposedObject = earlySingletonReference;
                    }
                    else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                        String[] dependentBeans = getDependentBeans(beanName);
                        Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);
                        for (String dependentBean : dependentBeans) {
                            if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                                actualDependentBeans.add(dependentBean);
                            }
                        }
                        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.");
                        }
                    }
                }
            }
    
            // 4.注册销毁方法
            try {
                registerDisposableBeanIfNecessary(beanName, bean, mbd);
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
            }
    
            return exposedObject;
        }

    由于篇幅原因,这里我没有花太多语言来介绍上面整个创建过程。而是将其放到了Spring源码系列4 - Bean的生命周期中。

    另外,我将创建Bean的整个过程绘制成了详细的流程图,对照流程图来理解代码会更轻松一些。

    finishBeanFactoryInitialization的完整流程图

    图一:

    图二:

    图三:

    三、Spring如何解决循环依赖?

    ①基于构造器的循环依赖

    基于构造器的循环依赖是无解的,会直接抛出BeanCurrentlyInCreationException异常,表示循环依赖。

        <!--基于构造器的循环依赖-->
        <bean id="beanA" class="com.lp.test.recycledependency.constructor.A">
            <constructor-arg ref="beanB"/>
        </bean>
    
        <bean id="beanB" class="com.lp.test.recycledependency.constructor.B">
            <constructor-arg ref="beanA"/>
        </bean>

    ②基于setter的循环依赖

    基于setter注入方式的循环依赖,是通过Spring容器将刚完成构造器注入,但尚未完成其它步骤(属性赋值)的bean提前暴露来完成的,而且只能解决单例作用域的bean循环依赖

        <!--基于setter的循环依赖-->
        <bean id="beanC" class="com.lp.test.recycledependency.setter.C">
            <property name="d" ref="beanD"/>
        </bean>
    
        <bean id="beanD" class="com.lp.test.recycledependency.setter.D">
            <property name="c" ref="beanC"/>
        </bean>

    假如 beanC 和 beanD 相互依赖,beanC为C类型,beanD为D类型,beanC中存在一个D类型的属性d ,beanD中存在一个C类型的属性c。

    ①假设首先实例化beanC,在完成beanC的实例化后,此时beanC实例中的所有属性还未被赋值,都为null,当前beanC称为原始Bean。

    beanC创建完后,会将自己提前暴露出来(加入到了三级缓存中)。

    ②接下来为beanC中的属性赋值,经过解析依赖,发现beanC依赖beanD。

    ③于是会先去实例化beanD,也同样会再经过解析依赖的过程,会检测发现依赖beanC,此时会通过(隐式的)getBean从容器中获取beanC。

    由于beanC已经添加到缓存中,通过ObjectFactory提前暴露,通过ObjectFactory#getObject()即可获取到beanC。容器获取 beanC 对象的一个早期的引用(early reference),并把这个早期引用注入到 beanD 中,让 beanD 先完成初始化。beanD 完成初始化后,beanC 就可以获取到 beanD 的完整引用,beanC也就随之完成初始化。

    下图就是解决循环依赖的流程图。注意图中标出的①②③这三处缓存的变化。

    ①处是Bean刚创建完后,将创建该Bean的工厂加入到三级缓存。

    ②处是在解决依赖时,通过一级缓存取出工厂创建Bean,然后将Bean加入到二级缓存中,同时删除了三级缓存(相当于三级缓存升级为二级缓存)。

    ③处是在Bean完成初始化后成为一个完整的Bean时,将该Bean加入到一级缓存,并同时清理二级缓存和三级缓存(相当于二级缓存升级为一级缓存)。

    总结

    1.Spring容器的启动流程

    2.Spring能解决哪种情况的循环依赖?

    只能解决基于setter方式的循环依赖,不能解决基于构造器注入循环依赖,和多例(prototype)的循环依赖。

    3.Spring是如何解决循环依赖的?理解其中的三级缓存!

    4.Spring为什么不能解决构造器的循环依赖?

    Bean在调用构造器进行实例化的过程中,并不会用到三级缓存,只是在实例化之后才放入三级缓存中。所以只能抛出循环依赖的异常了。

    5.为什么多实例Bean不能解决循环依赖?

    多实例Bean是每次创建都会调用doGetBean方法,根本没有使用一二三级缓存,也就不能解决循环依赖。

    参考:

    Spring IOC 循环依赖的解决办法

    高频面试题:Spring 如何解决循环依赖?

  • 相关阅读:
    CentOS下MySQL的彻底卸载
    cent 7.0 安装mysql
    centos 安装mysql Package: akonadi-mysql-1.9.2-4.el7.x86_64 (@anaconda)
    使用注解配置SQL映射器
    bean
    转:最简日志打印规范
    快速搭建sonar代码质量管理平台
    (转)Where与Having的总结
    一个问题,日后会写为什么贴出来
    hive Tutorial
  • 原文地址:https://www.cnblogs.com/rouqinglangzi/p/12308321.html
Copyright © 2011-2022 走看看