概念

什么是循环引用?

故名思义,多个对象形成环路。

有哪几种循环引用?

在Spring中存在如下几种循环引用,一一举例分析一下

  • 注入循环引用(Set注入 注解注入
package c.q.m;

import lombok.Data;

/**
* @Auther: chenqimiao
* @Date: 2019/6/28 11:16
* @Description:
*/
@Data
public class You {
private Me me;
}
package c.q.m;

import lombok.Data;

/**
* @Auther: chenqimiao
* @Date: 2019/6/28 11:16
* @Description:
*/
@Data
public class Me {
private You you;
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="me" class="c.q.m.Me">
<property name="you" ref="you"/>
</bean> <bean id="you" class="c.q.m.You">
<property name="me" ref="me"/>
</bean> </beans>
package c.q.m;

import org.junit.Assert;
import org.junit.Test; import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource; /**
* @Auther: chenqimiao
* @Date: 2019/6/28 13:44
* @Description:
*/
public class CircularReferenceTest { @Test
public void injectionTest() {
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("circular-reference-beans-test-config.xml"));
Me me = beanFactory.getBean(Me.class);
You you = beanFactory.getBean(You.class);
Assert.assertEquals(me.getYou(), you);
}
}
  • 构造器循环引用
package c.q.m;

import lombok.AllArgsConstructor;
import lombok.Getter; /**
* @Auther: chenqimiao
* @Date: 2019/6/28 11:16
* @Description:
*/
@AllArgsConstructor
@Getter
public class You {
private Me me;
}
package c.q.m;

import lombok.AllArgsConstructor;
import lombok.Getter; /**
* @Auther: chenqimiao
* @Date: 2019/6/28 11:16
* @Description:
*/
@AllArgsConstructor
@Getter
public class Me {
private You you;
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="me" class="c.q.m.Me">
<constructor-arg ref="you"/>
</bean> <bean id="you" class="c.q.m.You">
<constructor-arg ref="me"/>
</bean> </beans>
package c.q.m;

import org.junit.Assert;
import org.junit.Test; import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource; /**
* @Auther: chenqimiao
* @Date: 2019/6/28 13:44
* @Description:
*/
public class CircularReferenceTest { @Test
public void constructorTest() {
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("circular- reference-beans-test-config.xml"));
Me me = beanFactory.getBean(Me.class);
You you = beanFactory.getBean(You.class);
Assert.assertEquals(me.getYou(), you);
}
}
  • 工厂构造循环引用(与构造器循环引用类似)

Spring如何解决

提前暴露一个ObjectFactory 类型的工厂对象,通过这种方式Spring解决了单例模式下的注入循环引用,至于其他类型的循环引用Spring也并没有什么好的解决办法。

通过源码的方式来分析一下Spring的手段

  • org.springframework.beans.factory.support.AbstractBeanFactory

    protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
    @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { final String beanName = transformedBeanName(name);
    Object bean; // Eagerly check singleton cache for manually registered singletons.
    Object sharedInstance = getSingleton(beanName);
    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 + "'");
    }
    }
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    } else {
    // Fail if we're already creating this bean instance:
    // We're assumably within a circular reference.
    if (isPrototypeCurrentlyInCreation(beanName)) {
    throw new BeanCurrentlyInCreationException(beanName);
    } // Check if bean definition exists in this factory.
    BeanFactory parentBeanFactory = getParentBeanFactory();
    if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
    // Not found -> check parent.
    String nameToLookup = originalBeanName(name);
    if (parentBeanFactory instanceof AbstractBeanFactory) {
    return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
    nameToLookup, requiredType, args, typeCheckOnly);
    }
    else if (args != null) {
    // Delegation to parent with explicit args.
    return (T) parentBeanFactory.getBean(nameToLookup, args);
    }
    else if (requiredType != null) {
    // No args -> delegate to standard getBean method.
    return parentBeanFactory.getBean(nameToLookup, requiredType);
    }
    else {
    return (T) parentBeanFactory.getBean(nameToLookup);
    }
    } if (!typeCheckOnly) {
    markBeanAsCreated(beanName);
    } try {
    final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
    checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on.
    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);
    }
    }
    } // 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;
    }
    });
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    } else if (mbd.isPrototype()) {
    // 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 {
    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;
    }
    } // Check if required type matches the type of the actual bean instance.
    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());
    }
    }
    return (T) bean;
    }

    跟踪上面的 Object sharedInstance = getSingleton(beanName);方法

  • org.springframework.beans.factory.support.DefaultSingletonBeanRegistry

    	@Override
    @Nullable
    public Object getSingleton(String beanName) {
    return getSingleton(beanName, true);
    } @Nullable
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
    synchronized (this.singletonObjects) {
    singletonObject = this.earlySingletonObjects.get(beanName);
    if (singletonObject == null && allowEarlyReference) {
    //*******singleFactory中拿到提前暴露的ObjectFactory对象******//
    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
    if (singletonFactory != null) {
    singletonObject = singletonFactory.getObject();
    this.earlySingletonObjects.put(beanName, singletonObject);
    this.singletonFactories.remove(beanName);
    }
    }
    }
    }
    return singletonObject;
    }

    定位到这里,发现Spring再获取bean的时候,会去singletonFactories集合里面拿一个ObjectFactory

    对象,再调用其getObject()方法拿到我们想要的bean.那么问题来了,singletonFactories的对象是何时put进去的?带着这个问题,我们跟踪一下singletonFactories 这个集合。

    我们回到org.springframework.beans.factory.support.AbstractBeanFactorydoGetBean方法,定位到下面的代码段:

    				// 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;
    }
    });
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }

    定义的bean是单例,则调用getSingleton方法,跟踪这个方法发现它是属于org.springframework.beans.factory.suppor.DefaultSingletonBeanRegistry 的

    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "Bean name 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<>();
    }
    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;
    }
    }

    这个方法提供了很多的扩展方法,但是我们关注的核心是这一句话

    singletonObject = singletonFactory.getObject();singletonFactory就是我们刚刚传入的函数。

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

    接着定位到createBean方法,它是属于org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory的

    	@Override
    protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
    throws BeanCreationException { if (logger.isTraceEnabled()) {
    logger.trace("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);
    } 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);
    }
    }

    跟踪这一句话Object beanInstance = doCreateBean(beanName, mbdToUse, args);

    	protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
    throws BeanCreationException { // Instantiate the bean.
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
    instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
    instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    final Object bean = instanceWrapper.getWrappedInstance();
    Class<?> beanType = instanceWrapper.getWrappedClass();
    if (beanType != NullBean.class) {
    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.
    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");
    }
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    } // Initialize the bean instance.
    Object exposedObject = bean;
    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);
    }
    } 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<>(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.");
    }
    }
    }
    } // Register bean as disposable.
    try {
    registerDisposableBeanIfNecessary(beanName, bean, mbd);
    }
    catch (BeanDefinitionValidationException ex) {
    throw new BeanCreationException(
    mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
    } return exposedObject;
    }

    真相差不多浮出水面了,请看这一句话

    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

    	protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
    if (!this.singletonObjects.containsKey(beanName)) {
    this.singletonFactories.put(beanName, singletonFactory);
    this.earlySingletonObjects.remove(beanName);
    this.registeredSingletons.add(beanName);
    }
    }
    }

    this.singletonFactories.put(beanName, singletonFactory);就在这里beanFactory被put进了singletonFactories.看一下singletonFactory具体的实现

    	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
    if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
    }
    }
    }
    return exposedObject;
    }

    将传入的bean经过BeanPostProcessor的处理后返回。那么传入的bean到底是什么状态的bean呢?

    此时的bean是完成了初始化构造的bean,但是还没有进行set或者注解注入的bean,是bean的一个中间状态。

    说到这里应该差不多清晰了,Spring会在bean还未完全实例化的时候,将bean包装在ObjectFactory里面,调用doGetBean的时候,先尝试去ObjectFactory中去拿还未完全实例化的bean.

    You and Me 的例子在注解注入造成循环依赖时,Spring的调用链时序图如下:

    总结

Spring如何解决循环引用的更多相关文章

  1. Spring 如何解决循环依赖问题?

    在关于Spring的面试中,我们经常会被问到一个问题,就是Spring是如何解决循环依赖的问题的. 这个问题算是关于Spring的一个高频面试题,因为如果不刻意研读,相信即使读过源码,面试者也不一定能 ...

  2. Spring如何解决循环依赖问题

    目录 1. 什么是循环依赖? 2. 怎么检测是否存在循环依赖 3. Spring怎么解决循环依赖 本文主要是分析Spring bean的循环依赖,以及Spring的解决方式. 通过这种解决方式,我们可 ...

  3. Spring如何解决循环依赖

    一.什么是循环依赖 多个bean之间相互依赖,形成了一个闭环. 比如:A依赖于B.B依赖于c.c依赖于A 通常来说,如果问spring容器内部如何解决循环依赖, 一定是指默认的单例Bean中,属性互相 ...

  4. 深入研究Block用weakSelf、strongSelf、@weakify、@strongify解决循环引用(上)

    深入研究Block捕获外部变量和__block实现原理 前言 在上篇中,仔细分析了一下Block的实现原理以及__block捕获外部变量的原理.然而实际使用Block过程中,还是会遇到一些问题,比如R ...

  5. swift闭包中解决循环引用的问题

    swift中可以通过三种方法解决循环引用的问题 利用类似oc方法解决循环引用weak var weakSelf = self weak var weakSelf = self loadData = { ...

  6. 【C++】智能指针简述(五):解决循环引用的weak_ptr

    总结一下前文内容: 1.智能指针通过RAII方法来管理指针:构造对象时,完成资源初始化;析构对象时,对资源进行清理及汕尾. 2.auto_ptr,通过“转移所有权”来防止析构一块内存多次.(如何转移? ...

  7. Flask-分开Models解决循环引用

    Flask-分开Models解决循环引用 在之前我们测试中,所有语句都在同一个文件中,但随着项目越来越大,管理起来有所不便,所以将Models分离.基本的文件结构如下 \—–app.py\—–mode ...

  8. Spring 如何解决循环依赖的问题

    Spring 如何解决循环依赖的问题 https://blog.csdn.net/qq_36381855/article/details/79752689 Spring IOC 容器源码分析 - 循环 ...

  9. 一张图彻底理解Spring如何解决循环依赖!!

    写在前面 最近,在看Spring源码,看到Spring解决循环依赖问题的源码时,不得不说,源码写的太烂了.像Spring这种顶级的项目源码,竟然存在着这种xxx的代码.看了几次都有点头大,相信很多小伙 ...

随机推荐

  1. 【 D3.js 入门系列 --- 0 】 简介及安装

    家是我的个人博客: http://www.ourd3js.com/  ,csdn博客首页为:http://blog.csdn.net/lzhlzz/.转载请注明出处,谢谢. D3的全称是(Data-D ...

  2. s便携小方法,你值得拥有

    引言: 本章没有深奥的讲解js一些底层原理,比如this指针.作用域.原型啦,涉及的都是一些有利于平时开发时简化代码,提高执行效率,或者说可以当做一种经验方法来使用,篇幅都不长,小步快跑的让你阅读完整 ...

  3. CUDA二维纹理内存+OpenCV图像滤波

    CUDA和OpenCV混合编程,使用CUDA的纹理内存,实现图像的二值化以及滤波功能. #include <cuda_runtime.h> #include <highgui/hig ...

  4. Arraylist 和 linkedlist || hashset 和treeset. || hashMap 和 TreeMap

    参考:http://liuyuan418921673.iteye.com/blog/2256120 1. ArrayList和LinkedList的区别和使用场景   ArryList 与linked ...

  5. Java并发编程:synchronized和Lock

    转自  :   http://www.tuicool.com/articles/qYFzUjf

  6. QT开发环境的建立以及QTE4.6.3、tslib1.4的移植过程

    1.首先是建立Linux开发环境1.1.在windowsXP下安装博创公司提供的虚拟机软件VMware Workstation,版本为VMware-workstation-full-7.0.1-227 ...

  7. uva 11892 - ENimEN(推理)

    题目链接:uva 11892 - ENimEN 题目大意:给定n堆石子的个数,两人轮流选择石子堆取石子,直到不能取为失败,附加条件,假设前一次操作,即队手的操作,没有将选中石子堆中的石子取完,那么当前 ...

  8. 组态DNS、搜索域名和主机名

    一个.组态DNS和搜索领域 特别配置DNS该文件是/etc/resolv.conf:同时,我们可以ifcfg-eth0网卡的配置和其他配置文件中指定的. 演示样本: [root@instructor ...

  9. python 教程 第四章、 控制流

    第四章. 控制流 控制语句后面要加冒号: 1)    if语句 if guess == number: print 'Congratulations, you guessed it.' # New b ...

  10. nginx配置http跳转https

    配置相当简单,在配置文件头部加一行,如下: server { listen *:;//监听80端口 https://www.chenruhui.com$request_uri;//需要跳转的网页 } ...