博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
追踪解析Spring ioc启动源码(3)
阅读量:6973 次
发布时间:2019-06-27

本文共 45595 字,大约阅读时间需要 151 分钟。

4 在bean factory中创造 bean

写在前面:该 part 是 Spring ioc 的核心,显得非常冗杂,Spring 内不知名的组件非常的多,有很多笔者也难以描述清楚,甚至也没见过。在介绍的时候会做适当的忽略。

该 part 的起点:

public AnnotationConfigApplicationContext(Class
... annotatedClasses) { this(); register(annotatedClasses); refresh(); // 4 在 bean factory 中创造 bean}

来追踪这个方法的实现:

//AbstractApplicationContext.classpublic void refresh() throws BeansException, IllegalStateException {    synchronized (this.startupShutdownMonitor) {        //准备工作的配置        //4.3        prepareRefresh();        //获取 bean factory        //4.4        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();        //配置 bean factory        //4.5        prepareBeanFactory(beanFactory);        try {            //4.6            postProcessBeanFactory(beanFactory);            //4.7            invokeBeanFactoryPostProcessors(beanFactory);            //4.8            registerBeanPostProcessors(beanFactory);            //4.9            initMessageSource();            //4.10            initApplicationEventMulticaster();            //4.11            onRefresh();            //4.12            registerListeners();            //4.13            finishBeanFactoryInitialization(beanFactory);            //4.15            finishRefresh();        }catch (BeansException ex) {            if (logger.isWarnEnabled()) {                logger.warn("Exception encountered during context initialization - " +                        "cancelling refresh attempt: " + ex);            }            //4.16            destroyBeans();            //4.17            cancelRefresh(ex);            throw ex;        }finally {            //4.18            resetCommonCaches();        }    }}

4.1

在开始之前先来看一下 BeanPostProcessor:

public interface BeanPostProcessor {    //此方法在 bean 初始化之前、bean 的构造方法调用之后执行    @Nullable    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {        return bean;    }    //此方法在 bean 初始化之后执行    @Nullable    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {        return bean;    }}

BeanPostProcessor 在 bean 的注册阶段就已经大量接触到了,在下列 bean 的创建阶段会更多的遇到,是 Spring ioc 的重要组成部分。

Spring 容器本身在初始化的时候就会注册很多个 BeanPostProcessor 接口的实现类到 BeanFactory 中。这些类会被 BeanFactory 实例成 bean,并特殊存放到一个列表中。在其它普通 bean 的初始化(即为 init 方法被调用时)之前,轮循 BeanPostProcessor 列表,执行 postProcessBeforeInitialization(...) 方法;在其它普通 bean 的初始化之后,再次轮训 BeanPostProcessor 列表,执行 postProcessAfterInitialization(...) 方法。

从出入参可知,这两个方法用于在 bean 的初始化阶段对 bean 进行功能增强操作,包括但不限于代码织入(asm)、切面操作(aop)等。

作为 Spring 的使用者,只要是自行编写实现了该接口的类,然后通过配置将该类作为 Bean 注册到 Spring 中,就会被 Spring 一视同仁的作为内部 BeanPostProcessor 对待。

4.2

再来看一下创建 bean 的核心方法,即 BeanUtil.instantiateClass(...):

//BeanUtil.classpublic static 
T instantiateClass(Constructor
ctor, Object... args) throws BeanInstantiationException { Assert.notNull(ctor, "Constructor must not be null"); try { //设置 accessible = true,即为去掉 privite 关键词对构造的影响 ReflectionUtils.makeAccessible(ctor); //这个返回语句主要是为了兼容 kotlin 语言,对于 java 来说主要是 ctor.newInstance(args) //本质是调用 bean 的构造器来实例化 bean return (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ? KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args)); }catch (InstantiationException ex) { throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex); }catch (IllegalAccessException ex) { throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex); }catch (IllegalArgumentException ex) { throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex); }catch (InvocationTargetException ex) { throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException()); }}

BeanUtil 是 bean 的实例化的关键,Spring 的其它代码都是在做各种判断,但是真正实例化 bean 的就这一句。

4.3

看下方代码片段:

//AbstractApplicationContext.classprotected void prepareRefresh() {    //记录下启动时间    this.startupDate = System.currentTimeMillis();        //这两个变量与优雅关闭有关,这里确定 Spring 未关闭    this.closed.set(false);    this.active.set(true);    //logger 日志相关代码均不作描述了    if (logger.isDebugEnabled()) {        if (logger.isTraceEnabled()) {            logger.trace("Refreshing " + this);        }        else {            logger.debug("Refreshing " + getDisplayName());        }    }    //初始化 property    //该方法是空的    initPropertySources();    //检查读取到的 properties 键值对    getEnvironment().validateRequiredProperties();    //新建一个集合,这个集合用于储存需要立即通知的事件    this.earlyApplicationEvents = new LinkedHashSet<>();}

closed 和 active 都是定义在 AbstractApplicationContext 中的 AtomicBoolean,用以管理 Spring 容器的状态:

//容器是否处于活动中private final AtomicBoolean active = new AtomicBoolean();//容器是否已经关闭private final AtomicBoolean closed = new AtomicBoolean();

initPropertySources() 方法其实是一个预留下的空方法:

//AbstractApplicationContext.classprotected void initPropertySources() {    }

getEnvironment() 在之前的代码里看到过,用来获取一个新创建出来的 StandardEnvironment 对象,而 validateRequiredProperties() 是定义在 AbstractEnvironment 中的方法:

//AbstractEnvironment.classpublic void validateRequiredProperties() throws MissingRequiredPropertiesException {    this.propertyResolver.validateRequiredProperties();}

propertyResolver 是一个定义在 AbstractEnvironment 中的 PropertySourcesPropertyResolver 对象,顾名思义,是用来管理 properties 配置文件中读取到的值的。来看一下 validateRequiredProperties():

//AbstractPropertyResolver.classpublic void validateRequiredProperties() {    //新建了一个 Exception,用于在出错情况下进行返回    MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();    //这里的字面意思上可以看出,这个 for 循环语句用于从 requiredProperties 这个集合里获取每个 properties key    for (String key : this.requiredProperties) {        //getProperty(key) 方法会用 key 去获取 value 值,然后进行空值比对        if (this.getProperty(key) == null) {            //如果存在 null,则加入到错误信息里            ex.addMissingRequiredProperty(key);        }    }    //错误信息不为空,证明上述代码中存在值为 null 的,就直接抛出异常    if (!ex.getMissingRequiredProperties().isEmpty()) {        throw ex;    }}

4.4

看下方代码片段:

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

追踪 obtainFreshBeanFactory() 方法:

//AbstractApplicationContext.classprotected ConfigurableListableBeanFactory obtainFreshBeanFactory() {    refreshBeanFactory();    return getBeanFactory();}

追踪 refreshBeanFactory() 方法的内部实现:

//GenericApplicationContext.classprotected final void refreshBeanFactory() throws IllegalStateException {    if (!this.refreshed.compareAndSet(false, true)) {        //抛出错误,提示大意为:已经调用过一次 "refresh" 了        throw new IllegalStateException(                "GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");    }    //存入一个用于序列化的 id    this.beanFactory.setSerializationId(getId());}

refreshed 是一个定义在 GenericApplicationContext 中的 AtomicBoolean 类型对象。AtomicBoolean 的 compareAndSet(...) 方法等同于进行如下操作:

//比较 AtomicBoolean 当前的值和第一个参数是否相等,如果一致,则将 AtomicBoolean 当前的值换成第二个值。//以下为模拟代码,AtomicBoolean 的真实实现大多数是使用虚拟机底层代码完成,是原子化的操作if(atomicBoolean == false){    atomicBoolean = true;    return true;}else{    return false;}

this.beanFactory.setSerializationId(getId()) 会将 AbstractApplicationContext 内的 id 存入一个定义在 DefaultListableBeanFactory 中的 map 对象里。

4.5

看下方代码片段:

prepareBeanFactory(beanFactory);

追踪代码实现:

//AbstractApplicationContext.classprotected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {        //存入 AnnotationConfigApplicationContext 中的 classLoader    beanFactory.setBeanClassLoader(getClassLoader());    //StandardBeanExpressionResolver 用于解析 Spring EL 表达式的解析器    beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));    //ResourceEditorRegistrar 用于各种 bean 与 String 之间进行转换的属性编辑器    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));    //ApplicationContextAwareProcessor 用于注入各类 aware bean     beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));    //在自动装配(autowire)阶段要忽略的接口类    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.registerResolvableDependency(BeanFactory.class, beanFactory);    beanFactory.registerResolvableDependency(ResourceLoader.class, this);    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);    beanFactory.registerResolvableDependency(ApplicationContext.class, this);    //ApplicationListenerDetector 用于类型是 ApplicationListener 的bean添加到事件广播器    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));    //代码动态织入    //LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver"    if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {        //LoadTimeWeaverAwareProcessor 用于在 bean 初始化之前检查 bean 是否实现了LoadTimeWeaverAware 接口        //与 Spring aop 代码织入相关的 BeanPostProcessor        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));        //用于类型匹配的 classloader        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));    }    //注册 properties 和 environment 相关的 bean    //这里不仅会注册,而且会直接将这些 bean 存放到 singleObject 中(即为直接实例化出来)    if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {        beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());    }    if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {        beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());    }    if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {        beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());    }}

代码动态织入是 Spring 底层 cglib 的相关概念,暂时不展开。

4.6

看下方代码片段:

postProcessBeanFactory(beanFactory);

在 AbstractApplicationContext 中该方法是一个空方法:

//AbstractApplicationContext.classprotected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {}

该方法预留给 AbstractApplicationContext 的子类去实现,目的是在 bean factory 装配完成之后做一些定制化处理 AbstractApplicationContext。在 AnnotationConfigApplicationContext 及其父类中没有重写该方法。

4.7

看下方代码片段:

invokeBeanFactoryPostProcessors(beanFactory);

追踪代码实现:

//AbstractApplicationContext.classprotected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());    if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));    }}

这段代码和上述 4.4 中的一段几乎是一摸一样的,Spring 在这里做了二次验证。

4.8

看下方代码片段:

registerBeanPostProcessors(beanFactory);

从字面意思可以看出就是在 beanFactory 中注册 BeanPostProcessor。注册的本质是将 BeanPostProcessor 保存到一个列表里。从源码里看,该列表是定义在 AbstractBeanFactory 中的 beanPostProcessors。

追踪代码实现:

//AbstractApplicationContext.classprotected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {    PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);}

继续追踪:

//PostProcessorRegistrationDelegate.classpublic static void registerBeanPostProcessors(            ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {    //根据 class 获取到所有符合的 bean,即 BeanPostProcessor 的子类    String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);    //beanFactory.getBeanPostProcessorCount() 获取到的数字是在 4.4 中 set 的 BeanPostProcessor 的数量    int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;    //BeanPostProcessorChecker 是 PostProcessorRegistrationDelegate 的私有静态内部类    //如果一个 bean 没有被所有的 BeanPostProcessor 处理完毕,BeanPostProcessorChecker 就会打印日志    beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));    //处理 Order 相关接口    //Order 是一个用于排序的接口    //用于存放实现了 PriorityOrdered 接口的 BeanPostProcessor    List
priorityOrderedPostProcessors = new ArrayList<>(); //用于存放实现了 MergedBeanDefinitionPostProcessor 接口的 BeanPostProcessor List
internalPostProcessors = new ArrayList<>(); //用于存放实现了 Ordered 接口的 BeanPostProcessor List
orderedPostProcessorNames = new ArrayList<>(); //其它 BeanPostProcessor List
nonOrderedPostProcessorNames = new ArrayList<>(); for (String ppName : postProcessorNames) { //isTypeMatch(...) 方法会判断该名称的 bean 和 class 类型是否一致 //这里查看是否实现了 PriorityOrdered 接口 if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { //获取 bean 并添加到一个列表中 BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); priorityOrderedPostProcessors.add(pp); if (pp instanceof MergedBeanDefinitionPostProcessor) { //继续判断是否实现了 MergedBeanDefinitionPostProcessor 接口 internalPostProcessors.add(pp); } }else if (beanFactory.isTypeMatch(ppName, Ordered.class)) { //如果并非实现了 PriorityOrdered 接口就判断是否实现了 Ordered 接口 orderedPostProcessorNames.add(ppName); }else { //均无,则存入 nonOrderedPostProcessorNames 列表中 nonOrderedPostProcessorNames.add(ppName); } } //对于实现了 PriorityOrdered 接口的 BeanPostProcessor 进行排序 sortPostProcessors(priorityOrderedPostProcessors, beanFactory); //依次注册这些 BeanPostProcessor registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors); //下方代码块和上方很雷同,注册实现了 Ordered 接口的 BeanPostProcessor List
orderedPostProcessors = new ArrayList<>(); 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); //注册没有实现任何排序接口的 BeanPostProcessor List
nonOrderedPostProcessors = new ArrayList<>(); for (String ppName : nonOrderedPostProcessorNames) { BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); nonOrderedPostProcessors.add(pp); if (pp instanceof MergedBeanDefinitionPostProcessor) { internalPostProcessors.add(pp); } } registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors); //对于实现了 MergedBeanDefinitionPostProcessor 接口的 BeanPostProcessor 会统一进行排序并注册 sortPostProcessors(internalPostProcessors, beanFactory); registerBeanPostProcessors(beanFactory, internalPostProcessors); //ApplicationListenerDetector 用于在 bean 初始化后检查是否实现了 ApplicationListener 接口 //从代码来看,实现了该接口的 bean 会被存入一个叫 applicationListeners 的列表中 beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));}

综合来看这个方法的主体是对 BeanPostProcessor 进行依次的存储(会影响到 BeanPostProcessor 的执行顺序)。

次序的依据主要是看这些 BeanPostProcessor 是否实现了 Order 及其相关的接口。

4.9

看下方代码片段:

initMessageSource();

追踪代码实现:

//AbstractApplicationContext.classprotected void initMessageSource() {    //获取 beanFactory    ConfigurableListableBeanFactory beanFactory = getBeanFactory();    //MESSAGE_SOURCE_BEAN_NAME = "messageSource"    //先去查看 beanFactory 中是否有该名称的 bean    if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {        //获取到这个 bean        this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);        //主体思路是将数据源存入这个 bean 中        if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {            HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;            if (hms.getParentMessageSource() == null) {                //存入默认的数据源                //getInternalParentMessageSource() 方法会先                hms.setParentMessageSource(getInternalParentMessageSource());            }        }        if (logger.isTraceEnabled()) {            logger.trace("Using MessageSource [" + this.messageSource + "]");        }    }else {        //没有这个 bean 的情况下就自定义一个,并存入到 beanFactory 中        DelegatingMessageSource dms = new DelegatingMessageSource();        dms.setParentMessageSource(getInternalParentMessageSource());        this.messageSource = dms;        beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);        if (logger.isTraceEnabled()) {            logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]");        }    }}

Spring 中可以配置多套配置文件,之间通过 messageSource 进行切换。

4.10

看下方代码片段:

initApplicationEventMulticaster();

追踪代码实现:

//AbstractApplicationContext.classprotected void initApplicationEventMulticaster() {    ConfigurableListableBeanFactory beanFactory = getBeanFactory();    //APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster"    //检查是否存在名称为 applicationEventMulticaster 的 bean    if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {        //如果存在则赋值给 applicationEventMulticaster        this.applicationEventMulticaster =                beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);        if (logger.isTraceEnabled()) {            logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");        }    }else {        //没有的话就创建一个,并注册与赋值        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);        if (logger.isTraceEnabled()) {            logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +                    "[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");        }    }}

applicationEventMulticaster 是一个观察者模式的应用,可以理解为是用来分发数据给监听器(Listener)的中转站。

4.11

看下方代码片段:

onRefresh();

默认该方法为空:

//AbstractApplicationContext.classprotected void onRefresh() throws BeansException {}

预留用于处理特殊的 bean。

4.12

registerListeners();

追踪代码实现:

//AbstractApplicationContext.classprotected void registerListeners() {        //遍历 applicationListeners 列表    //往 applicationEventMulticaster 内部的列表 applicationListeners 里添加    //applicationListener 用于在容器初始化时期监听事件    for (ApplicationListener
listener : getApplicationListeners()) { getApplicationEventMulticaster().addApplicationListener(listener); } //获取可能存在的使用者自行定义的监听器,同样添加到列表里 String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false); for (String listenerBeanName : listenerBeanNames) { getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName); } //获取所有的 event,放到一个列表里 Set
earlyEventsToProcess = this.earlyApplicationEvents; this.earlyApplicationEvents = null; if (earlyEventsToProcess != null) { for (ApplicationEvent earlyEvent : earlyEventsToProcess) { getApplicationEventMulticaster().multicastEvent(earlyEvent); } }}

特定的监听器用于监听某一类的 event,在容器启动时生效,对 event 做出处理。

监听器被储存在 applicationEventMulticaster 中,发生事件的时候被告知。

4.13

finishBeanFactoryInitialization(beanFactory);

追踪代码实现:

//AbstractApplicationContext.classprotected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {        //CONVERSION_SERVICE_BEAN_NAME = "conversionService"    //conversionService 这个 bean 用于映射数据类型,比如将前端的字符串类型数据转成日期等    //由使用者自主实现并配置,默认情况下没有这个 bean    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));        }    //如果 beanFactory 里没有 embeddedValueResolver,就会从 environment 里获取并加载到里面    //embeddedValueResolver 是和配置文件读取相关的组件    if (!beanFactory.hasEmbeddedValueResolver()) {        beanFactory.addEmbeddedValueResolver((strVal) -> {            return this.getEnvironment().resolvePlaceholders(strVal);        });    }    //以下代码用于提前实例化代码织入相关的 bean    String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);    String[] var3 = weaverAwareNames;    int var4 = weaverAwareNames.length;    for(int var5 = 0; var5 < var4; ++var5) {        String weaverAwareName = var3[var5];        this.getBean(weaverAwareName);    }    //将 classLoader 置空,因为已经用不到了,所以清除掉,节省内存    beanFactory.setTempClassLoader((ClassLoader)null);    //这个方法会将 beanName 列表转换成一个字符串数组,节省内存    beanFactory.freezeConfiguration();    //实例化 bean 的核心方法    beanFactory.preInstantiateSingletons();}

来看一下 beanFactory.preInstantiateSingletons() 方法:

//DefaultListableBeanFactory.classpublic void preInstantiateSingletons() throws BeansException {    if (logger.isTraceEnabled()) {        logger.trace("Pre-instantiating singletons in " + this);    }    //将 beanName 列表拷贝一份    List
beanNames = new ArrayList<>(this.beanDefinitionNames); //遍历 for (String beanName : beanNames) { //获取每个 bean RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); //需要最终确认 bean 不是抽象类,且为单例的,且不是惰性加载的 if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { //判断其是否实现了 FactoryBean 接口 //FactoryBean 是 Spring 留出的可供使用者选择的用于生产 bean 的工厂模式接口 if (isFactoryBean(beanName)) { Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); //二次判断 if (bean instanceof FactoryBean) { final FactoryBean
factory = (FactoryBean
) bean; //是否期望被 init boolean isEagerInit; //系统存在权限问题的时候才会进入到这个判断语句中 if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { //AccessController.doPrivileged(...) 方法用于 java 的权限控制 //此处调用了此方法用于剔除权限对 Spring 的控制 //此处与下方代码都调用了 SmartFactoryBean 接口的 isEagerInit() 方法进行判断 isEagerInit = AccessController.doPrivileged((PrivilegedAction
) ((SmartFactoryBean
) factory)::isEagerInit, getAccessControlContext()); }else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean
) factory).isEagerInit()); } //实例化 if (isEagerInit) { getBean(beanName); } } }else { //其实对于绝大多数普通的 bean,是直接调用这个语句进行实例化的 getBean(beanName); } } } //此处再次循环获取 bean ,用于对实现了 SmartInitializingSingleton 接口的 bean 进行特殊操作 //SmartInitializingSingleton 接口内有一个 afterSingletonsInstantiated() 方法,会在完成 bean 的实例化之后执行 for (String beanName : beanNames) { Object singletonInstance = getSingleton(beanName); if (singletonInstance instanceof SmartInitializingSingleton) { final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance; if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction
) () -> { smartSingleton.afterSingletonsInstantiated(); return null; }, getAccessControlContext()); }else { smartSingleton.afterSingletonsInstantiated(); } } }}

4.14

所以最终实例化 bean 的是 getBean(...) 方法,此方法不仅可以用于从容器中取出 bean,也是实例化 bean 的具体实现。

追踪其具体代码实现:

//AbstractBeanFactory.classpublic Object getBean(String name) throws BeansException {    return doGetBean(name, null, null, false);}

继续来看 doGetBean(...) 方法:

//AbstractBeanFactory.classprotected 
T doGetBean(final String name, @Nullable final Class
requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { final String beanName = transformedBeanName(name); Object bean; //对于之前已经实例过的 bean,会在这个方法里获取到 //如果没有实例化过,那么此处获取的是 null Object sharedInstance = getSingleton(beanName); //args 是用于实例化 bean 过程中传入构造器的参数 //此处传入的 args 为 null if (sharedInstance != null && args == null) { if (logger.isTraceEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference"); }else { logger.trace("Returning cached instance of singleton bean '" + beanName + "'"); } } //此方法用于处理 FactoryBean 的情况,如果没有的话是直接返回 sharedInstance 的 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); }else { //如果 bean 正在创建,会抛出异常 if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } //先取出父类中存放的 parentBeanFactory,如果有实现的话就用该工厂来创建 bean //本例中没有使用 parentBeanFactory BeanFactory parentBeanFactory = getParentBeanFactory(); if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { String nameToLookup = originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory) { return ((AbstractBeanFactory) parentBeanFactory).doGetBean( nameToLookup, requiredType, args, typeCheckOnly); }else if (args != null) { return (T) parentBeanFactory.getBean(nameToLookup, args); }else if (requiredType != null) { return parentBeanFactory.getBean(nameToLookup, requiredType); }else { return (T) parentBeanFactory.getBean(nameToLookup); } } //这里会将 bean 标记为已经创建 if (!typeCheckOnly) { markBeanAsCreated(beanName); } try { //获取 bean 的包装类 final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); //bean 不能是一个 abstract 修饰的类,否则会报错 checkMergedBeanDefinition(mbd, beanName, args); //depends-on 用来处理要实例化某个 bean,必须先实例化另一个 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 + "'"); } //注册要依赖的 bean registerDependentBean(dep, beanName); try { //实例化要依赖的 bean getBean(dep); }catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } //是否是单例的 if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { //实例化 bean 的核心方法 return createBean(beanName, mbd, args); }catch (BeansException ex) { //如果报错的话会销毁掉这个 bean destroySingleton(beanName); throw ex; } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }else if (mbd.isPrototype()) { //如果 scope 注解为 prototype,则每次获取 bean 的时候都会创建新的 bean 实例 Object prototypeInstance = null; try { beforePrototypeCreation(beanName); prototypeInstance = createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); }else { //获取 scope 注解的值,如果是 null 的话会报错 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, () -> { 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; } } //requiredType 是使用者传入的 class 对象,使用者可以将 bean 转换成该类型并输出 if (requiredType != null && !requiredType.isInstance(bean)) { try { T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType); if (convertedBean == null) { throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } return convertedBean; }catch (TypeMismatchException ex) { if (logger.isTraceEnabled()) { logger.trace("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", ex); } throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } } //返回 bean return (T) bean;}

继续追踪 createBean(...) 方法:

//AbstractAutowireCapableBeanFactory.classprotected 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,然后在再次确定了 bean 的 class 无误之后,克隆一份 bean 并存入 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 { //根据注释来看,这个方法是使用 BeanPostProcessor 去进行动态代理 //如果存在对应的 BeanPostProcessor,返回的是动态代理的类,而不是 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(...) 方法:

//AbstractAutowireCapableBeanFactory.classprotected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)            throws BeanCreationException {    //BeanWrapper 是 bean 的另一种包装    BeanWrapper instanceWrapper = null;    if (mbd.isSingleton()) {        //factoryBeanInstanceCache 是一个 map 对象,用于缓存 wrapper        //此处从缓存中删除并返回此 wrapper        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);    }    //如果没有的话会创建一个    if (instanceWrapper == null) {        //这一步会实例化 bean        instanceWrapper = createBeanInstance(beanName, mbd, args);    }    //获取 bean 实例    final Object bean = instanceWrapper.getWrappedInstance();    //获取 class    Class
beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { mbd.resolvedTargetType = beanType; } synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { //该方法会轮询 BeanPostProcessor 列表进行 bean 的增强操作 applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); }catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.postProcessed = true; } } //是否是单例,是否允许循环引用,该单例 bean 是否在创建中 //isSingletonCurrentlyInCreation(beanName) 方法会去集合 singletonsCurrentlyInCreation 中搜寻 beanName //如果存在,证明该 bean 正在被初始化,初始化完成之后会从该集合中删除掉,功能相当于锁 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"); } //将 beanName 注册到 singletonFactories 和 registeredSingletons 中 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } //添加一个 bean 的引用 exposedObject Object exposedObject = bean; try { //该方法会根据配置来填充 mbd 中 populateBean(beanName, mbd, instanceWrapper); //将信息填充到 exposedObject 中 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) { //getSingleton(...) 方法的第二个参数代表是否允许循环引用,这里输入的是 false(不允许) //对于一般的 bean,这里获取到的 earlySingletonReference = null Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; }else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set
actualDependentBeans = new LinkedHashSet<>(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."); } } } } //注册需要执行销毁方法的 bean try { registerDisposableBeanIfNecessary(beanName, bean, mbd); }catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject;}

继续追踪 createBeanInstance(...) 方法:

//AbstractAutowireCapableBeanFactory.classprotected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {    //获取到 bean 的 class    Class
beanClass = resolveBeanClass(mbd, beanName); //如果 class 不为空,且修饰语非 public,且没有设置 accessible = true if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non-public access not allowed: " + beanClass.getName()); } //如果 BeanDefinition 中有保存 Supplier,则使用该方式去获取 bean //Supplier 是 jdk8 中配合函数式编程所添加的用于获取 bean 的接口 //Supplier 每次获取的 bean 都不是同一个 Supplier
instanceSupplier = mbd.getInstanceSupplier(); if (instanceSupplier != null) { return obtainFromSupplier(instanceSupplier, beanName); } //如果有工厂方法,就用工厂方法进行实例化 if (mbd.getFactoryMethodName() != null) { return instantiateUsingFactoryMethod(beanName, mbd, args); } boolean resolved = false; boolean autowireNecessary = false; if (args == null) { synchronized (mbd.constructorArgumentLock) { //查看是否缓存了构造器 if (mbd.resolvedConstructorOrFactoryMethod != null) { resolved = true; //这里要判断构造器的参数是否使用了 Autowire 注解 autowireNecessary = mbd.constructorArgumentsResolved; } } } if (resolved) { if (autowireNecessary) { //如果使用了 Autowire 注解就会进入这个方法进行实例化 return autowireConstructor(beanName, mbd, null, null); }else { //常规的实例化 bean return instantiateBean(beanName, mbd); } } //如果一个继承了 BeanPostProcessor 接口的 bean 的构造器参数使用了 Autowire 注解,会在此处单独处理 Constructor
[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args); } //获取首选的构造器 //在该版本的 RootBeanDefinition 里,该方法没有方法体,直接返回 null //也就是说,该代码是不启用的 ctors = mbd.getPreferredConstructors(); if (ctors != null) { return autowireConstructor(beanName, mbd, ctors, null); } //实例化 bean return instantiateBean(beanName, mbd);}

继续追踪 instantiateBean(...) 方法:

//AbstractAutowireCapableBeanFactory.classprotected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {    try {        Object beanInstance;        final BeanFactory parent = this;        //再次检测权限环境        //此例中没有涉及        if (System.getSecurityManager() != null) {            beanInstance = AccessController.doPrivileged((PrivilegedAction) () ->                    getInstantiationStrategy().instantiate(mbd, beanName, parent),                    getAccessControlContext());        }else {            //实例化 bean            beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);        }        //将 bean 包装成 BeanWrapper 并返回        BeanWrapper bw = new BeanWrapperImpl(beanInstance);        initBeanWrapper(bw);        return bw;    }catch (Throwable ex) {        throw new BeanCreationException(                mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);    }}

继续追踪 instantiate(...) 方法:

//SimpleInstantiationStrategy.classpublic Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {    //不需要方法重载,就不需要 cglib 帮助了    if (!bd.hasMethodOverrides()) {        Constructor
constructorToUse; synchronized (bd.constructorArgumentLock) { //先尝试查看 BeanDefinition 中是否保存了构造器,如果没有保存,就在下方从 class 中拿出来 constructorToUse = (Constructor
) bd.resolvedConstructorOrFactoryMethod; if (constructorToUse == null) { final Class
clazz = bd.getBeanClass(); if (clazz.isInterface()) { //bean 不能是一个接口 throw new BeanInstantiationException(clazz, "Specified class is an interface"); } try { //这里验证权限设置 if (System.getSecurityManager() != null) { constructorToUse = AccessController.doPrivileged( (PrivilegedExceptionAction
>) clazz::getDeclaredConstructor); }else { //获取构造器 constructorToUse = clazz.getDeclaredConstructor(); } bd.resolvedConstructorOrFactoryMethod = constructorToUse; }catch (Throwable ex) { throw new BeanInstantiationException(clazz, "No default constructor found", ex); } } } //实例化 bean return BeanUtils.instantiateClass(constructorToUse); }else { //使用 cglib 进行 bean 的实例化 return instantiateWithMethodInjection(bd, beanName, owner); }}

此小节是整个实例化 bean 过程中最冗长,也最核心的部分。

这中间 Spring 做了大量的验证和业务判断,但是实际上最终 bean 的实例化并不复杂。

4.15

finishRefresh();

追踪代码实现:

//AbstractApplicationContext.classprotected void finishRefresh() {    //清空缓存,即将 resourceCaches 这个 map 对象置空    clearResourceCaches();    //实例化一个类型为 DefaultLifecycleProcessor 的 bean,用于控制 bean 的生命周期    initLifecycleProcessor();    getLifecycleProcessor().onRefresh();    //刷新上下文事件    //ContextRefreshedEvent 并没有业务逻辑    publishEvent(new ContextRefreshedEvent(this));    //此方法会将 applicationContext 保存到 LiveBeansView 中的一个集合内    LiveBeansView.registerApplicationContext(this);}

到此为止,正常的 ApplicationContext 的初始化流程就完成了。

4.16

看下方代码片段:

destroyBeans();

这个方法的实现:

//AbstractApplicationContext.classprotected void destroyBeans() {    getBeanFactory().destroySingletons();}

destroySingletons() 是定义在 DefaultListableBeanFactory 中的方法:

//DefaultListableBeanFactory.classpublic void destroySingletons() {    super.destroySingletons();    //manualSingletonNames 是一个用来存放已经被创建的单例 bean 的名字的 Set集合    this.manualSingletonNames.clear();    clearByTypeCache();}

这里的 super.destroySingletons() 调用的是其父类 DefaultSingletonBeanRegistry 中的方法:

//DefaultSingletonBeanRegistry.classpublic void destroySingletons() {    if (logger.isTraceEnabled()) {        logger.trace("Destroying singletons in " + this);    }    synchronized (this.singletonObjects) {        this.singletonsCurrentlyInDestruction = true;    }    String[] disposableBeanNames;    synchronized (this.disposableBeans) {        disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());    }    for (int i = disposableBeanNames.length - 1; i >= 0; i--) {        destroySingleton(disposableBeanNames[i]);    }    this.containedBeanMap.clear();    this.dependentBeanMap.clear();    this.dependenciesForBeanMap.clear();        //清空相关的集合内的引用对象,并且销毁已经创建的 bean    clearSingletonCache();}

再来看一下 clearByTypeCache() 方法:

//DefaultListableBeanFactory.classprivate void clearByTypeCache() {    this.allBeanNamesByType.clear();    this.singletonBeanNamesByType.clear();}

4.17

看下方代码片段:

cancelRefresh(ex);

这个方法的实现:

//AbstractApplicationContext.classprotected void cancelRefresh(BeansException ex) {    //传入的 exception 其实并没有用到    this.active.set(false);}

active 是定义在 AbstractApplicationContext 中的一个 AtomicBoolean:

private final AtomicBoolean active = new AtomicBoolean();

这里将其的值设置为 false。

4.18

看下方代码片段:

resetCommonCaches();

具体实现

//AbstractApplicationContext.classprotected void resetCommonCaches() {    //主要是将 util 类里的一些 static 修饰的集合和 map 对象进行置空。    ReflectionUtils.clearCache();    AnnotationUtils.clearCache();    ResolvableType.clearCache();    CachedIntrospectionResults.clearClassLoader(getClassLoader());}

从 4.16 到 4.18 的代码总体都比较简单,就是把各种的相关的集合、map、列表都清空掉,将 bean 的引用对象也都 remove 掉。

实际上是优雅关闭的过程。

二 一点唠叨

· Spring 的代码实在是太庞大了,刚开始想要尽可能详细的去解释每个组件,但是后来觉得难度略大

· Spring 的小部分组件和代码在笔者看来是有些莫名其妙的,且都没有查到详细而合理的解释

· Spring 的代码很明显使用了防御性的编程原则,保证了每个方法都足够健壮,但是同时造成了重复验证和冗余

· Spring 显然非常强调易用性和泛用性,提供了繁多的功能,甚至有部分是显得过于灵活的,如果笔者未读源码大概一辈子都不会知道

· Spring 对组件状态的控制异常复杂且精确

· Spring 对内存的控制很苛刻

· 仅为个人的学习笔记,可能存在错误或者表述不清的地方,有缘补充

转载地址:http://ycrsl.baihongyu.com/

你可能感兴趣的文章
我希望一年前就知道 MongoDB 的那些事儿
查看>>
《Spark 官方文档》Spark独立模式
查看>>
《树莓派Python编程入门与实战(第2版)》——1.5 决定如何购买外围设备
查看>>
完全指南之在 Ubuntu 操作系统中安装及卸载软件
查看>>
《Spark 官方文档》在YARN上运行Spark
查看>>
《C++面向对象高效编程(第2版)》——2.5 数据封装的优点
查看>>
判断email格式的正则表达式
查看>>
HTTP Referer 二三事
查看>>
《策略驱动型数据中心——ACI技术详解》——导读
查看>>
SPDY 是什么?如何部署 SPDY?
查看>>
WebSocket实现网页聊天室
查看>>
《无人机DIY》——3.2 大疆Phantom 2 Vision+
查看>>
《Flink官方文档》Python 编程指南测试版(二)
查看>>
Linux有问必答:如何在VMware ESXi虚拟机上设置静态MAC地址
查看>>
《Unity 游戏案例开发大全》一6.1 背景以及功能概述
查看>>
《C++代码设计与重用》——2.6 接口一致性
查看>>
《AngularJS高级程序设计》——2.4 小结
查看>>
Spark Streaming + Spark SQL 实现配置化ETL流程
查看>>
算法之冒泡排序
查看>>
袋鼠云成企业服务潜力新星 云市场生态持续发酵
查看>>