spring源码分析-01-IOC初始化容器过程分析

本文深入探讨Spring框架的IOC初始化过程,从入口的refresh方法开始,包括定位、加载、注册BeanDefinition,直至实例化和依赖注入。重点分析了loadBeanDefinitions方法,详细解释了如何将XML配置转化为BeanDefinitions并注册到容器中,为后续的getBean()调用做准备。

入口:

public ClassPathXmlApplicationContext(String[] paths, Class<?> clazz, ApplicationContext parent)
			throws BeansException {

		super(parent);
		Assert.notNull(paths, "Path array must not be null");
		Assert.notNull(clazz, "Class argument must not be null");
		this.configResources = new Resource[paths.length];
		for (int i = 0; i < paths.length; i++) {
			this.configResources[i] = new ClassPathResource(paths[i], clazz);
		}
		refresh();
	}

主要方法就是refresh方法进行IOC容器初始化。初始化过程包括:

  • 定位
  • 加载:加载过程最复杂,先将xml读取为Element然后在将Element转换为BeanDefinitions。里面还有好多逻辑
  • 注册:注册比较简单,加载完毕后生成BeanDefinitionHolder对象,这个对象持有beanName和BeanDef所以直接get到然后放到Map结构中

最后将xml解析为BeanDefinition对象,放到map中,当调用getBean()执行时就会

  • 实例化
  • 依赖注入
public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
            //调用容器准备刷新方法,获取容器的当前时间,同时给容器设置同步标识
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
            //告诉子类启动refreshBeanFactory方法,Bean定义资源文件的载入从子类的refreshBeanFactory()方法启动
            /**
             * 1.AbstractRefreshableApplicationContext.refreshBeanFactory这个方法里面就是构造BeanFactory
             * 2.然后调用AbstractRefreshableApplicationContext.getBeanFactory赋值给ConfigurableListableBeanFactory
             * 3.返回return这个BeanFactory
             */
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
            //为BeanFactory配置容器特性,例如类加载器,事件处理器等
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
                //国际化初始化
				initMessageSource();

				// Initialize event multicaster for this context.
                //事件传播
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

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

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

上面refresh方法实现的代码ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();这个就是创建IOC容器的方法。所以继续看这个方法的代码

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        //采用委派模式,父类提供接口,子类提供实现
        //创建BeanFactory的方法。
		refreshBeanFactory();

		//创建好BeanFactory之后,通过getBeanFactory获得方法
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		if (logger.isDebugEnabled()) {
			logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
		}
		return beanFactory;
	}

调用父类AbstractRefreshableApplicationContext.refreshBeanFactory()创建容器。然后调用父类AbstractRefreshableApplicationContext.getBeanFactory()获得创建的IOC容器,并将这个IOC容器返回出去。下面是refreshBeanFactory代码第一部分是判断容器是否存在如果存在则销毁容器,重新创建。

protected final void refreshBeanFactory() throws BeansException {
	    //判断是否已经存在IOC容器,如果存在,则销毁
        /**
         * hasBeanFactory()在判断AbstractRefreshableApplicationContext里面的成员变量beanFactory如果不是空,则需要销毁IOC容器
         */
		if (hasBeanFactory()) {
		    //主要就是清除map。IOC的容器就是一个Map
			destroyBeans();
			//将BeanFactory=null
			closeBeanFactory();
		}
		try {
		    //创建IOC容器,这个时候的beanFacotr仅仅只是new一下,容器里面还没有东西
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			//对IOC容器进行定制化,如设置启动参数,开启注解的自动装配等等
			customizeBeanFactory(beanFactory);

			//调用载入Bean定义的方法,主要这里又使用了一个委派模式,在当前类中只定义了抽象的loadBeanDefinitions,具体的实现调用子类容器
			//使用子类AbstractXmlApplicationContext来实现
            loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}

从上面的代码可以知道spring实例了一个DefaultListableBeanFactory给用户使用。在DefaultListableBeanFactory beanFactory = createBeanFactory();里面只是new了一个容器,当时容器里面什么都没有真正填充容器的是loadBeanDefinitions(beanFactory)。

//调用AbstractXmlApplicationContext.loadBeanDefinitions这里采用了委派模式,自己不做事,交给子类做事
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// Create a new XmlBeanDefinitionReader for the given BeanFactory.
        //创建XmlBeanDefinitionReader,即Bean的读取器,并通过回调设置到容器beanFactory里面(目前beanFactory仅仅只是new了一个对象)容器使用这个读取器去读取Bean定义资源
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// Configure the bean definition reader with this context's
		// resource loading environment.
        //为Bean读取器设置Spring资源加载器,AbstractXmlApplicationContext的父类AbstractApplicationContext 继承了 DefaultResourceLoader,因此容器也是一个Bean读取器
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);

		//为Bean读取器设置SAX xml解析器
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		// Allow a subclass to provide custom initialization of the reader,
		// then proceed with actually loading the bean definitions.
        //当Bean读取器读取Bean定义的xml资源文件时,启用xml校验机制,此处仅仅是设置了校验标志位:设置true
		initBeanDefinitionReader(beanDefinitionReader);

		//Bean读取器真正实现加载的方法
		loadBeanDefinitions(beanDefinitionReader);
	}

下面关于loadBeanDefinitions的层次很深,需要认真跟踪。

//AbstractXmlApplicationContext.loadBeanDefinitions
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
		//获取Bean定义资源的定位
	    Resource[] configResources = getConfigResources();
		if (configResources != null) {
		    //xml Bean读取器调用父类AbstractBeanDefinitionReader读取定位的Bean定义资源
			reader.loadBeanDefinitions(configResources);
		}
		//采用委派模式,调用子类的获取Bean定义资源定位的方法,该方法在ClassPathXmlApplicationContext中实现
        //如果子类种获取的Bean定义资源定位为空,则获取FileSystemXmlApplicationContext构造方法中setConfigLocations方法设置的资源
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			reader.loadBeanDefinitions(configLocations);
		}
	}

上面将资源定位到,调用XML读取器解析xml。调用XmlBeanDefinitionReader.loadBeanDefinitions()继续解析xml在spring里面如果某个方法以do开头那么这个方法就是真正做事的方法,其他方法都是套路,不停的调用调用而已看到下面方法

	public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		Assert.notNull(encodedResource, "EncodedResource must not be null");
		if (logger.isInfoEnabled()) {
			logger.info("Loading XML bean definitions from " + encodedResource);
		}

		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
		if (currentResources == null) {
			currentResources = new HashSet<EncodedResource>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
		if (!currentResources.add(encodedResource)) {
			throw new BeanDefinitionStoreException(
					"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
		}
		try {
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
				InputSource inputSource = new InputSource(inputStream);
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
				//真正做事的人找到了!
				return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
			}
			finally {
				inputStream.close();
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"IOException parsing XML document from " + encodedResource.getResource(), ex);
		}
		finally {
			currentResources.remove(encodedResource);
			if (currentResources.isEmpty()) {
				this.resourcesCurrentlyBeingLoaded.remove();
			}
		}
	}

继续跟踪doLoadBeanDefinitions。这个方法里面首先将xml文件流解析为Document对象。

Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);

继续进入registerBeanDefinitions

	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
	    //创建DefaultBeanDefinitionDocumentReader对象,这个对象里面存放就是bean alias import resource等等
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		int countBefore = getRegistry().getBeanDefinitionCount();

        //前面已经将XML文件内容转换为Document对象。
        //下面方法是将Document对象转成Spring里面的BeanDefinition对象
        //DefaultBeanDefinitionDocumentReader类里面的方法
        //并且将BeanDefinition注册到IOC容器中
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));

		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

上面registerBeanDefinitions中主要就是documentReader.registerBeanDefinitions(doc, createReaderContext(resource));将Document对象转成BeanDefinitions对象。继续进入这个方法。找到DefaultBeanDefinitionDocumentReader.registerBeanDefinitions方法。(马上就可以看到曙光了)

	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		logger.debug("Loading bean definitions");
		Element root = doc.getDocumentElement();
		//从Document里面获得Element的根元素开始解析。
        //以do开头的方法就是具体做事的方法
		doRegisterBeanDefinitions(root);
	}

继续看doRegisterBeanDefinitions方法

protected void doRegisterBeanDefinitions(Element root) {
		BeanDefinitionParserDelegate parent = this.delegate;
		//创建BeanDefinitionParserDelegate对象。这个对象就是解析Document对象中的数据。是要生成BeanDefinition的处理对象。在下面创建对象的时候已经将配置文件中<beans>里面的默认熟悉解析出来了
        /**
         * 在<beans><beans/>里面主要定义了默认的属性如下
            default-lazy-init
            default-merge
            default-autowire
            default-dependency-check
            default-autowire-candidates
            default-init-method
            default-destroy-method
         */
		this.delegate = createDelegate(getReaderContext(), root, parent);

		//默认的命名空间,也就是
        /**
         *xmlns="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
         xsi:schemaLocation="
         http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context.xsd"
         */
		if (this.delegate.isDefaultNamespace(root)) {
			String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
			if (StringUtils.hasText(profileSpec)) {
				String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
						profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
				if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
					if (logger.isInfoEnabled()) {
						logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
								"] not matching: " + getReaderContext().getResource());
					}
					return;
				}
			}
		}

		preProcessXml(root);

		//处理配置文件
		parseBeanDefinitions(root, this.delegate);

		postProcessXml(root);

		this.delegate = parent;
	}

上面的方法首先创建BeanDefinitionParserDelegate,然后读取配置文件中配置的全局默认属性。紧接着处理命名空间。然后处理preProcessXml(root);这个方法是空的。真正处理配置的方法是parseBeanDefinitions(root, this.delegate);

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		if (delegate.isDefaultNamespace(root)) {
		    //取出root下面的子节点。一个一个的bean
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				if (node instanceof Element) {
					Element ele = (Element) node;

					//判断当前节点是否是命名空间,如果是命名空间则交给命名空间类处理
					if (delegate.isDefaultNamespace(ele)) {
					    //普通类型,则开始将Element转换成BeanDefinitions。下面的方法也区分是什么类型:import;alias;bean;beans
                        //一般的spring配置文件根目录只包含上面四种情况,所以下面的方法分别处理
						parseDefaultElement(ele, delegate);
					}
					else {
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}

上面方法中从node节点中取出所有子节点,如果是普通的类型则开始解析parseDefaultElement(ele, delegate);

	private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
	    //处理import
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			importBeanDefinitionResource(ele);
		}
		//处理alias。在SimpleAliasRegistry对象中有一个map结构,将name和alias保存到map里面
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
			processAliasRegistration(ele);
		}
		//处理bean
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			processBeanDefinition(ele, delegate);
		}
		//处理beans
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			//递归调用
			doRegisterBeanDefinitions(ele);
		}
	}

看到上面的方法心里终于放下心了,已经找到调用真正真正处理的地方了。直接进入处理Bean的地方

	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
	    //将Element转成BeanDefinition。然后将BeanDefinition和BeanName交给BeanDefinitionHolder持有
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				// Register the final decorated instance.
                //将BeanDefinition注册到IOC容器中
				BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to register bean definition with name '" +
						bdHolder.getBeanName() + "'", ele, ex);
			}
			// Send registration event.
			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
		}
	}

上面代码中BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);就是处理Element的方法,将Element转化成BeanDefinitionsHolder这个对象里面持有beanName和BeanDefinitions。然后就是注册过程。注册过程简单,调用BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());内部就是将beanName和BeanDefinitions关联起来

public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

		// Register bean definition under primary name.
		String beanName = definitionHolder.getBeanName();
		//注册,将beanName和BeanDefinitions放到map中。使用的类:DefaultListableBeanFactory
        //主要有一下数据:
        // beanDefinitionMap:beanName作为key,BeanDefinitions作为value
        //List<String> beanDefinitionNames:list保存beanName
        //private volatile Set<String> manualSingletonNames保存单例的名字
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

		// Register aliases for bean name, if any.
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String alias : aliases) {
				registry.registerAlias(beanName, alias);
			}
		}
	}

上面已经将xml文件注册成了BeanDefinitions对象并用键值对的方式保存到Map中。下面就是依赖注入的过程。当调用getBean()方法时就会触发依赖注入。在spring源码中经常看到N多个重载方法,一般都是有一个最全参数的重载方法,然后其他方法都是有默认值的调用这个最全参数的方法。在getBean中同样是这样设计的。可以找到getBean的入口方法:AbstractApplicationContext.getBean(String name)所有的重载方法getBean最后都是doGetBean()。代码如下AbstractBeanFactory.doGetBean

protected <T> T doGetBean(
			final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
			throws BeansException {
        //1.如果是FactoryBean这种Bean都是以&开头。所以需要去掉&
        //2.如果name的别名,则需要在SimpleAliasRegistry里面进行判断获取的真正的那么
		final String beanName = transformedBeanName(name);
		Object bean;

		// Eagerly check singleton cache for manually registered singletons.
        //从缓存中获取对象,避免重复创建
		Object sharedInstance = getSingleton(beanName);
        System.out.println(sharedInstance);
        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 + "'");
				}
			}
			/*这里 的 getObjectForBeanInstance 完成 的 是 FactoryBean 的 相关 处理,
			以 取得 FactoryBean 的 生产 结果, BeanFactory 和 FactoryBean 的 区别 已经 在前面 讲过, 这个 过程 在后面 还会 详细 地 分析*/
            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.
            /*
            这里 对 IoC 容器 中的 BeanDefintion 是否 存在 进行检查,
            检查 是否 能在 当前 的 BeanFactory 中 取得 需要 的 Bean。
            如果 在 当前 的 工厂 中 取 不到, 则 到 双亲 BeanFactory 中 去取;
            如果 当前 的 双亲 工厂 取 不到, 那就 顺着 双亲 BeanFactory 链 一直 向上 查找*/
            BeanFactory parentBeanFactory = getParentBeanFactory();
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				// Not found -> check parent.
				String nameToLookup = originalBeanName(name);
				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 {
			    //通过beanName获得BeanDefinition对象  //这里 根据 Bean 的 名字 取得 BeanDefinition
                final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				checkMergedBeanDefinition(mbd, beanName, args);

				// Guarantee initialization of beans that the current bean depends on.
                //检查bean配置是否设置了depend-on属性。即实例A需要先实例B
                //获取 当前 Bean 的 所有 依赖 Bean, 这样 会 触发 getBean 的 递归 调用, 直到 取 到 一个 没有
                // 任何 依赖 的 Bean 为止
                String[] dependsOn = mbd.getDependsOn();
				if (dependsOn != null) {
					for (String dep : dependsOn) {
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
						registerDependentBean(dep, beanName);
						try {
							getBean(dep);
						}
						catch (NoSuchBeanDefinitionException ex) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
						}
					}
				}
                /*这里 通过 调用 createBean 方法 创建 Singleton bean 的 实例, 这里 有一个 回 调 函数 getObject, 会在 getSingleton 中 调用 ObjectFactory 的 createBean*/

                // Create bean instance.
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
						@Override
						public Object getObject() throws BeansException {
							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, new ObjectFactory<Object>() {
							@Override
							public Object getObject() throws BeansException {
								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.
        // 这里 对 创建 的 Bean 进行 类型 检查, 如果 没有 问题, 就 返回 这个 新 创建 的 Bean, 这个 Bean 已经 //是 包含 了 依赖 关系 的 Bean
        if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
			try {
				return getTypeConverter().convertIfNecessary(bean, requiredType);
			}
			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;
	}
【激光质量检测】利用丝杆与步进电机的组合装置带动光源的移动,完成对光源使用切片法测量其光束质量的目的研究(Matlab代码实现)内容概要:本文研究了利用丝杆与步进电机的组合装置带动光源移动,结合切片法实现对激光光源光束质量的精确测量方法,并提供了基于Matlab的代码实现方案。该系统通过机械装置精确控制光源位置,采集不同截面的光强分布数据,进而分析光束的聚焦特性、发散角、光斑尺寸等关键质量参数,适用于高精度光学检测场景。研究重点在于硬件控制与图像处理算法的协同设计,实现了自动化、高重复性的光束质量评估流程。; 适合人群:具备一定光学基础知识和Matlab编程能力的科研人员或工程技术人员,尤其适合从事激光应用、光电检测、精密仪器开发等相关领域的研究生及研发工程师。; 使用场景及目标:①实现对连续或脉冲激光器输出光束的质量评估;②为激光加工、医疗激光、通信激光等应用场景提供可靠的光束分析手段;③通过Matlab仿真与实际控制对接,验证切片法测量方案的有效性与精度。; 阅读建议:建议读者结合机械控制原理与光学测量理论同步理解文档内容,重点关注步进电机控制逻辑与切片数据处理算法的衔接部分,实际应用时需校准装置并优化采样间距以提高测量精度。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值