您当前的位置:首页 > 电脑百科 > 程序开发 > 容器

Spring IOC容器对Bean实例化的过程详解源码分析

时间:2021-06-10 09:27:47  来源:  作者:FastCoder

环境:Spring5.3.3


Spring容器启动时,创建
DefaultListableBeanFactory 工厂

实例化
AnnotationConfigApplicationContext对象

public AnnotationConfigApplicationContext(String... basePackages) {
    this();
    scan(basePackages);
    refresh();
}

1.1 实例化单例Bean

进入refresh方法中的
finishBeanFactoryInitialization方法

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    // Initialize conversion service for this context.
    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.
    if (!beanFactory.hasEmbeddedValueResolver()) {
        beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
    }

    // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
    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.
    beanFactory.freezeConfiguration();

    // Instantiate all remaining (non-lazy-init) singletons.
    beanFactory.preInstantiateSingletons();
}

进入
beanFactory.preInstantiateSingletons()方法

这里的beanFactory实例是:
DefaultListableBeanFactory,进入该类的preInstantiateSingletons方法

@Override
public void preInstantiateSingletons() throws BeansException {
    if (logger.isTraceEnabled()) {
        logger.trace("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<>(this.beanDefinitionNames);

    // Trigger initialization of all non-lazy singleton beans...
    for (String beanName : beanNames) {
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
            if (isFactoryBean(beanName)) {
                // ...
            }
            else {
                getBean(beanName);
            }
        }
    }
    // ...
}        

这里会判断是否是工厂Bean(FactoryBean),如果不是进入else语句。

1.2 获取Bean对象

调用父类getBean方法(AbstractBeanFactory)

@Override
public Object getBean(String name) throws BeansException {
    return doGetBean(name, null, null, false);
}
@SuppressWarnings("unchecked")
protected <T> T doGetBean(
        String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
        throws BeansException {

    String beanName = transformedBeanName(name);
    Object beanInstance;

    // ...

    else {
        // ...
        try {
            // ...
            // Create bean instance.
            if (mbd.isSingleton()) {
                sharedInstance = getSingleton(beanName, () -> {
                    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;
                    }
                });
                beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }
            // ...
        }
        catch (BeansException ex) {
            beanCreation.tag("exception", ex.getClass().toString());
            beanCreation.tag("message", String.valueOf(ex.getMessage()));
            cleanupAfterBeanCreationFailure(beanName);
            throw ex;
        }
        finally {
            beanCreation.end();
        }
    }

    return adaptBeanInstance(name, beanInstance, requiredType);
}

1.3 创建Bean实例

核心方法createBean(beanName, mdb, args) ,进入
AbstractAutowireCapableBeanFactory的createBean方法

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException {
    // ...
    try {
        // 通过Bean后处理器返回一个代理的Bean(整个过程会判断是否需要代理)。
        Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
        if (bean != null) {
            return bean;
        }
    }
    // ...
    try {
        Object beanInstance = doCreateBean(beanName, mbdToUse, args);
        if (logger.isTraceEnabled()) {
            logger.trace("Finished creating instance of bean '" + beanName + "'");
        }
        return beanInstance;
    }
    catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
        // A previously detected exception with proper bean creation context already,
        // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
        throw ex;
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
                mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
    }
}           

第一步:


resolveBeforeInstantiation方法通过BeanPostProcessor返回一个代理对象而不是目标对象

protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
    Object bean = null;
    if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
        // Make sure bean class is actually resolved at this point.
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            Class<?> targetType = determineTargetType(beanName, mbd);
            if (targetType != null) {
                bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                if (bean != null) {
                    bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                }
            }
        }
        mbd.beforeInstantiationResolved = (bean != null);
    }
    return bean;
}


applyBeanPostProcessorsBeforeInstantiation方法:

@Nullable
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
    for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
        Object result = bp.postProcessBeforeInstantiation(beanClass, beanName);
        if (result != null) {
            return result;
        }
    }
    return null;
}

这里迭代所有的
InstantiationAwareBeanPostProcessor实例,在开启AOP时会自动注册一个AnnotationAwareAspectJAutoProxyCreator Bean该bean是InstantiationAwareBeanPostProcessor子类

在这里每一个Bean都分别会在实例化前调用
postProcessBeforeInstantiation方法(对象实例化前执行

@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
    Object cacheKey = getCacheKey(beanClass, beanName);

    if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
        if (this.advisedBeans.containsKey(cacheKey)) {
            return null;
        }
        if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return null;
        }
    }

    // Create proxy here if we have a custom TargetSource.
    // Suppresses unnecessary default instantiation of the target bean:
    // The TargetSource will handle target instances in a custom fashion.
    TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
    if (targetSource != null) {
        if (StringUtils.hasLength(beanName)) {
            this.targetSourcedBeans.add(beanName);
        }
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
        Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    return null;
}

isInfrastructureClass返回会判断是否是Advice, Pointcut, Advisor, AopInfrastructureBean如果是这些类那就不会创建Proxy对象。

protected boolean isInfrastructureClass(Class<?> beanClass) {
    boolean retVal = Advice.class.isAssignableFrom(beanClass) ||
            Pointcut.class.isAssignableFrom(beanClass) ||
            Advisor.class.isAssignableFrom(beanClass) ||
            AopInfrastructureBean.class.isAssignableFrom(beanClass);
    if (retVal && logger.isTraceEnabled()) {
        logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]");
    }
    return retVal;
}

如果isInfrastructureClass返回false 那么会执行 shouldSkip方法这个被子类
AspectJAwareAdvisorAutoProxyCreator重写会初始化切面定义相关信息。

@Override
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
    // TODO: Consider optimization by caching the list of the aspect names
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    for (Advisor advisor : candidateAdvisors) {
        if (advisor instanceof AspectJPointcutAdvisor &&
                ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
            return true;
        }
    }
    return super.shouldSkip(beanClass, beanName);
}

如果
AnnotationAwareAspectJAutoProxyCreator 没有配置customTargetSourceCreators那么就原样返回Bean。


resolveBeforeInstantiation返回返回null后会继续向下执行doCreateBean

第二步:

在上一步中如果不能通过BeanPostProcessor返回一个代理对象那么会继续向下执行正常的流程执行doCreateBean方法

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException {
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
        // 创建实例
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    // ...
    try {
        populateBean(beanName, mbd, instanceWrapper);
        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);
        }
    }
}           

创建目标对象的实例

instanceWrapper = createBeanInstance(beanName, mbd, args);

创建实例的方法

protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) {
    try {
        Object beanInstance;
        if (System.getSecurityManager() != null) {
            beanInstance = AccessController.doPrivileged(
                    (PrivilegedAction<Object>) () -> getInstantiationStrategy().instantiate(mbd, beanName, this),
                    getAccessControlContext());
        }
        else {
            // 创建目标对象的实例
            beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this);
        }
        BeanWrapper bw = new BeanWrapperImpl(beanInstance);
        initBeanWrapper(bw);
        return bw;
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
                mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
    }
}

1.4 填充Bean

populateBean方法填充Bean属性,并且在这里会执行BeanPostProcesser 的
postProcessAfterInstantiation方法

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    // ...
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                    return;
                }
            }
        }
    }
    // ...
    PropertyDescriptor[] filteredPds = null;
    if (hasInstAwareBpps) {
        if (pvs == null) {
            pvs = mbd.getPropertyValues();
        }
        for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
            PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
            if (pvsToUse == null) {
                if (filteredPds == null) {
                    filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
                }
                pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                if (pvsToUse == null) {
                    return;
                }
            }
            pvs = pvsToUse;
        }
    }
    // ...
}

这里展示的是核心代码

在这将会执行所有实现了
InstantiationAwareBeanPostProcessor接口的 postProcessAfterInstantiation(对象实例化后执行)

执行完
postProcessAfterInstantiation方法会开执行属性的填充执行InstantiationAwareBeanPostProcessor接口对应的postProcessProperties方法,进行属性的装配。

如类中有@Resource等注解的属性会通过
CommonAnnotationBeanPostProcessor处理器进行属性的注入。

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
    try {
        metadata.inject(bean, beanName, pvs);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
    }
    return pvs;
}

如类中有@Autowired,@Value,@Inject,setter属性会通过
AutowiredAnnotationBeanPostProcessor处理器进行属性的注入。

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    try {
        metadata.inject(bean, beanName, pvs);
    }
    catch (BeanCreationException ex) {
        throw ex;
    }
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
    }
    return pvs;
}

到此Bean相关的属性也都注入完成了。

1.5 初始化Bean

initializeBean方法 初始化给定的bean实例,应用工厂回调、init方法(Bean 是否实现了InitializingBean接口)和bean后处理器(BeanPostProcessor,在init方法前后调用)。如果该Bean同时实现了相关的Aware接口,那么会注入相关的对象实例。

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    
    invokeAwareMethods(beanName, bean);

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    return wrappedBean;
}

第一步:

invokeAwareMethods(beanName, bean);

装配Aware相关接口对象

invokeAwareMethods方法执行当前Bean实现了的相关*Aware接口方法。

private void invokeAwareMethods(String beanName, Object bean) {
    if (bean instanceof Aware) {
        if (bean instanceof BeanNameAware) {
            ((BeanNameAware) bean).setBeanName(beanName);
        }
        if (bean instanceof BeanClassLoaderAware) {
            ClassLoader bcl = getBeanClassLoader();
            if (bcl != null) {
                ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
            }
        }
        if (bean instanceof BeanFactoryAware) {
            ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
        }
    }
}

第二步:

wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);

执行BeanPostProcessor对象的
postProcessBeforeInitialization(对象初始化前执行

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
        throws BeansException {

    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        Object current = processor.postProcessBeforeInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}

第三步:

执行相关的初始化方法

protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
            throws Throwable {

    boolean isInitializingBean = (bean instanceof InitializingBean);
    if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
        // ...
        ((InitializingBean) bean).afterPropertiesSet();
        // ...
    }
    // ...
    invokeCustomInitMethod(beanName, bean, mbd);
}

1:

如果当前Bean是InitializingBean的实例那么先执行afterPropertiesSet方法。

2:

如果当前Bean配置了相关的init-method属性,那么执行相对应的方法。

第四步:

if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}


applyBeanPostProcessorsAfterInitialization()方法中会调用BeanPostProcessor的postProcessAfterInitialization方法

如果在开启AOP功能后会执行
AnnotationAwareAspectJAutoProxyCreator中的postProcessAfterInitialization方法该方法很重要用来产生代理对象Proxy。

这里我们查看
AnnotationAwareAspectJAutoProxyCreator中的方法执行

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

进入wrapIfNecessary方法

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (StringUtils.hasLength(beanName) && 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;
    }

    // Create proxy if we have advice.
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        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;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

这段代码中的shouldSkip方法会查找所有匹配的切面及通知。

protected boolean shouldSkip(Class<?> beanClass, String beanName) {
    // TODO: Consider optimization by caching the list of the aspect names
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    for (Advisor advisor : candidateAdvisors) {
        if (advisor instanceof AspectJPointcutAdvisor &&
                ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
            return true;
        }
    }
    return super.shouldSkip(beanClass, beanName);
}

findCandidateAdvisors方法会执行如下方法代码:

protected List<Advisor> findCandidateAdvisors() {
    // Add all the Spring advisors found according to superclass rules.
    List<Advisor> advisors = super.findCandidateAdvisors();
    // Build Advisors for all AspectJ aspects in the bean factory.
    if (this.aspectJAdvisorsBuilder != null) {
        advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
    }
    return advisors;
}

1:

查找所有的Advisor的Bean对象。

public List<Advisor> findAdvisorBeans() {
    // Determine list of advisor bean names, if not cached already.
    String[] advisorNames = this.cachedAdvisorBeanNames;
    if (advisorNames == null) {
        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let the auto-proxy creator apply to them!
        advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                this.beanFactory, Advisor.class, true, false);
        this.cachedAdvisorBeanNames = advisorNames;
    }
    if (advisorNames.length == 0) {
        return new ArrayList<>();
    }
    // ...
    return advisors;
}

这段代码会查找当前容器中所有的Advisor Bean。

2:

如果aspectJAdvisorsBuilder不为空会执行

advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());

3:


getAdvicesAndAdvisorsForBean方法返回当前bean所匹配到的所有切面及通知。

第五步:

createProxy创建代理对象

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

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

    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);

    if (!proxyFactory.isProxyTargetClass()) {
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        }
        else {
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }

    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());
}

到此整个Bean的实例化就完成了。

完毕!!!



Tags:Spring IOC   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
环境:Spring5.3.3Spring容器启动时,创建 DefaultListableBeanFactory 工厂实例化 AnnotationConfigApplicationContext对象public AnnotationConfigApplicationContext(String...【详细内容】
2021-06-10  Tags: Spring IOC  点击:(152)  评论:(0)  加入收藏
做Java的基本上都用过Spring,而IoC是Spring最核心的模块之一。那IoC具体有什么用,Spring又是如何做到IoC的呢?这是本文要探索的话题。关于IoC什么是依赖?首先我们要明确“依赖”...【详细内容】
2020-08-18  Tags: Spring IOC  点击:(36)  评论:(0)  加入收藏
▌简易百科推荐
一、为什么要搭建主从架构呢1.数据安全,可以进行数据的备份。2.读写分离,大部分的业务系统来说都是读数据多,写数据少,当访问压力过大时,可以把读请求给到从服务器。从而缓解数据...【详细内容】
2021-12-15  实战Java    Tags:Docker   点击:(10)  评论:(0)  加入收藏
在网页中渲染公式一直是泛学术工具绕不开的一个功能,最近更新产品功能,正巧遇到了这个需求,于是使用容器方式简单实现了一个相对靠谱的公式渲染服务。分享出来,希望能够帮到有类...【详细内容】
2021-12-01  编程菌zfn    Tags:Docker   点击:(10)  评论:(0)  加入收藏
1.1 docker命令直接部署1.1.1 拉取镜像docker pull wurstmeister/zookeeperdocker pull wurstmeister/kafka1.1.2 启动zookeeper容器docker run -d --name myzookeeper -p 2...【详细内容】
2021-11-15  无    Tags:docker   点击:(47)  评论:(0)  加入收藏
01 前言 顺着docker的发展,很多测试的同学也已经在测试工作上使用docker作为环境基础去进行一些自动化测试,这篇文章主要讲述我们在docker中使用浏览器进行自动化测试如果可以...【详细内容】
2021-10-29  小码哥聊软件测试    Tags:Docker   点击:(42)  评论:(0)  加入收藏
因为你懂得的原因,下载docker镜像速度非常喜感,故收集几个国内常用的docker镜像。Docker中国区官方镜像地址:https://registry.docker-cn.com网易163的镜像http://hub-mirror.c...【详细内容】
2021-10-28  抓蛙程序猿    Tags:docker   点击:(48)  评论:(0)  加入收藏
环境:Spring5.3.10通常,应用程序开发人员不需要对ApplicationContext实现类进行子类化。相反,SpringIOC容器可以通过插入特殊集成接口的实现来扩展。使用BeanPostProcessor自定...【详细内容】
2021-10-26  Java网络研发架构师    Tags:Spring   点击:(33)  评论:(0)  加入收藏
我们在很多场景下都需要做笔记,来对抗遗忘,一份好的笔记不仅能在需要的时候供我们查阅,也能帮助我们归纳整理知识提高做事效率。 目前市面上有很多云笔记软件,体验上各有不同,但...【详细内容】
2021-10-11  运维贼船    Tags:docker   点击:(61)  评论:(0)  加入收藏
1. Nacos官网Nacos Docker 快速开始2. Clone 项目git clone https://github.com/nacos-group/nacos-docker.git3. cd 到nacos-docker 路径下 直接启动即可cd nacos-dockerdo...【详细内容】
2021-09-16  程序狗爱化妆    Tags:Nacos   点击:(109)  评论:(0)  加入收藏
今天不做保姆级教程,分享奶爸常用、好用的Docker应用。有了这些Docker,Nas的可玩性会大幅提高,有时候奶爸也在想,刨去官方套件不考虑的话,Nas真的是差不多。如果小伙伴们有需要,后...【详细内容】
2021-09-03  晋升奶爸的垃圾佬    Tags:Docker   点击:(167)  评论:(0)  加入收藏
环境要求 ubuntu系统:20.04 docker版本:20.10.7 redis版本:6.0.6步骤由于我这里已经有相应的redis镜像,这里就不记录了,关于docker一些基础知识可以看我以前的笔记开启3台re...【详细内容】
2021-07-26  石老师小跟班    Tags:Redis主从复制   点击:(117)  评论:(0)  加入收藏
相关文章
    无相关信息
最新更新
栏目热门
栏目头条