现在网站优化怎么做/华与华营销策划公司
在 spring 中,为简化 bean 的配置,在 spring-context 模块下提供了包的自动扫描功能,将配置的包及其子包下的所有符合条件的类都注册到 BeanFactory 中。下面来看下具体是怎么实现的。
配置
<context:component-scan base-package="com.icheetor.annotation.service"/>
配置很简单,但背后要实现的功能确比较复杂,先来看看 spring 对这段配置的解析。
解析
在 META-INF/spring.handlers 文件中,找到自定义标签 context 对应的 handler 为 ContextNamespaceHandler,查看 ContextNamespaceHandler#init 方法,name 为 component-scan 对应的解析器为 ComponentScanBeanDefinitionParser,进入 ComponentScanBeanDefinitionParser#parse 方法。
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);// 扫描 bean 定义,并注册它们// 1.配置扫描器ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);// 2.执行扫描,得到 BeanDefinition 并注册到 BeanFactorySet<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);// 3.注册注解相关后置处理器registerComponents(parserContext.getReaderContext(), beanDefinitions, element);return null;
}
先是对 basePackage 的解析,此处也支持 "${...}" 占位符,但这种一开始 xml 解析时,由 spring 中的属性解析器 PropertyResolver 可知,所支持的占位字符串很有限。basePackage 也支持多个包的配置,以 "," 或 ";" 分隔即可,将 basePackage 解析为名称为 basePackages 的 String 数组。
此处解析 basePackage 中占位符时采用的策略很宽松,resolvePlaceholders,没有默认值的无法解析占位符将被忽略,并原封不动地传递。
解析完 basePackage,接着进行扫描器的配置,有了路径,还需要工具来执行。
扫描器配置
protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {boolean useDefaultFilters = true;// 使用默认过滤器,默认 trueif (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {useDefaultFilters = Boolean.parseBoolean(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));}// Delegate bean definition registration to scanner class.ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters);scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) {scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE));}try {// 解析 name-generatorparseBeanNameGenerator(element, scanner);}catch (Exception ex) {parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());}try {parseScope(element, scanner);}catch (Exception ex) {parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());}// 解析子标签,是否存在 include-filter 或 exclude-filterparseTypeFilters(element, scanner, parserContext);return scanner;
}protected ClassPathBeanDefinitionScanner createScanner(XmlReaderContext readerContext, boolean useDefaultFilters) {return new ClassPathBeanDefinitionScanner(readerContext.getRegistry(), useDefaultFilters,readerContext.getEnvironment(), readerContext.getResourceLoader());
}
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,Environment environment, @Nullable ResourceLoader resourceLoader) {Assert.notNull(registry, "BeanDefinitionRegistry must not be null");// registry 即 BeanFactorythis.registry = registry;if (useDefaultFilters) {registerDefaultFilters();}// 全局 Environment,创建 ApplicationContext 时创建setEnvironment(environment);// ApplicationContext 继承 ResourceLoader,当采用 ClassPathXmlApplicationContext,此处 resourceLoader 即 ClassPathXmlApplicationContextsetResourceLoader(resourceLoader);
}
// 注册默认过滤器
protected void registerDefaultFilters() {this.includeFilters.add(new AnnotationTypeFilter(Component.class));ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();try {this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));logger.trace("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 {this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");}catch (ClassNotFoundException ex) {// JSR-330 API not available - simply skip.}
}
注册过滤器,默认只对 @Component 注解标识的类进行注册。
设置 ResourceLoader,用于对资源文件进行解析加载。
@Override
public void setResourceLoader(@Nullable ResourceLoader resourceLoader) {this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);// 加载 META-INF/spring.components 下配置的候选的 Component,是一个扩展,默认 nullthis.componentsIndex = CandidateComponentsIndexLoader.loadIndex(this.resourcePatternResolver.getClassLoader());
}
此处resourceLoader 若为 ApplicationContext 子类,则获取的 resourcePatternResolver 就是 resourceLoader。
public CachingMetadataReaderFactory(@Nullable ResourceLoader resourceLoader) {super(resourceLoader);if (resourceLoader instanceof DefaultResourceLoader) {this.metadataReaderCache =((DefaultResourceLoader) resourceLoader).getResourceCache(MetadataReader.class);}else {setCacheLimit(DEFAULT_CACHE_LIMIT);}
}
// CachingMetadataReaderFactory 父类
public SimpleMetadataReaderFactory(@Nullable ResourceLoader resourceLoader) {this.resourceLoader = (resourceLoader != null ? resourceLoader : new DefaultResourceLoader());
}// DefaultResourceLoader
public <T> Map<Resource, T> getResourceCache(Class<T> valueType) {// resourceCaches 为一 ConcurrentHashMap,对 computeIfAbsent 进行了重写,此时返回值为计算出的值return (Map<Resource, T>) this.resourceCaches.computeIfAbsent(valueType, key -> new ConcurrentHashMap<>());
}
类的元数据读取,resourceLoader 若为 AbstractApplicationContext 子类,则就是 DefaultResourceLoader 子类。在 DefaultResourceLoader.resourceCaches 中放入 key 为 MetadataReader.class,value 为一个 ConcurrentHashMap 实例对象。
配置完成,将 ClassPathBeanDefinitionScanner 实例对象返回。
扫描
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {Assert.notEmpty(basePackages, "At least one base package must be specified");Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();// 遍历 basePackagesfor (String basePackage : basePackages) {Set<BeanDefinition> candidates = findCandidateComponents(basePackage);for (BeanDefinition candidate : candidates) {// 解析 @ScopeScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);candidate.setScope(scopeMetadata.getScopeName());String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);// 设置 AbstractBeanDefinition 属性if (candidate instanceof AbstractBeanDefinition) {postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);}// 设置其它注解属性,存在的话,@Lazy/@Primary/@DependsOn/@Role/@Descriptionif (candidate instanceof AnnotatedBeanDefinition) {AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);}// 校验 beanName 是否已经存在if (checkCandidate(beanName, candidate)) {BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);definitionHolder =AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);// 加入结果集beanDefinitions.add(definitionHolder);// 注册 beanDefinitionregisterBeanDefinition(definitionHolder, this.registry);}}}return beanDefinitions;
}
遍历 basePackages,获取 basePackage 下的候选的 BeanDefinition。
public Set<BeanDefinition> findCandidateComponents(String basePackage) {// 默认 nullif (this.componentsIndex != null && indexSupportsIncludeFilters()) {return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);}else {return scanCandidateComponents(basePackage);}
}private Set<BeanDefinition> scanCandidateComponents(String basePackage) {Set<BeanDefinition> candidates = new LinkedHashSet<>();try {// classpath*:com/icheetor/annotation/service/**/*.classString packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +resolveBasePackage(basePackage) + '/' + this.resourcePattern;Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);for (Resource resource : resources) {try {MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);if (isCandidateComponent(metadataReader)) {ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);sbd.setSource(resource);if (isCandidateComponent(sbd)) {...candidates.add(sbd);}else {...}}else {...}}catch (FileNotFoundException ex) {...}catch (Throwable ex) {...}}}catch (IOException ex) {...}return candidates;
}protected String resolveBasePackage(String basePackage) {return ClassUtils.convertClassNameToResourcePath(getEnvironment().resolveRequiredPlaceholders(basePackage));
}
先将配置的 basePackage 包名解析为包搜索路径的指定模式,此处针对 "com.icheetor.annotation.service" 解析出的 packageSearchPath 为:
classpath*:com/icheetor/annotation/service/**/*.class
此时解析 basePackage 中的 "${...}" 采用严格的解析策略,因为后面要直接根据路径搜索,所以此处遇到无法解析的占位符直接抛出异常。
接着利用创建 ClassPathBeanDefinitionScanner 时调用 setResourceLoader 设置的 resourcePatternResolver 获取资源文件。从上面介绍可知,这就是 AbstractApplicationContext 子类。
// AbstractApplicationContext
@Override
public Resource[] getResources(String locationPattern) throws IOException {return this.resourcePatternResolver.getResources(locationPattern);
}
public AbstractApplicationContext() {this.resourcePatternResolver = getResourcePatternResolver();
}
protected ResourcePatternResolver getResourcePatternResolver() {return new PathMatchingResourcePatternResolver(this);
}
转化为利用创建 AbstractApplicationContext 时创建的 PathMatchingResourcePatternResolver 实例对象,获取资源文件,获取的详细过程参考 spring 中的资源文件加载。
此处根据匹配后缀 "**/*.class" 匹配到的都是 class 字节码文件,接着遍历返回的 resources,此处由于是 class 文件,将其封装为 FileSystemResource。
在前面的扫描器配置 setResourceLoader 中,创建了 CachingMetadataReaderFactory 实例对象,赋值给 metadataReaderFactory,所以此时调用 ClassPathScanningCandidateComponentProvider#getMetadataReaderFactory 时返回的就是前面创建的 CachingMetadataReaderFactory 实例对象。接着调用 CachingMetadataReaderFactory#getMetadataReader。
// CachingMetadataReaderFactory
@Override
public MetadataReader getMetadataReader(Resource resource) throws IOException {if (this.metadataReaderCache instanceof ConcurrentMap) {// 先从缓存获取MetadataReader metadataReader = this.metadataReaderCache.get(resource);if (metadataReader == null) {// 不存在,通过父类进行创建metadataReader = super.getMetadataReader(resource);this.metadataReaderCache.put(resource, metadataReader);}return metadataReader;}else if (this.metadataReaderCache != null) {synchronized (this.metadataReaderCache) {MetadataReader metadataReader = this.metadataReaderCache.get(resource);if (metadataReader == null) {metadataReader = super.getMetadataReader(resource);this.metadataReaderCache.put(resource, metadataReader);}return metadataReader;}}else {return super.getMetadataReader(resource);}
}
// SimpleMetadataReaderFactory
@Override
public MetadataReader getMetadataReader(Resource resource) throws IOException {return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader());
}
CachingMetadataReaderFactory 是 SimpleMetadataReaderFactory 子类,相对于父类扩展了缓存 MetadataReader 的功能。
SimpleMetadataReader 的创建可参考 spring 中的字节码文件访问 -- classreading 包。
获取到 metadataReader 实例对象后,调用 ClassPathScanningCandidateComponentProvider#isCandidateComponent,判断是否是合格的候选者。
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {for (TypeFilter tf : this.excludeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {return false;}}for (TypeFilter tf : this.includeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {return isConditionMatch(metadataReader);}}return false;
}
通过配置的 excludeFilters 和 includeFilters 对 metadataReader 进行匹配。匹配代码比较长,核心逻辑就是,利用 SimpleMetadataReader 中的 annotations,即 MergedAnnotationsCollection,遍历其中的 AnnotationTypeMappings 数组,接着遍历 AnnotationTypeMappings 中的 AnnotationTypeMapping 集合,获取其中的 annotationType 与指定注解类型进行匹配。
// MergedAnnotationsCollection
private <A extends Annotation> MergedAnnotation<A> find(Object requiredType,@Nullable Predicate<? super MergedAnnotation<A>> predicate,@Nullable MergedAnnotationSelector<A> selector) {if (selector == null) {// 默认选择器selector = MergedAnnotationSelectors.nearest();}MergedAnnotation<A> result = null;for (int i = 0; i < this.annotations.length; i++) {MergedAnnotation<?> root = this.annotations[i];AnnotationTypeMappings mappings = this.mappings[i];// 遍历 AnnotationTypeMappings 中的 mappingsfor (int mappingIndex = 0; mappingIndex < mappings.size(); mappingIndex++) {AnnotationTypeMapping mapping = mappings.get(mappingIndex);if (!isMappingForType(mapping, requiredType)) {continue;}// 匹配到了MergedAnnotation<A> candidate = (mappingIndex == 0 ? (MergedAnnotation<A>) root :TypeMappedAnnotation.createIfPossible(mapping, root, IntrospectionFailureLogger.INFO));if (candidate != null && (predicate == null || predicate.test(candidate))) {// 判断 distance == 0if (selector.isBestCandidate(candidate)) {return candidate;}// result 赋值result = (result != null ? selector.select(result, candidate) : candidate);}}}return result;
}
private static boolean isMappingForType(AnnotationTypeMapping mapping, @Nullable Object requiredType) {if (requiredType == null) {return true;}Class<? extends Annotation> actualType = mapping.getAnnotationType();return (actualType == requiredType || actualType.getName().equals(requiredType));
}
注解类型符合过滤条件,创建 BeanDefinition。
public ScannedGenericBeanDefinition(MetadataReader metadataReader) {Assert.notNull(metadataReader, "MetadataReader must not be null");this.metadata = metadataReader.getAnnotationMetadata();setBeanClassName(this.metadata.getClassName());setResource(metadataReader.getResource());
}
之后将 ScannedGenericBeanDefinition 实例对象加入 candidates 返回。
接着遍历 candidates。执行 this.scopeMetadataResolver.resolveScopeMetadata(candidate),其目的是查看类上面是否有 @Scope 注解,存在,解析其上的 value 和 proxyMode 属性,之后将封装的 ScopeMetadata 实例对象返回。
ScopeMetadata 中默认 scopeName 为 "singleton"。
接着采用 AnnotationBeanNameGenerator 来生成 beanName。
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {if (definition instanceof AnnotatedBeanDefinition) {// 解析注解 value 属性获取 beanNameString beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);if (StringUtils.hasText(beanName)) {// Explicit bean name found.return beanName;}}// 注解未配置 beanName,创建默认 beanName,类名首字母小写return buildDefaultBeanName(definition, registry);
}
@Nullable
protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {AnnotationMetadata amd = annotatedDef.getMetadata();// 获取注解类型Set<String> types = amd.getAnnotationTypes();String beanName = null;for (String type : types) {// 解析 type 上属性,得到 AnnotationAttributesAnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(amd, type);if (attributes != null) {// 解析 type 上 meta-type // 举例: @Service meta-type @Component @Component 上 meta-type @Indexed,所以返回的 metaTypes 会包含 @Component 和 @IndexedSet<String> metaTypes = this.metaAnnotationTypesCache.computeIfAbsent(type, key -> {Set<String> result = amd.getMetaAnnotationTypes(key);return (result.isEmpty() ? Collections.emptySet() : result);});// metaTypes 是否含有 @Component,attributes 是否含有 value 属性if (isStereotypeWithNameValue(type, metaTypes, attributes)) {Object value = attributes.get("value");if (value instanceof String) {String strVal = (String) value;if (StringUtils.hasLength(strVal)) {if (beanName != null && !strVal.equals(beanName)) {throw ...}beanName = strVal;}}}}}return beanName;
}
注解上存在 value 属性,解析后将其作为 beanName,不存在创建一个默认 beanName,即类名首字母小写。
接着为 beanDefinition 设置相关属性,分别调用 ClassPathBeanDefinitionScanner#postProcessBeanDefinition 和 AnnotationConfigUtils#processCommonDefinitionAnnotations 两个方法。
最后,完成 beanDefintion 注册。也就是说,在扫描的这一步就已经完成了 BeanDefinition 的注册。
注册后置处理器
接着,在 BeanFactory 中注册所有注解相关的后置处理器。
// ComponentScanBeanDefinitionParser
protected void registerComponents(XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) {Object source = readerContext.extractSource(element); // nullCompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);for (BeanDefinitionHolder beanDefHolder : beanDefinitions) {compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder));}// Register annotation config processors, if necessary.boolean annotationConfig = true;if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {annotationConfig = Boolean.parseBoolean(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));}if (annotationConfig) {Set<BeanDefinitionHolder> processorDefinitions =AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);for (BeanDefinitionHolder processorDefinition : processorDefinitions) {compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));}}readerContext.fireComponentRegistered(compositeDef);
}// AnnotationConfigUtils
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) {DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);if (beanFactory != null) {if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {// beanFactory 设置 dependencyComparatorbeanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);}if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {// beanFactory 设置 autowireCandidateResolverbeanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());}}Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);// BeanDefinitionRegistryPostProcessor 实现类// org.springframework.context.annotation.internalConfigurationAnnotationProcessorif (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));}// BeanPostProcessor 实现类,实例化时使用// org.springframework.context.annotation.internalAutowiredAnnotationProcessorif (!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));}// 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));}// 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));}// BeanFactoryPostProcessor 实现类// org.springframework.context.event.internalEventListenerProcessorif (!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));}// EventListenerFactory 接口// org.springframework.context.event.internalEventListenerFactoryif (!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;
}// 注册内部后置处理器对应的 BeanDefinition
private static BeanDefinitionHolder registerPostProcessor(BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);registry.registerBeanDefinition(beanName, definition);return new BeanDefinitionHolder(definition, beanName);
}
只有 beanFactory 中的 dependencyComparator 和 autowireCandidateResolver 属性直接设置的是实例对象,其它内部的后置处理器都是作为 BeanDefinition 先注册在 BeanFactory 中,待 AbstractApplicationContext#refresh 中执行 invokeBeanFactoryPostProcessors 和 registerBeanPostProcessors 时通过 beanFactory.getBean 创建实例对象,完成后置处理器的注册。
此处还有一点要注意,就是 autowireCandidateResolver,看似只创建了 ContextAnnotationAutowireCandidateResolver 一个实例对象,但由于继承关系,实际上也获得了处理处理其它注解的能力。其类结构如下:
这样,就完成了基于注解的包扫描模式下的 BeanDefinition 的注册,也完成了对 context 标签下 component-scan 的解析。
相关文章:

spring 中包自动扫描之 component-scan 解析
在 spring 中,为简化 bean 的配置,在 spring-context 模块下提供了包的自动扫描功能,将配置的包及其子包下的所有符合条件的类都注册到 BeanFactory 中。下面来看下具体是怎么实现的。 配置 <context:component-scan base-package"…...

【C语言】Linux 飞翔的小鸟
【C语言】Linux 飞翔的小鸟 零、环境部署 安装Ncurses库 sudo apt-get install libncurses5-dev壹、编写代码 代码如下: bird.c #include<stdio.h> #include<time.h> #include<stdlib.h> #include<signal.h> #include<curses.h>…...

mcasttest-tool组播检测工具
作者:广大 检测组播 mcasttest-tool是oracle组播检测工具,组播是oracle 11.2.0.2开始的新功能。 1、上传mcasttest工具解压并授权 [rootrac1 soft]# cd /u01/soft/ [rootrac1 soft]# tar -xvf mcasttest.tgz[rootrac1 soft]# chown -R grid:oinstall…...

ncnn 库编译的一些问题,使用交叉编译
一开始的问题是编译完程序,但是部分工具没有编译出来。 主要的问题是: 1. ncnn2in8 程序没有编译出来:主要原因应该是cmakelists.txt文件中对于的模块没打开on,或者这个模块没加进去编译: 添加以下 -DNCNN_BUILD_EXAMPLESON -…...

Python基础教程(一)
1.编程基础 1.1标识符 标识符是变量、函数、模块和其他对象的名称。Python中标识符的命名不是随意的,而是要遵守一定的命名规则,比如说: 1、标识符是由字母 (A~Z 和 a~z) 、下划线和数字组成,但第一个字符不 能是数字。 2、标识符不…...

基于C51和OLED12864实现贪吃蛇小游戏
引言 在微电子技术飞速发展的今天,单片机作为智能控制的核心,广泛应用于各种电子设备中。C51系列单片机以其高效、稳定的特性,成为众多电子爱好者和工程师的首选平台。而OLED显示屏以其轻薄、低功耗、响应速度快等优点,在显示设备…...

JVM性能调优全指南:高流量电商系统的最佳实践
1.G1(Garbage-First) 官网: G1 Garbage Collection G1收集器是Java 7中引入的垃圾收集器,用于替代CMS(Concurrent Mark-Sweep)收集器。它主要针对大内存、多核CPU环境下的应用场景,具有以下特点: 分代收集:G1仍然保留了分代的概念,但新生代和老年代不再是物理隔离的,…...

前端常见场景、JS计算精度丢失问题(Decimal.js 介绍)
目录 一. Decimal.js 介绍 二. 常用方法 1. 创建 Decimal 实例 2.加法 add 或 plus 3.减法 sub 或 minus 4.乘法 times 或 mul 5.除法 div 或 dividedBy 6.取模 7.幂运算 8.平方根 9.保留小数位 toFixed方法(四舍五入) 三.项目应用 前端精度丢失问题通常由以下原因…...

Python写UI自动化--playwright(点击操作)
本篇介绍playwright点击操作,click()方法的常用参数 目录 0. selector (必需) 1. modifiers(可选) 2. position(可选) 3. button(可选) 4. click_count(可选) 5. delay 6. timeout(可选) 7. forceTrue(可选) 8. trialTrue(可选) 9. no_wait_after(可选) …...

[C#面对对象] 之抽象方法 虚方法 接口
1.虚方法 我的理解 "法国的“巴黎公社”,俄国的“十月革命”,都是把主要战略方向首先夺取中心城市 " 设计为 一个父类中的虚方法(virtual),这个虚方法已经有实现了(就是通过暴力革命夺取的方法 最终返回 城市)然而秋收暴动(子类)失败…...

docker 发布geoserver服务添加字体
1. 创建容器时可直接挂载到系统字体库 2. 已发布的容器挂载字体目录 关闭docker服务 : systemctl stop docker.socket 修改config.v2.json :位置在 cd /var/lib/docker/containers/容器id 重新启动docker服务:systemctl start docker...

数据赋能(162)——开发:数据整理——技术方法、主要工具
技术方法 从商业角度来看,从前未知的数据分析模式或趋势的发现为企业提供了非常有价值的洞察力。数据整理技术能够为企业对未来的发展具有一定的预见性。数据整理技术可以分成3类:群集、分类和预测。 群集技术: 这是一种将相似的数据项进行…...

安全服务面试
对安全服务是怎么理解的 安全服务对象是人, 渗透测试对象是网站。(我的理解) 安全概念和资讯 安全工具使用 渗透测试 安全基线检查 应急响应 代码审计 安全边界建设 安全规范 1.拿到一个待检测的站,你觉得应该先做什么&…...

昇思25天学习打卡营第23天|LSTM+CRF序列标注
Mindspore框架CRF条件随机场概率图模型实现文本序列命名实体标注|(一)序列标注与条件随机场的关系 Mindspore框架CRF条件随机场概率图模型实现文本序列命名实体标注|(二)CRF模型构建 Mindspore框架CRF条件随机场概率图模型实现文本…...

抖音直播弹幕数据逆向:websocket和JS注入
🔍 思路与步骤详解 🕵️♂️ 思路介绍 首先,我们通过抓包工具进入的直播间,捕获其网络通信数据,重点关注WebSocket连接。发现直播弹幕数据通过WebSocket传输,这种方式比传统的HTTP更适合实时数据的传输。…...

AIGC diffusers文生图模型optimum量化使用案例
参考: https://github.com/huggingface/blog/blob/main/quanto-diffusers.md 安装 pip install optimum-quanto %pip install optimum使用 from optimum.quanto import freeze, qfloat8, quantize from diffusers import PixArtSigmaPipeline import torchpipeline = PixArt…...

PDF怎么转换成Word?这些工具一键搞定!
在日常生活中,我们经常遇到需要将PDF文件转换成Word文档的情况。PDF怎么转换成Word?一些工具的使用十分重要!下文中就为大家推荐几个亲测好用的PDF转换工具。 一、Foxit PDF转换大师(365客户端) 链接:www…...

【TS】TypeScript函数类型:提升函数的类型安全性和可读性
🌈个人主页: 鑫宝Code 🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础 💫个人格言: "如无必要,勿增实体" 文章目录 TypeScript函数类型:提升函数的类型安全性和可读性1. 引言2. 基本函…...

“八股文”在实际工作中是助力、阻力还是空谈?
前言:在当今快速发展的技术时代,程序员的角色变得日益重要。随着技术的不断进步,招聘流程也在不断演变以适应新的需求。在程序员的招聘过程中,“八股文”作为一种面试现象,已成为不可忽视的一部分。所谓“八股文”&…...

代码随想录算法训练营第22天-leetcode-回溯算法part01:
#回溯算法理论基础 能解决的问题: 组合问题:N个数里面按一定规则找出k个数的集合切割问题:一个字符串按一定规则有几种切割方式子集问题:一个N个数的集合里有多少符合条件的子集排列问题:N个数按一定规则全排列&…...

MySql 触发器、存储器练习
一: 触发器 1、建立两个表:goods(商品表)、orders(订单表) 查看数据库:mysql> show databases; 使用数据库:mysql> use mydb16_trigger; 创建goods表: mysql> create table goods(gid char(8) not null primary key, …...

【Plotly-驯化】一文教您画出Plotly中动态可视化饼图:pie技巧
【Plotly-驯化】一文教您画出Plotly中动态可视化饼图:pie技巧 本次修炼方法请往下查看 🌈 欢迎莅临我的个人主页 👈这里是我工作、学习、实践 IT领域、真诚分享 踩坑集合,智慧小天地! 🎇 免费获取相关内…...

Mirror学习笔记(一) 简介
文章目录 一、常规学习:Mirror核心功能有服务器和主机 二、时间戳批处理时间戳 三、TCP和UDP四、CCU(同时在线人数)五、SyncDirection(同步方向)六、RTT(往返时间)七、Connection Quality(连接质量)八、Lag Compensati…...

终端pip安装包后,Pycharm却导入失败?新手别慌,3招搞定!
很多小伙伴在学习Python的过程中,都会遇到这种情况:明明在终端用pip安装好了需要的包,但在Pycharm中导入时却报错。难道是安装姿势不对? 例如在cmd中已经有了pandas,但是去pycharm中导入pandas显示没有 先别急着怀疑人生,这很可能是因为pip安装包的路径和Pycharm项目使用…...

Redis 与 Scrapy:无缝集成的分布式爬虫技术
1. 分布式爬虫的概念 分布式爬虫系统通过将任务分配给多个爬虫节点,利用集群的计算能力来提高数据抓取的效率。这种方式不仅可以提高爬取速度,还可以在单个节点发生故障时,通过其他节点继续完成任务,从而提高系统的稳定性和可靠性…...

大厂linux面试题攻略四之Linux网络服务(一)
一、Linux网络服务-SSH服务 1.哪些设置能够提升SSH远程管理的安全等级? ssh的登录验证方式 ssh的登录端口和监听设置: 配置文件: /etc/ssh/sshd_config #Port 22 #ssh服务默认监听端口 #ListenAddress 0.0.0.0 #ssh服务…...

【Pulling fs layer】Linux使用docker-compose的时候,一直Pulling fs layer
当Docker在拉取镜像时卡在“pulling fs layer”阶段,可以通过重启Docker服务来解决。 具体步骤如下: 首先,尝试重启Docker服务。可以通过运行以下命令来重启Docker服务: systemctl restart docker 这个命令会重启Docker服务…...

最新保姆级教程使用WildCard开通Claude3升级ChatGPT4.0(2024.8)
如何使用 WildCard 服务注册 Claude3 随着 Claude3 的震撼发布,最强 AI 模型的桂冠已不再由 GPT-4 独揽。Claude3 推出了三个备受瞩目的模型:Claude 3 Haiku、Claude 3 Sonnet 以及 Claude 3 Opus,每个模型都展现了卓越的性能与特色。其中&a…...

layui 乱入前端
功能包含 本实例代码为部分傻瓜框架,插入引用layui。因为样式必须保证跟系统一致,所以大部分功能都是自定义的。代码仅供需要用layui框架,但原项目又不是layui搭建的提供解题思路。代码较为通用 自定义分页功能自定义筛选列功能行内编辑下拉、…...

中国十大顶级哲学家,全球公认的伟大思想家颜廷利:人类为何拥有臀部
人类为何拥有臀部?若众生皆无此部位,又如何能寻得一处真正属于自己的“座位”?在博大精深的中国传统文化中,汉字“座”与“坐”均蕴含“土”字元素。在易经的智慧里,作为五行之一的“土”,象征着人类社会的…...