Spring IOC:ComponentScanBeanDefinitionParser调用链
参考资料:
《Spring IoC源码学习:context:component-scan 节点详解》
前文:
《Spring IOC:parseCustomElement调用链》
写在开头:本文为个人学习笔记,内容比较随意,夹杂个人理解,如有错误,欢迎指正。
目录
一、parse
二、configureScanner
1、configureScanner
2、createScanner
3、registerDefaultFilters
4、parseTypeFilters
5、createTypeFilter
三、doScan
1、doScan
2、 findCandidateComponents
2.1、isCandidateComponent
2.2、isCandidateComponent
3、resolveScopeMetadata
4、postProcessBeanDefinition
5、processCommonDefinitionAnnotations
6、checkCandidate
7、applyScopedProxyMode
8、registerBeanDefinition
三、registerComponents
1、registerComponents
2、registerAnnotationConfigProcessors
总结:
一、parse
在前文中,我们了解到当 element 为 <context:component-scan /> 节点时,findParserForElement(element, parserContext) 会返回 ComponentScanBeanDefinitionParser,接着执行 ComponentScanBeanDefinitionParser类的parse 方法。
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {// 1.findParserForElement: 给element寻找对应的BeanDefinition解析器// 2.使用BeanDefinition解析器解析element节点return findParserForElement(element, parserContext).parse(element, parserContext);
}
在parse方法中,我们首先解析base-package属性,拿到要扫描的路径,进行解析,如果有使用了占位符的还需要进行替换,如果有使用了分隔符","或者";"的,还需要拆分成数组。然后调用configureScanner方法构建ClassPathBeanDefinitionScanner来为后续扫描做准备。拿到ClassPathBeanDefinitionScanner后,调用其doScan方法,将扫描路径下使用了相关注解的类加入到beanDefinition中。最后,再注册其余的组件,如后置处理器等。
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {// 拿到<context:component-scan>节点的base-package属性值String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);// 解析占位符, 例如 ${basePackage}basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);// 解析base-package(允许通过 ",; \t\n" 中的任一符号填写多个)// String CONFIG_LOCATION_DELIMITERS = ",; \t\n";String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);// Actually scan for bean definitions and register them.// 构建和配置ClassPathBeanDefinitionScannerClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);// 使用scanner在指定的basePackages包中执行扫描,返回已注册的bean定义Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);// 组件注册(包括注册一些内部的注解后置处理器、触发注册事件)registerComponents(parserContext.getReaderContext(), beanDefinitions, element);return null;
}
二、configureScanner
1、configureScanner
进入configureScanner方法,会先读取该节点下是否配置了use-default-filters属性,该属性用来指定是否使用默认的过滤器(注意是过滤器,不是过滤规则)。然后调用createScanner方法创建扫描器,创建完毕后,再解析一些其他属性,如resource-pattern、name-generator等。最后调用parseTypeFilters方法解析类型过滤器。
protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {// 解析use-default-filters属性,默认为true,用于指示是否使用默认的filterboolean useDefaultFilters = true;if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));}// Delegate bean definition registration to scanner class.// 构建ClassPathBeanDefinitionScanner,将bean定义注册委托给scanner类ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters);scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());// 解析resource-pattern属性if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) {scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE));}try {// 解析name-generator属性parseBeanNameGenerator(element, scanner);}catch (Exception ex) {parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());}try {// 解析scope-resolver、scoped-proxy属性parseScope(element, scanner);}catch (Exception ex) {parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());}// 解析类型过滤器parseTypeFilters(element, scanner, parserContext);return scanner;
}
2、createScanner
createScanner方法判断是否使用默认的filter,如果是,则继续调用registerDefaultFilters方法
protected ClassPathBeanDefinitionScanner createScanner(XmlReaderContext readerContext, boolean useDefaultFilters) {return new ClassPathBeanDefinitionScanner(readerContext.getRegistry(), useDefaultFilters,readerContext.getEnvironment(), readerContext.getResourceLoader());
}// ClassPathBeanDefinitionScanner.java
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,Environment environment, ResourceLoader resourceLoader) {Assert.notNull(registry, "BeanDefinitionRegistry must not be null");this.registry = registry;if (useDefaultFilters) {// 注册默认的filterregisterDefaultFilters();}setEnvironment(environment);setResourceLoader(resourceLoader);
}
3、registerDefaultFilters
registerDefaultFilters方法会将@Component和@ManagedBean对应的filter加入到includeFilters中。
// ClassPathScanningCandidateComponentProvider.java
protected void registerDefaultFilters() {// 添加@Component注解Filter到includeFilters中this.includeFilters.add(new AnnotationTypeFilter(Component.class));ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();try {// 添加@ManagedBean注解Filter到includeFilters中this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");}catch (ClassNotFoundException ex) {// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.}try {// 添加@Named注解Filter到includeFilters中this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");}catch (ClassNotFoundException ex) {// JSR-330 API not available - simply skip.}
}
4、parseTypeFilters
解析当前节点下的子节点,读取可能存在的include-filter和exclude-filter节点,然后针对读取到的属性调用createTypeFilter方法创建typeFilter,并加入到扫描器中。
protected void parseTypeFilters(Element element, ClassPathBeanDefinitionScanner scanner, ParserContext parserContext) {// Parse exclude and include filter elements.ClassLoader classLoader = scanner.getResourceLoader().getClassLoader();NodeList nodeList = element.getChildNodes();// 遍历解析element下的所有子节点for (int i = 0; i < nodeList.getLength(); i++) {Node node = nodeList.item(i);if (node.getNodeType() == Node.ELEMENT_NODE) {// 拿到节点的localName// 例如节点:<context:exclude-filter type="" expression=""/>,localName为:exclude-filterString localName = parserContext.getDelegate().getLocalName(node);try {/*** 例如* <context:component-scan base-package="com.joonwhee.open">* <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>* </context:component-scan>*/// 解析include-filter子节点if (INCLUDE_FILTER_ELEMENT.equals(localName)) {// 构建TypeFilterTypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext);// 添加到scanner的includeFilters属性scanner.addIncludeFilter(typeFilter);}// 解析exclude-filter子节点else if (EXCLUDE_FILTER_ELEMENT.equals(localName)) {// 构建TypeFilterTypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext);// 添加到scanner的excludeFilters属性scanner.addExcludeFilter(typeFilter);}} catch (Exception ex) {parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());}}}
}
5、createTypeFilter
读取include-filter和exclude-filter节点中的type、expression属性,根据不同的属性创建AnnotationTypeFilter。比如type="annotation"时,表明按注解过滤,expression中设置注解的路径,如expression="org.springframework.stereotype.Controller",则会过滤掉@Controller注解。
protected TypeFilter createTypeFilter(Element element, ClassLoader classLoader, ParserContext parserContext) {// 获取type、expressionString filterType = element.getAttribute(FILTER_TYPE_ATTRIBUTE);String expression = element.getAttribute(FILTER_EXPRESSION_ATTRIBUTE);expression = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(expression);try {// 根据filterType,返回对应的TypeFilter,例如annotation返回AnnotationTypeFilterif ("annotation".equals(filterType)) {// 指定过滤的注解, expression为注解的类全名称, 例如: org.springframework.stereotype.Controllerreturn new AnnotationTypeFilter((Class<Annotation>) classLoader.loadClass(expression));}else if ("assignable".equals(filterType)) {// 指定过滤的类或接口, 包括子类和子接口, expression为类全名称return new AssignableTypeFilter(classLoader.loadClass(expression));}else if ("aspectj".equals(filterType)) {// 指定aspectj表达式来过滤类, expression为aspectj表达式字符串return new AspectJTypeFilter(expression, classLoader);}else if ("regex".equals(filterType)) {// 通过正则表达式来过滤类, expression为正则表达式字符串return new RegexPatternTypeFilter(Pattern.compile(expression));}else if ("custom".equals(filterType)) {// 用户自定义过滤器类型, expression为自定义过滤器的类全名称Class<?> filterClass = classLoader.loadClass(expression);// 自定义的过滤器必须实现TypeFilter接口, 否则抛异常if (!TypeFilter.class.isAssignableFrom(filterClass)) {throw new IllegalArgumentException("Class is not assignable to [" + TypeFilter.class.getName() + "]: " + expression);}return (TypeFilter) BeanUtils.instantiateClass(filterClass);}else {throw new IllegalArgumentException("Unsupported filter type: " + filterType);}}catch (ClassNotFoundException ex) {throw new FatalBeanException("Type filter class not found: " + expression, ex);}
}
三、doScan
1、doScan
doScan方法会调用findCandidateComponents将符合要求的bean都找出来(即使用了注解,且满足filter过滤规则的bean)。遍历所有符合要求的bean,解析@Scope,生成beanName,然后调用postProcessBeanDefinition、processCommonDefinitionAnnotations进一步解析候选bean。后续再封装BeanDefinitionHolder,然后根据proxyMode的值选择是否创建作用域代理,最后注册BeanDefinition(即beanDefinitionMap、beanDefinitionNames、aliasMap这三个缓存)
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {Assert.notEmpty(basePackages, "At least one base package must be specified");Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();// 遍历basePackagesfor (String basePackage : basePackages) {// 扫描basePackage,将符合要求的bean定义全部找出来(这边符合要求最常见的就是使用Component注解)Set<BeanDefinition> candidates = findCandidateComponents(basePackage);// 遍历所有候选的bean定义for (BeanDefinition candidate : candidates) {// 解析@Scope注解, 包括scopeName(默认为singleton,常见的还有prototype), 和proxyMode(默认不使用代理, 可选接口代理/类代理)ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);candidate.setScope(scopeMetadata.getScopeName());// 使用beanName生成器来生成beanNameString beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);if (candidate instanceof AbstractBeanDefinition) {// 进一步处理BeanDefinition对象,比如: 此bean是否可以自动装配到其他bean中postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);}if (candidate instanceof AnnotatedBeanDefinition) {// 处理定义在目标类上的通用注解,包括@Lazy, @Primary, @DependsOn, @Role, @DescriptionAnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);}// 检查beanName是否已经注册过,如果注册过,检查是否兼容if (checkCandidate(beanName, candidate)) {// 将当前遍历bean的 bean定义和beanName封装成BeanDefinitionHolderBeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);// 根据proxyMode的值(步骤4中解析), 选择是否创建作用域代理definitionHolder =AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);beanDefinitions.add(definitionHolder);// 注册BeanDefinition(注册到beanDefinitionMap、beanDefinitionNames、aliasMap缓存)registerBeanDefinition(definitionHolder, this.registry);}}}return beanDefinitions;
}
2、 findCandidateComponents
读取配置的路径,将所有的类都加载进来,并使用过滤器检查给定的类是否为候选类,如满足要求,则加入到candidates中
public Set<BeanDefinition> findCandidateComponents(String basePackage) {Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();try {// 根据我们配置的包名,组装成要扫描的通配包路径,例如:com.joonwhee.open 会被组装成: classpath*:com/joonwhee/open/**/*.classString packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +resolveBasePackage(basePackage) + '/' + this.resourcePattern;// 根据通配包路径匹配拿到所有匹配的类资源(本项目依赖的jar,如果路径也符合,则会一起扫描进来)Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);boolean traceEnabled = logger.isTraceEnabled();boolean debugEnabled = logger.isDebugEnabled();// 遍历所有匹配的类资源for (Resource resource : resources) {if (traceEnabled) {logger.trace("Scanning " + resource);}if (resource.isReadable()) {try {// 使用metadataReader读取资源,MetadataReader是专门用来访问元数据的类(包括: 类元数据ClassMetadata、注解元数据AnnotationMetadata等)MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);// 使用过滤器检查给定的类是否为候选类(候选类: 与excludeFilters的所有Filter不匹配,并且与includeFilters的至少一个Filter匹配)if (isCandidateComponent(metadataReader)) {ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);sbd.setResource(resource);sbd.setSource(resource);// 判断sbd是否为候选类(独立的 && (具体的实现类 || (抽象类 && 类中有方法使用@Lookup注解)))if (isCandidateComponent(sbd)) {if (debugEnabled) {logger.debug("Identified candidate component class: " + resource);}// 确定是候选类,则添加到candidatescandidates.add(sbd);}else {if (debugEnabled) {logger.debug("Ignored because not a concrete top-level class: " + resource);}}}else {if (traceEnabled) {logger.trace("Ignored because not matching any filter: " + resource);}}}catch (Throwable ex) {throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, ex);}}else {if (traceEnabled) {logger.trace("Ignored because not readable: " + resource);}}}}catch (IOException ex) {throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);}return candidates;
}
2.1、isCandidateComponent
判断metadataReader(MetadataReader是专门用来访问元数据的类)是否与excludeFilters 和 includeFilters中的TypeFilter相匹配。
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {for (TypeFilter tf : this.excludeFilters) {if (tf.match(metadataReader, this.metadataReaderFactory)) {// 如果metadataReader与excludeFilters中的任意一个匹配,则返回false,表示metadataReader对应的类不是候选者类return false;}}// includeFilters默认包含: org.springframework.stereotype.Component注解、javax.annotation.ManagedBean注解for (TypeFilter tf : this.includeFilters) {if (tf.match(metadataReader, this.metadataReaderFactory)) {// 如果metadataReader与includeFilters中的任意一个TypeFilter匹配(如果tf为Component注解:metadataReader对应的类使用了Component则匹配),// 则判断@Conditional注解是否匹配(@Conditional基本不用,此处不深入解析);如果匹配,则返回true,表示metadataReader对应的类为候选者类return isConditionMatch(metadataReader);}}return false;
}
2.2、isCandidateComponent
继续判断该类是否满足要求。
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {AnnotationMetadata metadata = beanDefinition.getMetadata();// isIndependent:确定底层类是否是独立的,即它是否是顶级类或嵌套类(静态内部类),它可以独立于封闭类构造。// isConcrete:返回底层类是表示具体类,即:既不是接口也不是抽象类。// isAbstract:返回底层类是否标记为抽象。// hasAnnotatedMethods:确定基础类是否具有使用给定注解(@Lookup)类型进行注解(或元注解)的任何方法。return (metadata.isIndependent() && (metadata.isConcrete() ||(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}
3、resolveScopeMetadata
解析@Scope注解,判断是否使用代理模式。defaultProxyMode取决前文中parseScope方法读取到的scope-resolver、scoped-proxy属性,可以通过 scoped-proxy 来设置,默认为 ScopedProxyMode.NO。
@Override
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {ScopeMetadata metadata = new ScopeMetadata();if (definition instanceof AnnotatedBeanDefinition) {AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(annDef.getMetadata(), this.scopeAnnotationType);// 如果使用了@Scope注解if (attributes != null) {// 解析scopeName属性metadata.setScopeName(attributes.getString("value"));// 解析proxyMode属性ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");if (proxyMode == null || proxyMode == ScopedProxyMode.DEFAULT) {proxyMode = this.defaultProxyMode;}metadata.setScopedProxyMode(proxyMode);}}return metadata;
}
4、postProcessBeanDefinition
这一步给beanDefinition设置默认值,同时读取autowire-candidate,该属性用来设置某个bean是否在自动注入的时候是否为作为候选bean。
protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {// 给beanDefinition设置默认值beanDefinition.applyDefaults(this.beanDefinitionDefaults);if (this.autowireCandidatePatterns != null) {// 设置此bean是否可以自动装配到其他bean中, 默认为truebeanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));}
}
<bean id="beantest" class="com.test" autowire-candidate="false"/>
5、processCommonDefinitionAnnotations
继续解析常用属性,包括@Lazy, @Primary, @DependsOn, @Role, @Description。
public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {processCommonDefinitionAnnotations(abd, abd.getMetadata());
}static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {// 解析@Lazy注解, 设置是否延迟加载if (metadata.isAnnotated(Lazy.class.getName())) {abd.setLazyInit(attributesFor(metadata, Lazy.class).getBoolean("value"));}else if (abd.getMetadata() != metadata && abd.getMetadata().isAnnotated(Lazy.class.getName())) {abd.setLazyInit(attributesFor(abd.getMetadata(), Lazy.class).getBoolean("value"));}// 解析@Primary注解, 自动装配时当出现多个Bean都匹配时,被注解为@Primary的Bean将作为首选者,否则将抛出异常// (场景较小, 如果可能出现多个匹配者时, 可以使用@Autowired @Qualifier的组合)if (metadata.isAnnotated(Primary.class.getName())) {abd.setPrimary(true);}// 解析@DependOn注解if (metadata.isAnnotated(DependsOn.class.getName())) {abd.setDependsOn(attributesFor(metadata, DependsOn.class).getStringArray("value"));}if (abd instanceof AbstractBeanDefinition) {// 解析@Role注解AbstractBeanDefinition absBd = (AbstractBeanDefinition) abd;if (metadata.isAnnotated(Role.class.getName())) {absBd.setRole(attributesFor(metadata, Role.class).getNumber("value").intValue());}// 解析@Description注解if (metadata.isAnnotated(Description.class.getName())) {absBd.setDescription(attributesFor(metadata, Description.class).getString("value"));}}
}
6、checkCandidate
利用beanDefinitionMap判断该beanName是否已在缓存中,如不在,直接返回,如在,则取出该beanName在beanDefinitionMap中的BeanDefinition,继续判断,如使用了代理,则取出代理的BeanDefinition,并作为缓存中的值与当前要注册的BeanDefinition比较,判断是否兼容。
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {// 如果该注册表(beanDefinitionMap缓存)没有包含beanName, 则返回true,代表可以注册该bean定义if (!this.registry.containsBeanDefinition(beanName)) {return true;}// 如果注册表中包含beanName// 拿到注册表中该beanName的BeanDefinitionBeanDefinition existingDef = this.registry.getBeanDefinition(beanName);// 拿到原始BeanDefinition(使用了代理的BeanDefinition会有原始BeanDefinition)BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();if (originatingDef != null) {// 如果有原始BeanDefinition, 则使用原始BeanDefinitionexistingDef = originatingDef;}// 检查新BeanDefinition是否与原BeanDefinition兼容,如果兼容则返回false,跳过注册if (isCompatible(beanDefinition, existingDef)) {return false;}// 如果不兼容,则抛异常throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName +"' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +"non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");
}
7、applyScopedProxyMode
判断是否使用代理模式,如是,则继续判断是基于接口还是基于类的代理,前者使用CGlib代理,后者使用JDK动态代理,确认代理模式后,创建相应的scope代理。
static BeanDefinitionHolder applyScopedProxyMode(ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();// 如果不需要创建代理,则直接返回bean定义if (scopedProxyMode.equals(ScopedProxyMode.NO)) {return definition;}// 判断是使用基于类的代理还是基于接口的代码, 基于类: 使用CGLIB代理, 基于接口: 使用JDK动态代理boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);// 使用相应的代理模式, 创建一个scope代理return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
}
8、registerBeanDefinition
调用 BeanDefinitionReaderUtils 工具类来完成 BeanDefinition 的注册。此方法在默认命名空间节点解析中已介绍过(《Spring IOC:parseDefaultElement调用链》第5小节),这里不再赘述,主要作用在于注册beanDefinitionMap、beanDefinitionNames、aliasMap这三个缓存。
protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {// 调用BeanDefinitionReaderUtils工具类来完成BeanDefinition的注册BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
}
三、registerComponents
1、registerComponents
判断是否需要注册基于注解的bean后置处理器,默认为treu,然后调用registerAnnotationConfigProcessors方法进行注册。
protected void registerComponents(XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) {Object source = readerContext.extractSource(element);// 使用注解的tagName(例如: context:component-scan)和source 构建CompositeComponentDefinitionCompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);// 将扫描到的所有BeanDefinition添加到compositeDef的nestedComponents属性中for (BeanDefinitionHolder beanDefHolder : beanDefinitions) {compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder));}boolean annotationConfig = true;if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {// 获取component-scan标签的annotation-config属性值(默认为true)annotationConfig = Boolean.valueOf(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));}if (annotationConfig) {// 如果annotation-config属性值为true,在给定的注册表中注册所有用于注解的Bean后置处理器Set<BeanDefinitionHolder> processorDefinitions =AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);for (BeanDefinitionHolder processorDefinition : processorDefinitions) {// 将注册的注解后置处理器的BeanDefinition添加到compositeDef的nestedComponents属性中compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));}}// 触发组件注册事件,默认实现为EmptyReaderEventListener(空实现,没有具体操作)readerContext.fireComponentRegistered(compositeDef);
}
2、registerAnnotationConfigProcessors
判断是否包含常用注解的BeanDefinition,包括@Configuration、@Autowired、@Required、@Resource等基于后置处理器实现的注解,然后调用registerPostProcessor注册。
核心注册方法registerBeanDefinition 在《Spring IOC:parseDefaultElement调用链》中第5小节已分析过,这里省略。
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, Object source) {DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);if (beanFactory != null) {if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {// 设置dependencyComparator属性beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);}if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {// 设置autowireCandidateResolver属性(设置自动注入候选对象的解析器,用于判断BeanDefinition是否为候选对象)beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());}}Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4);// 注册内部管理的用于处理@Configuration注解的后置处理器的beanif (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);def.setSource(source);// registerPostProcessor: 注册BeanDefinition到注册表中beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));}// 注册内部管理的用于处理@Autowired、@Value、@Inject以及@Lookup注解的后置处理器的beanif (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));}// 注册内部管理的用于处理@Required注解的后置处理器的beanif (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));}// 注册内部管理的用于处理JSR-250注解(例如@Resource, @PostConstruct, @PreDestroy)的后置处理器的bean// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));}// 注册内部管理的用于处理JPA注解的后置处理器的bean// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition();try {def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,AnnotationConfigUtils.class.getClassLoader()));} catch (ClassNotFoundException ex) {throw new IllegalStateException("Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);}def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));}// 注册内部管理的用于处理@EventListener注解的后置处理器的beanif (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));}// 注册内部管理用于生产ApplicationListener对象的EventListenerFactory对象if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));}return beanDefs;
}private static BeanDefinitionHolder registerPostProcessor(BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {// 设置roledefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);// 注册BeanDefinitionregistry.registerBeanDefinition(beanName, definition);// 封装成BeanDefinitionHolder并返回return new BeanDefinitionHolder(definition, beanName);
}
总结:
<context:component-scan> 节点解析过程主要分为以下几步:
1、扫描 base-package 目录,将所有基于@Component注解的bean取出,并加入到注册表中(依然是这三个重要缓存中,beanDefinitionMap、beanDefinitionNames、aliasMap,和前文parseDefaultElement调用链解析默认命名空间一样)
2、将几个常规的bean后置处理器加入到BeanDefinition中,比如@Autowired所基于的AutowiredAnnotationBeanPostProcessor等。
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
相关文章
- 2020年第六届 美亚杯电子取证 团体赛 wp
2020年第六届 美亚杯电子取证 团体赛 wp一、案情简介二、检材三、题目ZelloXenoBob 张伟华Bob的桌上计算机Bob iphoneSamsung S2Bob iMACCole 冯启礼Cole桌上计算机Cole笔记本计算机Cole笔记本的随机存取记忆体Cole的PICole NASCole手机Daniel 罗俊杰Daniel的桌上计算机Daniel的…...
2024/4/20 8:31:39 - 前端入门——HTML和CSS
一、什么是HTML? HTML是用来描述网页的一种语言 HTML指的是超文本语言(Hyper Text Makedown Language); HTML是一种标记语言,而不是一种编程语言; 标记语言是一套标记标签 二、网页的组成部分 HTML&a…...
2024/5/3 2:14:20 - LeetCode 热题 HOT 100 第23天:“字母异位词分组”
继续刷LeetCode 热题 HOT 100 的题目,并且在博客更新我的solutions。在csdn博客中我会尽量用文字解释清楚,相关Java代码大家可以前往我的个人博客jinhuaiyu.com中查看。 题目:字母异位词分组 给你一个字符串数组,请你将 字母异位词…...
2024/4/19 20:11:26 - ES堆栈监测上的Logstash监控之概览,节点,管道简单汇总
最近要做一个类似于Kibana的Logstash监测,在网上找了好久都没有这一块的详细介绍,下面是我自己的一些观点,不对的话请见谅 1、堆栈监测Logstash首页概览 1、已接收事件和已发出事件是各个节点最新数据相加一起的结果 获取集群下的节点DSL语句…...
2024/5/7 11:51:21 - 微信小程序返回箭头返回前增加弹窗
OnLoad里写wx.enableAlertBeforeUnload 官方链接 wx.enableAlertBeforeUnload({message: "退出后编辑过的内容将不保存,确认退出吗?",success: function (res) {console.log("成功:", res);},fail: function (err) {con…...
2024/4/14 3:31:21 - org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘‘
1、遇到的问题 org.springframework.beans.factory.BeanCreationException: Error creating bean with name paymentController: Injection of autowired dependencies failed; 原因:服务单词写错了 2、解决办法 修改正确即可,如下图: 附件…...
2024/4/7 17:26:26 - Opencv 图像处理-Contours函数提取轮廓及感兴趣区域ROI的必用且实用操作技巧-(涵盖Contours的一切使用基础,附代码段)
需求目的:一般都是做项目时使用opencv的findcontours和drawcontours搭配使用抓取图像内感兴趣区域。 1.Contours函数轮廓点大小排序 当使用findcontours后一般返回是contours, hierarchy两个,contours内包含的是根据设定模式所返回的轮廓。 最常使用&…...
2024/4/7 17:26:25 - dialog内部定位
参考:https://www.bilibili.com/video/BV14J4114768?p226...
2024/5/7 11:53:55 - java的三种注释区别
注释 注释的分类 单行注释 符号: // 多行注释 符号 :/* */ 文档注释 符号:/** */ 注释之间的区别 单行注释顾名思义只能注释掉一行代码。多行注释可以注释掉多行代码。多行注释之间不能嵌套使用,但多行注释中可以有单行注释&…...
2024/5/2 23:01:13 - js流程控制
一、顺序流程控制 程序按照代码的先后顺序进行依次执行 二、分支流程控制 再执行代码过程中,根据不同的条件,执行不同的路径代码,从而得到不同的结果; 1.语法结构否则什么也不做 条件成立,执行代码, va…...
2024/5/7 11:44:17 - shell实用脚本-导入环境库变量
以下脚本可以在编译时指定./configure --prefixpwd/dist 中放入一个profile文件,然后使用的时候直接source profile,可以实现当前任意目录执行脚本,环境变量生效 #/bin/bash SHELL_PATH$(readlink -f dirname ${BASH_SOURCE[0]} ) export P…...
2024/4/14 3:31:21 - crmebV4标准版数据字典
数据字典 — Table structure for eb_agreementDROP TABLE IF EXISTS eb_agreement; CREATE TABLE eb_agreement ( id int(11) UNSIGNED NOT NULL AUTO_INCREMENT, type tinyint(1) NOT NULL DEFAULT 0 COMMENT ‘协议类型 1:会员协议’, title varchar(200) CHARAC…...
2024/5/7 11:48:36 - Python调用纷享销客CRM开放平台API
使用Python调用纷享销客CRM开放平台API接口: #!/usr/bin/env python # -*- coding:utf-8 -*- # Author:wangzhifengtongdog.com.cn import requests import jsonclass fxiaoke():#构造方法def __init__(self, AppID,APPSecret,permanentCode,currentOpenUserId):se…...
2024/4/14 3:31:31 - 基于vue2快速上手vue3
创建vue项目 首先现在德vue3已经成为了默认德版本了,现在的创建vue项目的方式有两种: 1:使用脚手架,不走和vue2大致是一样的但是需要注意的是我们vue的版本必须要在4.5以上才能创建vue3项目,现在如果你还再使用vue2过渡…...
2024/4/14 3:31:46 - 闰年判断(蓝桥杯基础练习)
#include<bits/stdc.h> using namespace std; void Is_R(int y){if((y%4000)||(y%40&&y%100!0)){cout<<"yes";}else{cout<<"no";} } int main(){int year;//输入年份cin>>year;Is_R(year);return 0; }...
2024/4/14 16:12:33 - LeetCode 热题 HOT 100 第22天:“字母异位词分组”
继续刷LeetCode 热题 HOT 100 的题目,并且在博客更新我的solutions。在csdn博客中我会尽量用文字解释清楚,相关Java代码大家可以前往我的个人博客jinhuaiyu.com中查看。 题目:字母异位词分组 给你一个字符串数组,请你将 字母异位词…...
2024/5/3 0:32:17 - Java 内存泄漏排查,新技能Get
| 背景 前些日子小组内安排值班,轮流看顾我们的服务,主要做一些报警邮件处理、Bug 排查、运营 issue 处理的事。工作日还好,无论干什么都要上班的,若是轮到周末,那这一天算是毁了。 不知道是公司网络广了就这样还是网…...
2024/4/5 4:04:17 - Map集合的获取功能
import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set;public static void main(String[] args) {//创建Map集合对象Map<Integer, String> map new HashMap<Integer, String>();//添加元素map.put(1, "张三&…...
2024/4/20 10:07:59 - 全球及中国模块化自动化系统行业发展战略与投资规划分析报告2022~2028年
【报告目录】 1 模块化自动化系统市场概述 1.1 产品定义及统计范围 1.2 按照不同产品类型,模块化自动化系统主要可以分为如下几个类别 1.2.1 不同产品类型模块化自动化系统市场规模2019 VS 2022 VS 2028 1.2.2 分布式控制系统 1.2.3 模块包 1.3 从不同应用…...
2024/4/14 3:32:17 - Git进阶
Git进阶 git进阶命令 文章目录Git进阶git branchgit blamegit stashgit cherry-pickgit logfetch、pullmerge、rebasegit diffgit resetgit revert 用法git branch 查看本地有什么分支,当前使用分支前会有一个 * 来标识 git branch * master查看所有,包括隐藏分支…...
2024/5/7 11:47:46
最新文章
- 单位档案寄存该怎么处理才好
处理单位档案寄存的方式可以根据实际情况来确定,以下是一些常见的处理方式: 1. 数字化存档:将单位档案进行数字化处理,通过扫描或拍照将文件转化为电子格式。这样可以方便查找和管理,减少纸质文件的存储量,…...
2024/5/9 1:19:51 - 梯度消失和梯度爆炸的一些处理方法
在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言,在此感激不尽。 权重和梯度的更新公式如下: w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...
2024/5/7 10:36:02 - C# 抽象类、接口
(1)、抽象类和抽象方法的定义和实现:abstract override abstract class Vehicle{ public abstract void Run(); } 继承抽象类并且实现抽象方法 class RaceCar : Vehicle{ public override void Run(){ } } (2)、接口的…...
2024/5/6 11:49:43 - linuxday05
1、makedile原理(增量编译生成代码) # (注释符) 目标------依赖 目标不存在//目标比依赖旧才会执行命令; makefile的实现 1、命名要求(Makefile/makefile) 2、规则的集合 目标文件&#…...
2024/5/7 13:05:29 - 【外汇早评】美通胀数据走低,美元调整
原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...
2024/5/8 6:01:22 - 【原油贵金属周评】原油多头拥挤,价格调整
原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...
2024/5/7 9:45:25 - 【外汇周评】靓丽非农不及疲软通胀影响
原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...
2024/5/4 23:54:56 - 【原油贵金属早评】库存继续增加,油价收跌
原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...
2024/5/7 14:25:14 - 【外汇早评】日本央行会议纪要不改日元强势
原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...
2024/5/4 23:54:56 - 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响
原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...
2024/5/4 23:55:05 - 【外汇早评】美欲与伊朗重谈协议
原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...
2024/5/4 23:54:56 - 【原油贵金属早评】波动率飙升,市场情绪动荡
原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...
2024/5/7 11:36:39 - 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试
原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...
2024/5/4 23:54:56 - 【原油贵金属早评】市场情绪继续恶化,黄金上破
原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...
2024/5/6 1:40:42 - 【外汇早评】美伊僵持,风险情绪继续升温
原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...
2024/5/4 23:54:56 - 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势
原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...
2024/5/8 20:48:49 - 氧生福地 玩美北湖(上)——为时光守候两千年
原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...
2024/5/7 9:26:26 - 氧生福地 玩美北湖(中)——永春梯田里的美与鲜
原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...
2024/5/4 23:54:56 - 氧生福地 玩美北湖(下)——奔跑吧骚年!
原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...
2024/5/8 19:33:07 - 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!
原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...
2024/5/5 8:13:33 - 「发现」铁皮石斛仙草之神奇功效用于医用面膜
原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...
2024/5/8 20:38:49 - 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者
原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...
2024/5/4 23:54:58 - 广州械字号面膜生产厂家OEM/ODM4项须知!
原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...
2024/5/6 21:42:42 - 械字号医用眼膜缓解用眼过度到底有无作用?
原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...
2024/5/4 23:54:56 - 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...
解析如下:1、长按电脑电源键直至关机,然后再按一次电源健重启电脑,按F8健进入安全模式2、安全模式下进入Windows系统桌面后,按住“winR”打开运行窗口,输入“services.msc”打开服务设置3、在服务界面,选中…...
2022/11/19 21:17:18 - 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。
%读入6幅图像(每一幅图像的大小是564*564) f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...
2022/11/19 21:17:16 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...
win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面,在等待界面中我们需要等待操作结束才能关机,虽然这比较麻烦,但是对系统进行配置和升级…...
2022/11/19 21:17:15 - 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...
有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows,请勿关闭计算机”的提示,要过很久才能进入系统,有的用户甚至几个小时也无法进入,下面就教大家这个问题的解决方法。第一种方法:我们首先在左下角的“开始…...
2022/11/19 21:17:14 - win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...
置信有很多用户都跟小编一样遇到过这样的问题,电脑时发现开机屏幕显现“正在配置Windows Update,请勿关机”(如下图所示),而且还需求等大约5分钟才干进入系统。这是怎样回事呢?一切都是正常操作的,为什么开时机呈现“正…...
2022/11/19 21:17:13 - 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...
Win7系统开机启动时总是出现“配置Windows请勿关机”的提示,没过几秒后电脑自动重启,每次开机都这样无法进入系统,此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一:开机按下F8,在出现的Windows高级启动选…...
2022/11/19 21:17:12 - 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...
有不少windows10系统用户反映说碰到这样一个情况,就是电脑提示正在准备windows请勿关闭计算机,碰到这样的问题该怎么解决呢,现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法:1、2、依次…...
2022/11/19 21:17:11 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...
今天和大家分享一下win7系统重装了Win7旗舰版系统后,每次关机的时候桌面上都会显示一个“配置Windows Update的界面,提示请勿关闭计算机”,每次停留好几分钟才能正常关机,导致什么情况引起的呢?出现配置Windows Update…...
2022/11/19 21:17:10 - 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...
只能是等着,别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚,只能是考虑备份数据后重装系统了。解决来方案一:管理员运行cmd:net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...
2022/11/19 21:17:09 - 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?
原标题:电脑提示“配置Windows Update请勿关闭计算机”怎么办?win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢?一般的方…...
2022/11/19 21:17:08 - 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...
关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!关机提示 windows7 正在配…...
2022/11/19 21:17:05 - 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...
钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...
2022/11/19 21:17:05 - 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...
前几天班里有位学生电脑(windows 7系统)出问题了,具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面,长时间没反应,无法进入系统。这个问题原来帮其他同学也解决过,网上搜了不少资料&#x…...
2022/11/19 21:17:04 - 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...
本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法,并在最后教给你1种保护系统安全的好方法,一起来看看!电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中,添加了1个新功能在“磁…...
2022/11/19 21:17:03 - 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...
许多用户在长期不使用电脑的时候,开启电脑发现电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机。。.这要怎么办呢?下面小编就带着大家一起看看吧!如果能够正常进入系统,建议您暂时移…...
2022/11/19 21:17:02 - 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...
配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!配置windows update失败 还原更改 请勿关闭计算机&#x…...
2022/11/19 21:17:01 - 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...
不知道大家有没有遇到过这样的一个问题,就是我们的win7系统在关机的时候,总是喜欢显示“准备配置windows,请勿关机”这样的一个页面,没有什么大碍,但是如果一直等着的话就要两个小时甚至更久都关不了机,非常…...
2022/11/19 21:17:00 - 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...
当电脑出现正在准备配置windows请勿关闭计算机时,一般是您正对windows进行升级,但是这个要是长时间没有反应,我们不能再傻等下去了。可能是电脑出了别的问题了,来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...
2022/11/19 21:16:59 - 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...
我们使用电脑的过程中有时会遇到这种情况,当我们打开电脑之后,发现一直停留在一个界面:“配置Windows Update失败,还原更改请勿关闭计算机”,等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢࿰…...
2022/11/19 21:16:58 - 如何在iPhone上关闭“请勿打扰”
Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...
2022/11/19 21:16:57