文档章节

Spring源码解析(七)——实例创建(中)

MarvelCode
 MarvelCode
发布于 06/23 15:12
字数 2876
阅读 39
收藏 1

前言

    上一节讲到了,Spring 会根据实例的作用域执行不同的创建逻辑,分别是 Singleton、Prototype、其他 Scope,其中 Singleton 会调用 getSingleton 从缓存中获取,缓存中没有才会创建实例;Prototype 每次都会创建;其他 Scope 会调用 Scope.get 获取,保证各作用域内实例唯一。

    它们的公共创建逻辑都是  createBean 方法。

 

源码解读

    createBean 方法由 AbstractAutowireCapableBeanFactory 实现。

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
        implements AutowireCapableBeanFactory {

    // bean默认实例化策略 —— Ciglib增强代理
    private InstantiationStrategy instantiationStrategy = 
                              new CglibSubclassingInstantiationStrategy();

    @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;

        // 解析获取 bean对应类的 Class对象,并拷贝一份 RootBeanDefinition
        Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
        if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
            mbdToUse = new RootBeanDefinition(mbd);
            mbdToUse.setBeanClass(resolvedClass);
        }

        /**
         * 对于 methodOverrides的校验,即 lookup-method和 replaced-method
         * 如果指定的方法不存在,会抛出异常
         */
        try {
            mbdToUse.prepareMethodOverrides();
        } catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
                    beanName, "Validation of method overrides failed", ex);
        }

        try {
            // 让 BeanPostProcessors有机会返回代理而不是目标 bean实例
            Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
            if (bean != null) {
                return bean;
            }
        } catch (Throwable ex) {
            throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
                    "BeanPostProcessor before instantiation of bean failed", ex);
        }

        // 创建目标 bean实例
        Object beanInstance = doCreateBean(beanName, mbdToUse, args);
        if (logger.isDebugEnabled()) {
            logger.debug("Finished creating instance of bean '" + beanName + "'");
        }
        return beanInstance;
    }
}

    首先会解析出要创建实例的类型,之后是对 methodOverrides 的校验

  • lookup-method :用于将 bean 的指定方法(name属性),配置指定返回哪些实例(bean属性);
  • replaced-method :用于将 bean 的指定方法(name属性),替换为——“实现MethodReplacer”的对象(replacer属性),即方法替换为实现的 reimplement 方法。

    这些替换的前提就是,被指定的方法一定要存在,这就是校验的目的。之后就有两条线了,一个是代理路线(resolveBeforeInstantiation),另一个是常规的实例创建路线(doCreateBean)。

 

resolveBeforeInstantiation

    protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
        Object bean = null;
        if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
            // 判断 bean是否为合成的(即不是应用本身定义的),例如 <aop-config>创建的
            // 判断全局是否注册有 InstantiationAwareBeanPostProcessor(BeanPostProcessor子类)
            if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
                // 获取 bean的目标类型
                Class<?> targetType = determineTargetType(beanName, mbd);
                if (targetType != null) {
                    // 调用 InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation
                    bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                    // 返回不为 null的实现只有:AbstractAutoProxyCreator和 ScriptFactoryPostProcessor                  
                    if (bean != null) {
                        // 直接调用 BeanPostProcessor.postProcessAfterInitialization 
                        bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                    }
                }
            }
            // 设置是否被处理的标志位
            mbd.beforeInstantiationResolved = (bean != null);
        }
        return bean;
    }

    protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
        // 遍历 InstantiationAwareBeanPostProcessor
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
                // 只要有一个返回不为 null,就会返回
                if (result != null) {
                    return result;
                }
            }
        }
        // 遍历所有 InstantiationAwareBeanPostProcessor
        // 没有一个 postProcessBeforeInstantiation返回不为 null
        return null;
    }


    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;
        // 遍历 BeanPostProcessor
        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
            result = beanProcessor.postProcessAfterInitialization(result, beanName);
            // 和 postProcessBeforeInstantiation相反
            // 只要有一个返回为 null,就会返回
            if (result == null) {
                return result;
            }
        }
        // 遍历所有 BeanPostProcessor
        // 没有一个 postProcessAfterInitialization返回为 null
        return result;
    }

    我们看到了 bean 生命周期相关接口的执行,分别是 postProcessBeforeInstantiation和 postProcessAfterInitialization。前者是在构造器调用前被执行,后者是属性填充后被调用,因为 AOP 代理并非常规的实例创建,少了类似属性填充等过程,所以自然少了与之关联的其他“生命周期”接口调用,在 postProcessBeforeInstantiation 调用后就直接调用 postProcessAfterInitialization 宣告初始化结束。让我们来看看 AOP 代理的逻辑:

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
        implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {

    // 有自定义的 TargetSource直接创建代理对象
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        Object cacheKey = getCacheKey(beanClass, beanName);

        if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
            // 下面判断的缓存
            if (this.advisedBeans.containsKey(cacheKey)) {
                return null;
            }
            // 不代理的条件一:非 Advice、Pointcut、Advisor、AopInfrastructureBean子类
            // 不代理的条件二:shouldSkip返回 true,默认返回 false
            // - AspectJAwareAdvisorAutoProxyCreator:所有 AspectJPointcutAdvisor指定增强 bean应该跳过
            if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
                // 满足条件的不会进行代理处理
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return null;
            }
        }

        // 如果我们有自定义的 TargetSource,就创建代理
        // TargetSource将以自定义的方式处理目标实例
        if (beanName != null) {
            // 由自定义的 TargetSourceCreator.getTargetSource返回,没有则返回 null
            TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
            if (targetSource != null) {
                // 这里会放入缓存,入口就无需再判断
                this.targetSourcedBeans.add(beanName);
                // 返回满足条件的 Advice或者 Advisor集合,涉及两个分支:
                // BeanNameAutoProxyCreator:给名字匹配 bean自动创建代理
                // AbstractAdvisorAutoProxyCreator:给容器中满足 Advisor中指定切点创建代理
                Object[] specificInterceptors =
                        getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
                // 创建代理
                Object proxy =
                        createProxy(beanClass, beanName, specificInterceptors, targetSource);
                this.proxyTypes.put(cacheKey, proxy.getClass());
                // 一旦返回,就不会再进行常规的实例创建了
                return proxy;
            }
        }

        return null;
    }

    protected Object createProxy(Class<?> beanClass, String beanName,
                                 Object[] specificInterceptors, TargetSource targetSource) {

        if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
            AutoProxyUtils.exposeTargetClass(
                    (ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
        }

        // 采用 ProxyFactory来进行代理创建
        ProxyFactory proxyFactory = new ProxyFactory();
        // 复制当前类的相关配置
        proxyFactory.copyFrom(this);

        if (!proxyFactory.isProxyTargetClass()) {
            // 检查 beanDifinition的是否设置 preserveTargetClass为 true
            if (shouldProxyTargetClass(beanClass, beanName)) {
                // 满足条件,使用 TargetClass代理
                // 如果 TargetClass为接口或代理类时,使用JDK代理,否则使用 Ciglib代理
                proxyFactory.setProxyTargetClass(true);
            } else {
                // 看是否有合理的接口,逻辑如下
                evaluateProxyInterfaces(beanClass, proxyFactory);
            }
        }
        // 整理合并Advisor:自定义切面和公共拦截(涉及到先后执行顺序)
        // 公共拦截:interceptorNames,Advice会被包装成 DefaultPointcutAdvisor
        Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
        // 添加切面
        proxyFactory.addAdvisors(advisors);
        proxyFactory.setTargetSource(targetSource);
        // 空实现
        customizeProxyFactory(proxyFactory);

        proxyFactory.setFrozen(this.freezeProxy);
        if (advisorsPreFiltered()) {
            proxyFactory.setPreFiltered(true);
        }
        // 返回代理对象
        return proxyFactory.getProxy(getProxyClassLoader());
    }

    protected void evaluateProxyInterfaces(Class<?> beanClass, ProxyFactory proxyFactory) {
        // 获取类实现的所有接口
        Class<?>[] targetInterfaces =
                ClassUtils.getAllInterfacesForClass(beanClass, getProxyClassLoader());
        boolean hasReasonableProxyInterface = false;
        // 遍历所有接口,找出合理的接口
        for (Class<?> ifc : targetInterfaces) {
            // 条件一:接口非 InitializingBean、DisposableBean、Closeable、AutoCloseable、父接口非 Aware
            // 条件二:接口非 groovy.lang.GroovyObject、cglib.proxy.Factory、bytebuddy.MockAccess
            // 条件三:接口方法至少一个
            if (!isConfigurationCallbackInterface(ifc) && !isInternalLanguageInterface(ifc) &&
                    ifc.getMethods().length > 0) {
                hasReasonableProxyInterface = true;
                break;
            }
        }
        if (hasReasonableProxyInterface) {
            for (Class<?> ifc : targetInterfaces) {
                proxyFactory.addInterface(ifc);
            }
        } else {
            // 没有合理的接口,使用 TargetClass代理
            proxyFactory.setProxyTargetClass(true);
        }
    }

}

    以上是有自定义 TargetSource 自动代理的创建过程,如果没有自定义 TargetSource,就需要在常规实例创建过程中(见下节 initializeBean),走到 postProcessAfterInitialization 来进行代理创建:

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
        implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (!this.earlyProxyReferences.contains(cacheKey)) {
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }

    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        // 这里缓存逻辑同 postProcessBeforeInstantiation
        if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // 同样获取满足条件的 Advice或者 Advisor集合
        Object[] specificInterceptors =
                getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            // 同样调用 createProxy创建代理
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors,
                    new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

}

 

doCreateBean

    接下来就是常规实例的创建概览了。

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

        BeanWrapper instanceWrapper = null;
        // 如果是单例,首先要取出未完成的FactoryBean实例的缓存
        if (mbd.isSingleton()) {
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }
        // 如果没有缓存,根据指定 bean使用对应策略创建新实例,将 BeanDefinition转化为 BeanWrapper
        if (instanceWrapper == null) {
            // 会调用构造器来实例化对象,展开此方法
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
        Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
        mbd.resolvedTargetType = beanType;

        // 后处理器修改合并的 bean定义.
        // 锁对象,防并发
        synchronized (mbd.postProcessingLock) {
            if (!mbd.postProcessed) {
                try {
                    // 生命周期:MergedDefinitionPostProcessors.postProcessMergedBeanDefinition
                    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在创建中,检测循环依赖
        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");
            }
            /**
             * 为避免后期循环依赖,在bean初始化完成前将创建实例的 ObjectFactory放入工厂
             * 循环依赖时仅通过 ObjectFactory创建实例即可
             */
            addSingletonFactory(beanName, new ObjectFactory<Object>() {
                @Override
                public Object getObject() throws BeansException {
                    /**
                     * 生命周期:SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference
                     * Aop就是在此将 advice动态植入 bean,如果没有则直接返回 bean
                     */
                    return getEarlyBeanReference(beanName, mbd, bean);
                }
            });
        }

        Object exposedObject = bean;
        try {
            /**
             * 生命周期:InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation
             * 自动装配(区分 byName、byType)
             * 生命周期:InstantiationAwareBeanPostProcessor.postProcessPropertyValues
             * 属性填充
             */
            populateBean(beanName, mbd, instanceWrapper);
            if (exposedObject != null) {
                /**
                 * BeanNameAware、BeanClassLoaderAware、BeanFactoryAware
                 * 生命周期:BeanPostProcessor.postProcessBeforeInitialization
                 * 生命周期:InitializingBean.afterPropertiesSet
                 * 生命周期:调用初始化方法:init-method
                 * 生命周期:BeanPostProcessor的postProcessAfterInitialization
                 */
                exposedObject = initializeBean(beanName, exposedObject, mbd);
            }
        }......// 省略 catch

        /**
         * 提前曝光处理:父类 DefaultSingletonBeanRegistry持有 earlySingletonObjects
         * 存储了在创建 bean早期对创建的原始 bean的一个引用
         * getSingleton会查询 earlySingletonObjects时候包含了 beanName
         * 因此如果返回不为空说明检测到了循环依赖
         */
        if (earlySingletonExposure) {
            Object earlySingletonReference = getSingleton(beanName, false);
            if (earlySingletonReference != null) {
                if (exposedObject == bean) {
                    exposedObject = earlySingletonReference;
                }
                // hasDependentBean:判断 dependentBeanMap是否包含该 beanName
                // 见:创建实例(上),doGetBean处理 depends-on,registerDependentBean
                else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                    // 返回依赖于指定 beanName的所有 beanName
                    String[] dependentBeans = getDependentBeans(beanName);
                    Set<String> actualDependentBeans =
                            new LinkedHashSet<String>(dependentBeans.length);
                    for (String dependentBean : dependentBeans) {
                        // alreadyCreated不包含 dependentBean会返回 false
                        // 即 dependentBean依赖的 bean已被创建,但它自身还没完成创建
                        if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                            actualDependentBeans.add(dependentBean);
                        }
                    }
                    // 上面扫描后发现还有依赖 bean未创建完成的会抛出异常
                    if (!actualDependentBeans.isEmpty()) {
                        throw new BeanCurrentlyInCreationException(...// 省略异常信息)
                    }
                }
            }
        }

        // 根据作用域注册bean(这里的bean仅是未进行属性填充的bean)
        try {
            registerDisposableBeanIfNecessary(beanName, bean, mbd);
        } catch (BeanDefinitionValidationException ex) {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
        }

        return exposedObject;
    }

    这一步可以说涵盖了常规实例创建的全过程——实例化Instantiation)和初始化Initialization) ,实例化可以笼统认为是 new 一个对象,初始化可以理解为在实例化的基础上,进行各个成员属性的填充。这一节我们将实例化展开讲解,下一节再对初始化讲解。

 

createBeanInstance

    protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
        // 解析出目标 bean的 Class对象
        Class<?> beanClass = resolveBeanClass(mbd, beanName);

        // isNonPublicAccessAllowed默认返回 true,即非 public的类同样可以实例化
        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());
        }

        // 如果配置了 factory-method,使用工厂方法实例化
        // 见 ConstructorResolver.instantiateUsingFactoryMethod
        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;
                    autowireNecessary = mbd.constructorArgumentsResolved;
                }
            }
        }
        /**
         * 解析过直接从 RootBeanDefinition中属性 resolvedConstructorOrFactoryMethod中取
         * 首次解析后会更新 resolvedConstructorOrFactoryMethod属性(包含工厂方法、构造器注入、默认构造器)
         */
        if (resolved) {
            if (autowireNecessary) {
                // 构造器自动注入
                // 见 ConstructorResolver.autowireConstructor
                return autowireConstructor(beanName, mbd, null, null);
            } else {
                // 使用默认构造器构造(即无参构造器)
                // 见 SimpleInstantiationStrategy.instantiate
                return instantiateBean(beanName, mbd);
            }
        }

        // 首次解析的过程
        // 判断使用哪个构造器:如果实现了 SmartInstantiationAwareBeanPostProcessor,可以通过方法 determineCandidateConstructors指定,否则返回 null
        Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
        if (ctors != null ||
                mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
                mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
            // 构造函数自动注入
            / 见 ConstructorResolver.autowireConstructor
            return autowireConstructor(beanName, mbd, ctors, args);
        }

        // 使用默认构造器(即无参构造器)
        // 见 SimpleInstantiationStrategy.instantiate
        return instantiateBean(beanName, mbd);
    }

    实例化分为三种方式:工厂方法构造器注入、以及默认构造器。具体处理代码集中于 ConstructorResolver SimpleInstantiationStrategy,为了保证整个创建流程的清晰,这里不展开讲解,有兴趣的可以自行研究下。

    经过这一步的处理,bean就已经被实例化了,包装在 BeanWrapper 中,通过调用 getWrappedInstance 就可以获取。

 

总结

    到目前为止创建实例的进度来看,可以简单的理解为,我们已经完成了 new 一个对象的操作,但还未调用 setter 方法为成员变量赋值,这将留到下一节“初始化”进行展开分析。

© 著作权归作者所有

共有 人打赏支持
MarvelCode
粉丝 51
博文 30
码字总数 64261
作品 0
南京
程序员
私信 提问
Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密...

小致dad
08/03
0
0
注册中心 Eureka 源码解析 —— 应用实例注册发现(六)之全量获取

摘要: 原创出处 http://www.iocoder.cn/Eureka/instance-registry-fetch-all/ 「芋道源码」欢迎转载,保留摘要,谢谢! 本文主要基于 Eureka 1.8.X 版本 1. 概述 2. Eureka-Client 发起全量获...

Java公众号_芋道源码_每日更新
10/28
0
0
spring源码-bean之初始化-1

  一、spring的IOC控制反转:控制反转——Spring通过一种称作控制反转(IOC)的技术促进了松耦合。当应用了IOC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建...

小不点丶
08/09
0
0
SpringMVC源码解析(一)——初始化

前言 本系列文章顺延“Spring源码解析”,是在“父容器”创建完成后,对“子容器”(SpringMVC)创建,以及请求处理的解析。 源码解读 说起 SpringMVC,DispatcherServlet 应该是最熟悉的类之...

MarvelCode
06/26
0
0
Spring源码解析系列之IOC容器(一)

前言 实际上我所有的博客都是原来对原来印象笔记里笔记内容的加工,关于Spring源码自己已经解析了很多遍,但是时间长总是忘记,写一篇博客权当加强记忆,也算再次学习下大师们的设计思想,思...

后厂村老司机
06/02
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Spring应用学习——AOP

1. AOP 1. AOP:即面向切面编程,采用横向抽取机制,取代了传统的继承体系的重复代码问题,如下图所示,性能监控、日志记录等代码围绕业务逻辑代码,而这部分代码是一个高度重复的代码,也就...

江左煤郎
今天
2
0
eclipse的版本

Eclipse各版本代号一览表 Eclipse的设计思想是:一切皆插件。Eclipse核心很小,其它所有功能都以插件的形式附加于Eclipse核心之上。 Eclipse基本内核包括:图形API(SWT/Jface),Java开发环...

mdoo
今天
1
0
SpringBoot源码:启动过程分析(一)

本文主要分析 SpringBoot 的启动过程。 SpringBoot的版本为:2.1.0 release,最新版本。 一.时序图 还是老套路,先把分析过程的时序图摆出来:时序图-SpringBoot2.10启动分析 二.源码分析 首...

Jacktanger
今天
3
0
小白带你认识netty(二)之netty服务端启动(上)

上一章 中的标准netty启动代码中,ServerBootstrap到底是如何启动的呢?这一章我们来瞅下。 server.group(bossGroup, workGroup);server.channel(NioServerSocketChannel.class).optio...

天空小小
今天
3
0
聊聊storm trident batch的分流与聚合

序 本文主要研究一下storm trident batch的分流与聚合 实例 TridentTopology topology = new TridentTopology(); topology.newStream("spout1", spout) .p......

go4it
昨天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部