热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

spring获取bean的源码解析

这篇文章主要介绍了spring获取bean的源码解析,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

介绍

前面一章说了AbstractApplicationContext中的refresh方法中的invokeBeanFactoryPostProcessors。主要是调用BeanFactoryPostProcessor。其中也有获取bean的过程,就是beanFactory.getBean的方法。这一章就说下getBean这个方法。由于spring中获取bean的方法比较复杂,涉及到的流程也非常多,这一章就先说下整个大体的流程。其中的细节会在后面也会慢慢说。

源码

直接看源码吧

@Override
	public Object getBean(String name) throws BeansException {
		return doGetBean(name, null, null, false);
	}
 
	@Override
	public  T getBean(String name, @Nullable Class requiredType) throws BeansException {
		return doGetBean(name, requiredType, null, false);
	}
 
	@Override
	public Object getBean(String name, Object... args) throws BeansException {
		return doGetBean(name, null, args, false);
	}
 
 
	public  T getBean(String name, @Nullable Class requiredType, @Nullable Object... args)
			throws BeansException {
 
		return doGetBean(name, requiredType, args, false);
	}
 
 
	@SuppressWarnings("unchecked")
	protected  T doGetBean(final String name, @Nullable final Class requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
        
        // 把name转化成beanName,也就是把FactoryBean的名称转化成beanName如果有别名则用别名
		final String beanName = transformedBeanName(name);
		Object bean;
 
		// 从缓存中获取实例
        // 可能是需要的Bean实例,也可能是FactoryBean
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			if (logger.isDebugEnabled()) {
				if (isSingletonCurrentlyInCreation(beanName)) {
					logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
							"' that is not fully initialized yet - a consequence of a circular reference");
				}
				else {
					logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
				}
			}
            // 获取需要的bean或者FactoryBean
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}
 
		else {
			// 判断prototype类型的bean是否存在循环引用
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}
 
			// 校验父类BeanFactory
			BeanFactory parentBeanFactory = getParentBeanFactory();
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				// 父类去获取bean
				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 {
					// No args -> delegate to standard getBean method.
					return parentBeanFactory.getBean(nameToLookup, requiredType);
				}
			}
            
            // 标记成已创建
			if (!typeCheckOnly) {
				markBeanAsCreated(beanName);
			}
 
			try {
                // 把原来BeanDefinition转换成RootBeanDefinition
				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				checkMergedBeanDefinition(mbd, beanName, args);
 
				// 获取依赖的bean,也就是通过@DependsOn注入进来的bean
				String[] dependsOn = mbd.getDependsOn();
				if (dependsOn != null) {
					for (String dep : dependsOn) {
                        // 校验dependsOn的bean是否存在循环应用
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
                        // 加入到引用的缓存中,由于校验dependsOn循环引用
						registerDependentBean(dep, beanName);
                        // 获取@dependsOn的bean
						getBean(dep);
					}
				}
 
				// 创建单例的bean
				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或者FactoryBean
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}
                // 创建prototype的bean
				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);
				}
                // 创建其他的bean,比如session,request等
				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;
			}
		}
 
		// 如果要求的类型不是这个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.isDebugEnabled()) {
					logger.debug("Failed to convert bean '" + name + "' to required type '" +
							ClassUtils.getQualifiedName(requiredType) + "'", ex);
				}
				throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
			}
		}
		return (T) bean;
	}

获取bean的整体流程就像上面源码所示,这里再梳理下spring获取bean的整个流程

1.先转换bean的名称,转换成beanName。这里意思就是,我们在获取bean的时候,可能是FactoryBean的名称(&开头),这里转成不带&开头的名称,如果有别名,再获取别名。

2.从缓存中获取bean,这里的缓存分为一二三级缓存,也就是我们常常被问到了spring三级缓存,具体逻辑下面再说。

3.根据获取的到对象再去获取我们想要的bean,因为这里获取到的对象可能是我们需要的bean,也可能是FactoryBean。

4.如果缓存中没有,那么我们就要自己去创建bean了。

5.查看有没有父类的BeanFactory,如果有,那么就父类去创建bean。

6.获取要创建的bean对象的@DependsOn注解上的名称,先去创建DependsOn的bean,并且校验是否存在循环引用

7.创建bean,根据类型创建不同的bean,比如singleton,prototype,request,session等。

8.如果需要转换类型,则进行类型转换。

整体的获取bean的流程就是这样了,其中有些具体流程接着分析。

从缓存中获取bean对象

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) {
                    // 从三级缓存中获取
					ObjectFactory<&#63;> singletOnFactory= this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						singletOnObject= singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

spring通过三级缓存来解决循环依赖的问题。简单来介绍下三级缓存。

1. singletonObjects为一级缓存,我们实例化的bean都在这个map里,侠义的说singletonObjects才是我们真正的spring容器,存放bean的地方。

2. earlySingletonObjects为二级缓存,是存放未完成的bean的缓存,如果有代理的话,存放的是代理对象。

3. singletonFactories为三级缓存,存放的是一个ObjectFactory,数据通过getObject方法获得。

从BeanInstance中获取对象

接下来看getObjectForBeanInstance方法。

	protected Object getObjectForBeanInstance(
			Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
 
        // name是不是factoryBean的name(&开头的)
		if (BeanFactoryUtils.isFactoryDereference(name)) {
			if (beanInstance instanceof NullBean) {
				return beanInstance;
			}
            // 如果是FactoryBeanName,但是获取到的bean不是FactoryBean,则抛异常
			if (!(beanInstance instanceof FactoryBean)) {
				throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
			}
		}
 
        // 如果bean不是FactoryBean,或者名称是FactoryBeanName,直接返回BeanInstace
		if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
			return beanInstance;
		}
 
		Object object = null;
		if (mbd == null) {
            // 从缓存中获取
			object = getCachedObjectForFactoryBean(beanName);
		}
		if (object == null) {
			// 这里可以确定beanInstance是FactoryBean了
			FactoryBean<&#63;> factory = (FactoryBean<&#63;>) beanInstance;
			// Caches object obtained from FactoryBean if it is a singleton.
			if (mbd == null && containsBeanDefinition(beanName)) {
				mbd = getMergedLocalBeanDefinition(beanName);
			}
			boolean synthetic = (mbd != null && mbd.isSynthetic());
            // 通过FactoryFBean中获取需要的beanInstance
			object = getObjectFromFactoryBean(factory, beanName, !synthetic);
		}
		return object;
	}

这里是通过BeanInstance获取我们想要的bean,这里也简单说下流程

1. 首先判断name是不是FactoryBean的name,也就是&开头的name,如果是去判断beanInstance是不是FactoryBean,如果beanInstance不是FactoryBean则抛异常。

2. 由于上面已经判断过,如果name是FactoryBeanName,但是BeanInstance不是FactoryBean的话,就会抛出异常。所以如果BeanInstance如果不是FactoryBean的话,那么name一定不是FactoryBeanName。那么就直接返回BeanInstance就是我们需要的了。

如果name是FactoryBeanName,那么我们需要获取的就是FactoryBean,也直接返回就可以了。

3. 如果都没有返回,那么已经可以确定我们此时的已经可以确定BeanInstance是FactoryBean了,因为如果不是FactoryBean的话,在!(beanInstance instanceof FactoryBean)就已经返回了。

4. 通过FactoryBean的getObject方法获取我们需要的bean实例。

创建bean

根据@dependsOn查找依赖的bean并且加到依赖里面去没有什么好说的,代码逻辑也很简单,接下来看创建单例bean。其他类型的bean的创建也都差别不大。看源码

	public Object getSingleton(String beanName, ObjectFactory<&#63;> 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 + "'");
				}
                // 在没创建bean之前的处理
				beforeSingletonCreation(beanName);
				boolean newSingleton = false;
				boolean recordSuppressedExceptiOns= (this.suppressedExceptiOns== null);
				if (recordSuppressedExceptions) {
					this.suppressedExceptiOns= new LinkedHashSet<>();
				}
				try {
                    // 获取创建的bean
					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;
		}
	}

对于创建单例bean的主要流程就是如此,传入一个beanName,和一个ObjectFactory。ObjectFactory中具体实现了创建bean的逻辑。在看具体创建bean的逻辑之前,我们还需要去看下getSingleton中的创建bean之前的工作和创建bean之后的工作。这里面就是查找bean的循环依赖的方法(和dependsOn不同)。主要是查找根据filed,set,构造器方法的循环依赖。

protected void beforeSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}
 
	protected void afterSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
			throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
		}
	}

可以看到代码非常的简单,就是创建bean之前,如果没有排除依赖检查,那么就加入到正在创建的Set中,如果加入不进去,说明之前已经加过,这就产生了循环依赖,从而抛出异常。

如果在创建bean之后,没有排除检查依赖,并且移除失败,说明已经不在Set中,也会抛出异常。

好了,既然明白了spring是如何校验循环依赖的,也看到了三级缓存,后面再说为什么不能解决构造器依赖就很好说了。接着看创建bean的方法。

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
 
		if (logger.isDebugEnabled()) {
			logger.debug("Creating instance of bean '" + beanName + "'");
		}
		RootBeanDefinition mbdToUse = mbd;
        
        // 获取要创建bean的class
		Class<&#63;> resolvedClass = resolveBeanClass(mbd, beanName);
		if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
            // 如果没有beanclass,设置beanclass
			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如果能产生代理,则直接返回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.isDebugEnabled()) {
				logger.debug("Finished creating instance of bean '" + beanName + "'");
			}
			return beanInstance;
		}
		catch (BeanCreationException ex) {
			// A previously detected exception with proper bean creation context already...
			throw ex;
		}
		catch (ImplicitlyAppearedSingletonException ex) {
			// An IllegalStateException to be communicated up to DefaultSingletonBeanRegistry...
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
		}
	}

这里其实还是没有到创建bean的过程,还是在创建bean的一些准备工作。其实我们可以发现,spring中,真正做事的都是do开头的方法。

这边的流程就是设置beanClass,后面需要根据反射来创建bean。然后会根据spring里面的beanPostProcessor,看看有没有能产生代理bean的,如果有就返回,没有就去创建bean。

看真正的doCreateBean方法

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {
 
		// 装饰Bean的对象
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
            // 通过缓存获取
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
            // 创建bean
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		final Object bean = instanceWrapper.getWrappedInstance();
		Class<&#63;> 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 {
                    // 调用MergedBeanDefinitionPostProcessor的方法
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				}
				mbd.postProcessed = true;
			}
		}
 
		// 加入到三级缓存中去
		boolean earlySingletOnExposure= (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isDebugEnabled()) {
				logger.debug("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}
 
		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
            // 填充依赖的bean,field注入,和方法注入的bean
			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;
				}
                // 如果调用初始化后的bean和之前的bean不一致,并且有依赖
				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.");
					}
				}
			}
		}
 
		// 注册disposableBean
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}
 
		return exposedObject;
	}

真正创建bean这里还是有点复杂的。这里再进行一个简单梳理。

1. 根据class还有bean以及参数创建bean。

2. 调用beanPostprocessor的方法,调用属于MergedBeanDefinitionPostProcessor的方法。对bean进行一些处理,比如找到那些依赖的bean的field和method。

3. 将bean加入到三级缓存中去。

4. 填充bean需要注入的其他bean。

5. 调用初始化方法,先去调用@PostConstruct注解方法,然后调用InitializingBean的afterPropertiesSet,以及自定义的init-method方法。在bean调用初始化方法之后,再去调用后置接口看看是否需要生成Aop代理。

6. 接着进行校验。这里稍微比较复杂一点。如果从二级缓存能取到,那就说明之前被别人从三级缓存拿出来过了。可能是因为循环依赖,也可能是因为别的地方调用了getBean方法了。从三级缓存拿出来的时候有个getEarlyBeanReference的方法,就是查看是否要生成代理的bean。如果生成过了,那么在调用第五步的时候,就不会在生成代理了。这样exposedObject ==bean,直接只用代理返回。

如果不相等:这里的情况就是如果是spring自己的@Async,在从二级缓存生成代理之后,再去调用第五步时候一样会生成代理。所以exposedObject !=bean,所以在再往下发现有循环调用,并且bean还在创建时,就会抛出异常了。所以一般慎用spring的@Async。但是一般也可以使用@Lazy进行处理。至于原理后面再说。

到这里spring的创建bean就结束了。然后返回时候就到了入口方法getBean的getObjectForBeanInstance的方法,到底需要的bean还是FactoryBean。

最后就是如果requiredType和实例不一样就要进行类型转换了。

总结

本篇大概说下spring获取bean和加到容器里面的流程。其实广义上来说Bean的容器是BeanFactory或者applicationContext。狭义上说就是一个map。也就是一级缓存SingletonObjects。我们获取的真正需要的bean也就是从中获取的。本篇只是简要的说了下bean获取和加入容器的整个流程,具体的根据无参构造器创建bean,有参数构造器创建bean。还有对于创建bean中依赖的bean的查找还有创建,三级缓存如何解决循环依赖还有为何不能解决构造器依赖,以及bean调用初始化的等等操作都没有说。因为一篇说起来确实太长了。后面都会一一去分析。

到此这篇关于spring获取bean的源码解析的文章就介绍到这了,更多相关spring获取bean源码内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!


推荐阅读
  • docker增加restart=always, docker重启后自动启动容器的方法
    本文介绍了在运行docker容器时如何添加参数来保证每次docker服务重启后容器也自动重启的方法,以及如何使用命令来更新已启动的容器。 ... [详细]
  • 处理docker容器时间和宿主机时间不一致问题的方法
    本文介绍了处理docker容器时间和宿主机时间不一致问题的方法,包括复制主机的localtime到容器、处理报错情况以及重启容器的步骤。通过这些方法,可以解决docker容器时间和宿主机时间不一致的问题。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • 本文介绍了作者在开发过程中遇到的问题,即播放框架内容安全策略设置不起作用的错误。作者通过使用编译时依赖注入的方式解决了这个问题,并分享了解决方案。文章详细描述了问题的出现情况、错误输出内容以及解决方案的具体步骤。如果你也遇到了类似的问题,本文可能对你有一定的参考价值。 ... [详细]
  • 本文介绍了Java的集合及其实现类,包括数据结构、抽象类和具体实现类的关系,详细介绍了List接口及其实现类ArrayList的基本操作和特点。文章通过提供相关参考文档和链接,帮助读者更好地理解和使用Java的集合类。 ... [详细]
  • 本文介绍了电流源并联合并的方法,以及谐振电路的原理。谐振电路具有很强的选频能力,通过将电感和电容连接在一起,电流和电压会产生震荡。谐振频率的大小取决于电感和电容的大小,而电路中的电阻会逐渐降低震荡的幅度。电阻和电容组成的电路中,当电容放完电后,电阻两端的电压为0,电流不再流过电容。然而,电感是一种特殊的器件,当有电流流过时,线圈会产生感应磁场,阻止电流的流动,从而使电流不会减小。 ... [详细]
  • 标题: ... [详细]
  • 单点登录原理及实现方案详解
    本文详细介绍了单点登录的原理及实现方案,其中包括共享Session的方式,以及基于Redis的Session共享方案。同时,还分享了作者在应用环境中所遇到的问题和经验,希望对读者有所帮助。 ... [详细]
  • 本文介绍了在Docker容器技术中限制容器对CPU的使用的方法,包括使用-c参数设置容器的内存限额,以及通过设置工作线程数量来充分利用CPU资源。同时,还介绍了容器权重分配的情况,以及如何通过top命令查看容器在CPU资源紧张情况下的使用情况。 ... [详细]
  • 集合的遍历方式及其局限性
    本文介绍了Java中集合的遍历方式,重点介绍了for-each语句的用法和优势。同时指出了for-each语句无法引用数组或集合的索引的局限性。通过示例代码展示了for-each语句的使用方法,并提供了改写为for语句版本的方法。 ... [详细]
  • Python SQLAlchemy库的使用方法详解
    本文详细介绍了Python中使用SQLAlchemy库的方法。首先对SQLAlchemy进行了简介,包括其定义、适用的数据库类型等。然后讨论了SQLAlchemy提供的两种主要使用模式,即SQL表达式语言和ORM。针对不同的需求,给出了选择哪种模式的建议。最后,介绍了连接数据库的方法,包括创建SQLAlchemy引擎和执行SQL语句的接口。 ... [详细]
  • position属性absolute与relative的区别和用法详解
    本文详细解读了CSS中的position属性absolute和relative的区别和用法。通过解释绝对定位和相对定位的含义,以及配合TOP、RIGHT、BOTTOM、LEFT进行定位的方式,说明了它们的特性和能够实现的效果。同时指出了在网页居中时使用Absolute可能会出错的原因,即以浏览器左上角为原始点进行定位,不会随着分辨率的变化而变化位置。最后总结了一些使用这两个属性的技巧。 ... [详细]
  • 开发笔记:Docker 上安装启动 MySQL
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Docker上安装启动MySQL相关的知识,希望对你有一定的参考价值。 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
author-avatar
deadman21
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有