首页 > 其他分享 >05document转为BeanDefinition并注册过程

05document转为BeanDefinition并注册过程

时间:2024-07-22 22:40:35浏览次数:8  
标签:05document String beanName ele bean delegate null 转为 BeanDefinition

接着之前的文章4继续分析,文章4里的步骤七里xmlReader(XmlBeanDefinitionReader)的
registerBeanDefinitions(doc, resource)方法里最终走到调用他的好友
documentReader.registerBeanDefinitions(doc, createReaderContext(resource))方法

Ⅰ、代码流程

一、程序入口位于documentReader的registerBeanDefinitions方法
//DefaultBeanDefinitionDocumentReader.registerBeanDefinitions
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
	this.readerContext = readerContext;
	logger.debug("Loading bean definitions");
	Element root = doc.getDocumentElement();//获得docuemnt的根元素
	doRegisterBeanDefinitions(root);
}

protected void doRegisterBeanDefinitions(Element root) {
	//1、检查根节点的profile属性
	String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
	if (StringUtils.hasText(profileSpec)) {
		//this.environment就是beanFactory的environment即StandardEnvironment 
		Assert.state(this.environment != null, "Environment must be set for evaluating profiles");
		//解析得到比如dev,uat这样的数组
		String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
				profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
		//让environment去判断spring.profiles.active或SPRING_PROFILES_ACTIVE的值是否包含dev(uat),
		//包含说明当前整个xml是active的,可以继续,否则直接return
		if (!this.environment.acceptsProfiles(specifiedProfiles)) {
			return;
		}
	}

	//2、创建委托对象,交给委托对象解析<beans>标签
	// Any nested <beans> elements will cause recursion in this method. In
	// order to propagate and preserve <beans> default-* attributes correctly,
	// keep track of the current (parent) delegate, which may be null. Create
	// the new (child) delegate with a reference to the parent for fallback purposes,
	// then ultimately reset this.delegate back to its original (parent) reference.
	// this behavior emulates a stack of delegates without actually necessitating one.
	BeanDefinitionParserDelegate parent = this.delegate;//表面上看该属性为空,但是当
	//<beans>里面嵌套有<beans>时会引起递归调用当前方法,此时该属性不为空,该对象
	//表示上次解析<beans>时的委托对象,每一个delegate对象对应一个<beans>标签,
	//当存在内外嵌套<beans>场景时,内层的部分属性继承外层的,所以递归到内层
	//<beans>时要拿到外层的delegate对象做相关处理
	this.delegate = createDelegate(this.readerContext, root, parent);//就是BeanDefinitionParserDelegate

	preProcessXml(root);//模板空方法,允许开发者覆盖之后,实现在解析document之前先解析xml里的自定义标签
	parseBeanDefinitions(root, this.delegate);
	postProcessXml(root);//同preProcessXml,也是空方法,实现扩展功能用

	this.delegate = parent;
}

protected BeanDefinitionParserDelegate createDelegate(
		XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {
	//这句createHelper方法创建了个寂寞
	BeanDefinitionParserDelegate delegate = createHelper(readerContext, root, parentDelegate);
	if (delegate == null) {
		delegate = new BeanDefinitionParserDelegate(readerContext, this.environment);
		delegate.initDefaults(root, parentDelegate);
	}
	return delegate;
}

@Deprecated
protected BeanDefinitionParserDelegate createHelper(
		XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {
	return null;
}


二、documentReader开始解析root节点
delegate作为documentReader的助手角色参与其中
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
	//如果是默认命名空间
	if (delegate.isDefaultNamespace(root)) {//正常进入该分支
		NodeList nl = root.getChildNodes();
		//遍历<beans>下的所有节点
		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)) {//跟上面一样正常进入该分支
					parseDefaultElement(ele, delegate);
				}
				else {
					delegate.parseCustomElement(ele);
				}
			}
		}
	}
	//否则说明是自定义节点
	else {
		delegate.parseCustomElement(root);
	}
}

//delegate.isDefaultNamespace
public boolean isDefaultNamespace(Node node) {
	return isDefaultNamespace(getNamespaceURI(node));
}

//delegate.getNamespaceURI
public String getNamespaceURI(Node node) {
	return node.getNamespaceURI();//比如当前是<beans>则返回http://www.springframework.org/schema/beans
}

//delegate.isDefaultNamespace
public boolean isDefaultNamespace(String namespaceUri) {
	return (!StringUtils.hasLength(namespaceUri) || BEANS_NAMESPACE_URI.equals(namespaceUri));
	//内部有个常量public static final String BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans";
}


三、documentReader开始解析root下的子节点,比如<bean>、<import>、<alise>、<beans>
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
	if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
		importBeanDefinitionResource(ele);
	}
	else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
		processAliasRegistration(ele);
	}
	else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
		processBeanDefinition(ele, delegate);
	}
	else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
		// recurse
		doRegisterBeanDefinitions(ele);//这里就会递归到上面步骤一的doRegisterBeanDefinitions方法里
	}
}

protected void importBeanDefinitionResource(Element ele) {
	//1、解析import标签的resource属性得到被引入xml的路径字符串
	String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
	//2、利用environment把带有占位符的路径字符串变成真正的路径字符串
	location = environment.resolveRequiredPlaceholders(location);
	//3、借助之前的xmlReader去再次解析xml
	//context类似threadLocal用法,该上下文里的reader就是xmlReader,等于
	//程序执行流程又回到了文章4的步骤6的第三个loadBeanDefinitions方法。
	int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
	//4、发出事件,触发关心者去处理import事件,暂时不考虑那么细,略
	getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}

//暂时略
protected void processAliasRegistration(Element ele) {
}


四、documentReader开始解析<bean>节点,交给助手delegate去做
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
	//1、交给delegate去解析<bean>节点,注意这里得到的是holder对象
	BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
	if (bdHolder != null) {
	
		//2、判断如果有自定义属性调用自定义处理器装饰下bdHolder对象
		bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
		//decorateBeanDefinitionIfRequired方法里判断如果当前<bean>存在自定义
		//属性,则调用NamespaceHandlerResolver对象根据自定义属性的命名空间得到
		//NamespaceHandler命名空间处理器,用该处理器解析属性得到被装饰后的
		//holder对象(就是上面bdHolder,等于这里调用开发者的处理器把bdHolder进行处理了)
		//该方法代码见附件2
		try {
			//3、注册bdHolder到beanFactory里,getReaderContext().getRegistry()是beanFactory
			// Register the final decorated instance.
			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));
	}
}

五、delegate开始解析<bean>标签
//delegate.parseBeanDefinitionElement(ele)
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
	return parseBeanDefinitionElement(ele, null);
}

//delegate.parseBeanDefinitionElement(ele, containingBean)
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
	//1、解析id和name属性
	String id = ele.getAttribute(ID_ATTRIBUTE);
	String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
	
	//2、判断name属性如果带逗号说明是别名,分割后加入别名list
	List<String> aliases = new ArrayList<String>();
	if (StringUtils.hasLength(nameAttr)) {
		String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
		aliases.addAll(Arrays.asList(nameArr));
	}
	
	//3、如果id为空并且别名list不为空时,取第一个别名作为beanName
	String beanName = id;
	if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
		beanName = aliases.remove(0);
		if (logger.isDebugEnabled()) {
			logger.debug("No XML 'id' specified - using '" + beanName +
					"' as bean name and " + aliases + " as aliases");
		}
	}
	
	//4、检查beanName必须唯一,不唯一就报错
	if (containingBean == null) {//正常进入该分支
		checkNameUniqueness(beanName, aliases, ele);//通过usedNames检查beanName是否唯一
	}
	
	//5、得到Definition对象
	AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
	if (beanDefinition != null) {
		if (!StringUtils.hasText(beanName)) {//正常不会进入该分支,如果xml里去掉id="person"则进入这里
			try {
				if (containingBean != null) {
					beanName = BeanDefinitionReaderUtils.generateBeanName(
							beanDefinition, this.readerContext.getRegistry(), true);
				}
				else {
					beanName = this.readerContext.generateBeanName(beanDefinition);//DefaultBeanNameGenerator.generateBeanName-->BeanDefinitionReaderUtils.generateBeanName内部规则为class完全限定名learn.dto.Person+#+序号,这里是learn.dto.Person#0
					
					//为了向后兼容,用完全限定名作为别名,这里beanClassName是learn.dto.Person
					// Register an alias for the plain bean class name, if still possible,
					// if the generator returned the class name plus a suffix.
					// This is expected for Spring 1.2/2.0 backwards compatibility.
					String beanClassName = beanDefinition.getBeanClassName();
					if (beanClassName != null &&
							beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
							!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
						aliases.add(beanClassName);//beanClassName是learn.dto.Person
					}
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Neither XML 'id' nor 'name' specified - " +
							"using generated bean name [" + beanName + "]");
				}
			}
			catch (Exception ex) {
				error(ex.getMessage(), ele);
				return null;
			}
		}
		String[] aliasesArray = StringUtils.toStringArray(aliases);
		//6、返回holder,即BeanDefinitionHolder对象。
		//BeanDefinitionHolder参见:https://blog.csdn.net/cgsyck/article/details/88880196
		return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
	}
	return null;
}

public AbstractBeanDefinition parseBeanDefinitionElement(
		Element ele, String beanName, BeanDefinition containingBean) {
	//1、parseState是模仿栈的数据结构,用于记录当前解析进度,方便错误信息输出,略
	this.parseState.push(new BeanEntry(beanName));

	//2、解析节点的class属性
	String className = null;
	if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
		className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
	}

	try {
		//3、解析节点的parent属性
		String parent = null;
		if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
			parent = ele.getAttribute(PARENT_ATTRIBUTE);
		}
		
		//4、创建BeanDefinition实例对象
		AbstractBeanDefinition bd = createBeanDefinition(className, parent);//就是new GenericBeanDefinition

		//5、解析节点的scope、singleton、abstrace、lazy-init、autowire、dependency-check、
		//depends-on、autowire-candidate、primary、init-method、destroy-method、factory-method、
		//factory-bean属性,把这些属性的值设置到bd的相应属性里。
		parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);//方法代码下面不列了
		
		//6、解析节点下的<description>子节点,把该子节点的值设置到相应属性里
		bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

		//7、解析节点下的子节点的标签值是否等于meta,没细看,暂时略
		parseMetaElements(ele, bd);
		
		//8、解析节点下的子节点的标签值是否等于lookup-method,暂时略
		//参见:https://blog.csdn.net/weixin_46909938/article/details/138535529
		parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
		
		//9、解析节点下的子节点的标签值是否等于replaced-method,暂时略
		//参见:https://www.jianshu.com/p/06f71d241866
		parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

		//10、解析构造参数
		parseConstructorArgElements(ele, bd);
		
		//11、解析bean的属性
		parsePropertyElements(ele, bd);
		
		//12、解析qualifier子元素,暂时略
		parseQualifierElements(ele, bd);

		bd.setResource(this.readerContext.getResource());//这个Resource对象就是applicationContext
		//一开始把路径字符串得到的Resource对象
		bd.setSource(extractSource(ele));//暂时略

		return bd;
	}
	catch (ClassNotFoundException ex) {
		error("Bean class [" + className + "] not found", ele, ex);
	}
	catch (NoClassDefFoundError err) {
		error("Class that bean class [" + className + "] depends on not found", ele, err);
	}
	catch (Throwable ex) {
		error("Unexpected failure during bean definition parsing", ele, ex);
	}
	finally {
		this.parseState.pop();
	}

	return null;
}

protected AbstractBeanDefinition createBeanDefinition(String className, String parentName){
	return BeanDefinitionReaderUtils.createBeanDefinition(
			parentName, className, this.readerContext.getBeanClassLoader());
	//this.readerContext.getBeanClassLoader()就是xmlReader.getBeanClassLoader(),此处
	//的ClassLoader为null
}

//BeanDefinitionReaderUtils.createBeanDefinition()
public static AbstractBeanDefinition createBeanDefinition(
		String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException {

	GenericBeanDefinition bd = new GenericBeanDefinition();
	bd.setParentName(parentName);
	if (className != null) {
		if (classLoader != null) {
			bd.setBeanClass(ClassUtils.forName(className, classLoader));
		}
		else {//正常走这个分支
			bd.setBeanClassName(className);
		}
	}
	return bd;
}

六、beanFactory注册BeanDefinition
//BeanDefinitionReaderUtils.registerBeanDefinition
public static void registerBeanDefinition(
		BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
		throws BeanDefinitionStoreException {

	// Register bean definition under primary name.
	String beanName = definitionHolder.getBeanName();
	
	//调用beanFactory对象的方法来进行注册,详细参见附录2
	registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

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

Ⅱ、总结:
spring框架通过documentReader和delegate完成了document到BeanDefinition的转换工作。

附录:
1、delegate对象的状态bdHolder对象方法

public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder) {
	return decorateBeanDefinitionIfRequired(ele, definitionHolder, null);
}

public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
		Element ele, BeanDefinitionHolder definitionHolder, BeanDefinition containingBd) {

	BeanDefinitionHolder finalDefinition = definitionHolder;

	// Decorate based on custom attributes first.
	NamedNodeMap attributes = ele.getAttributes();
	for (int i = 0; i < attributes.getLength(); i++) {
		Node node = attributes.item(i);
		finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
	}

	// Decorate based on custom nested elements.
	NodeList children = ele.getChildNodes();
	for (int i = 0; i < children.getLength(); i++) {
		Node node = children.item(i);
		if (node.getNodeType() == Node.ELEMENT_NODE) {
			finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
		}
	}
	return finalDefinition;
}

3、beanFactory注册beanDefinition过程

//DefaultListableBeanFactory.registerBeanDefinition
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
		throws BeanDefinitionStoreException {

	Assert.hasText(beanName, "Bean name must not be empty");
	Assert.notNull(beanDefinition, "BeanDefinition must not be null");

	//1、校验bd对象是否有效
	//GenericBeanDefinition父类是AbstractBeanDefinition,所以会进入该分支
	if (beanDefinition instanceof AbstractBeanDefinition) {
		try {
			//validate方法内部校验规则
			//1、为不能同时存在“方法覆盖methodOverrides”和“factoryMethodName”这两种配置项,
			//2、如果配了方法覆盖,校验要覆盖的方法名在对应bean的class里有没有,没有报错
			((AbstractBeanDefinition) beanDefinition).validate();
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
					"Validation of bean definition failed", ex);
		}
	}

	synchronized (this.beanDefinitionMap) {
		Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
		//按道理应该为null,想不到什么场景下会不为null
		if (oldBeanDefinition != null) {
			if (!this.allowBeanDefinitionOverriding) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
						"': There is already [" + oldBeanDefinition + "] bound.");
			}
			else {
				if (this.logger.isInfoEnabled()) {
					this.logger.info("Overriding bean definition for bean '" + beanName +
							"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
				}
			}
		}
		//正常进入该分支
		else {
			this.beanDefinitionNames.add(beanName);//加入到beanDefinitionNames
			this.frozenBeanDefinitionNames = null;
		}
		this.beanDefinitionMap.put(beanName, beanDefinition);//加入到beanDefinitionMap
	}

	//重置当前bean
	resetBeanDefinition(beanName);
}

//重置当前bean,即清空缓存
protected void resetBeanDefinition(String beanName) {
	//1、这个不清楚,暂时略
	// Remove the merged bean definition for the given bean, if already created.
	clearMergedBeanDefinition(beanName);

	// Remove corresponding bean from singleton cache, if any. Shouldn't usually
	// be necessary, rather just meant for overriding a context's default beans
	// (e.g. the default StaticMessageSource in a StaticApplicationContext).
	//2、从缓存里销毁bean实例
	destroySingleton(beanName);

	//3、清空allBeanNamesByType和singletonBeanNamesByType
	// Remove any assumptions about by-type mappings.
	clearByTypeCache();//->this.allBeanNamesByType.clear();this.singletonBeanNamesByType.clear();

	//4、把当前bean所有子递归的进行重置操作
	// Reset all bean definitions that have the given bean as parent (recursively).
	for (String bdName : this.beanDefinitionNames) {
		if (!beanName.equals(bdName)) {
			BeanDefinition bd = this.beanDefinitionMap.get(bdName);
			if (beanName.equals(bd.getParentName())) {
				resetBeanDefinition(bdName);
			}
		}
	}
}

protected void clearMergedBeanDefinition(String beanName) {
	this.mergedBeanDefinitions.remove(beanName);
}

public void destroySingleton(String beanName) {
	// Remove a registered singleton of the given name, if any.
	removeSingleton(beanName);

	// Destroy the corresponding DisposableBean instance.
	DisposableBean disposableBean;
	synchronized (this.disposableBeans) {
		disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
	}
	destroyBean(beanName, disposableBean);
}

protected void removeSingleton(String beanName) {
	synchronized (this.singletonObjects) {
		this.singletonObjects.remove(beanName);
		this.singletonFactories.remove(beanName);
		this.earlySingletonObjects.remove(beanName);
		this.registeredSingletons.remove(beanName);
	}
}

private void clearByTypeCache() {
	this.allBeanNamesByType.clear();
	this.singletonBeanNamesByType.clear();
}

标签:05document,String,beanName,ele,bean,delegate,null,转为,BeanDefinition
From: https://www.cnblogs.com/bplan/p/18317161

相关文章

  • 将Json格式的文件转为Excel格式文件的python代码
    importpandasaspdimportosfromopenpyxlimportWorkbook'''pandas是Python中用于数据分析的一个非常强大的库,它提供了快速、灵活和表达式丰富的数据结构,旨在使“关系”或“标签”数据的处理工作变得既简单又直观。pandas非常适合于处理表格数据,如CSV文件、SQL查询结......
  • .NET|--杂类|--将Shp文件转为GeoJson-通过GDAL
    前言真实需求是将Shp转为pbf文件,不过我现在已经实现了,将GeoJson格式数据转换为pbf文件,所以需要实现将Shp文件转换为GeoJson格式即可.1.下载GDAL的程序集下载地址→https://www.gisinternals.com/development.php下载完成,解压zip文件,可以看到这些dll文件(路......
  • java把m3u8视频转为mp4
    java把m3u8视频转为mp4代码importjava.io.*;importjava.nio.charset.Charset;importjava.nio.file.Path;importjava.nio.file.Files;importjava.util.ArrayList;importjava.util.List;importjava.util.concurrent.TimeUnit;/***@Title:Process*@Authorcx......
  • sqlalchemy pandas转化字典转为orm写入到sqlite数据库报错类型错误的解决办法
    使用pandas读取csv数据,然后将其转化为字典,再写入到数据库的时候,数据库总是报错类型错误,于是转为orm之前,统一转化一下类型fromsqlalchemyimportDECIMAL,Index,String,Date,Integer,Text,CHAR,SmallInteger,Float,Time,case,and_,extract,TypeDecoratorfrom......
  • php将png转为jpg,可设置压缩率
    /***将PNG文件转换为JPG文件*@param$pngFilePathstringPNG文件路径*@param$jpgFilePathstringJPG文件路径*@param$qualityintJPG质量,0-100,值越低,压缩率越高*@returnvoid*@throwsException*/functionconvertPngToJpg($pngFilePath,$j......
  • R语言数据格式转换:字符串(Strings)转为日期类型(Dates)
     R语言数据格式转换:字符串(Strings)转为日期类型(Dates)目录 R语言数据格式转换:字符串(Strings)转为日期类型(Dates)as.Date函数单个字符串到日期类型字符串向量到日期类型向量dataframe一列从字符串到日期类型dataframe多列从字符串到日期类型 as.Date函数通常,当您......
  • 将svg格式的图片转为png格式的图
    ""D:\桌面\素材2-副本\coffee.svg"先安装Python中的cairosvg库pipinstallcairosvgimportcairosvgdefconvert_svg_to_png(svg_file_path,png_file_path):"""将SVG文件转换为PNG文件。:paramsvg_file_path:SVG文件的路径。:paramp......
  • 【java-POI】如何将一个WorkBook转为一个InputStream?
    /***利用workBook创建一个输入流用于后续操作**@return*/privateInputStreamcreateInputSream(){if(inputStream!=null){try{inputStream.reset();returninputStream;......
  • hyperf 生成二维码并且转为CMYK色彩通道的图片
    注意:CMYK色彩通道的图片格式需要为JPEG或TIFF,png是不支持CMYK的,不然转换的话会转换会srgb或Gray使用前先安装imagick拓展1{2"require":{3"ext-imagick":"*"4}5}  1publicfunctioncreateQrcode($data):void2{3//......
  • Spring中GenericBeanDefinition属性
    1.id再Spring容器中作为Bean的唯一标识2.name用于为id属性创建一个或多个别名,用空格、逗号分开3.classBean的类名,全限定名primary多个相同Bean再容器中的优先级5.parent标注该Bean的父类Bean,继承父类Bean的所有属性6.abstract标注Bean是否是抽象的,一般用于父类Bean。......