博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
springboot情操陶冶-@Configuration注解解析
阅读量:4316 次
发布时间:2019-06-06

本文共 31525 字,大约阅读时间需要 105 分钟。

承接前文,本文将在前文的基础上分析下@Configuration注解是如何一步一步被解析的

@Configuration

如果要了解与明白@SpringBootApplication的工作机制,必须了解@Configuration的注解应用。因为前者依赖后者,此处看下源码

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @interface Configuration {    /**     * Explicitly specify the name of the Spring bean definition associated     * with this Configuration class. If left unspecified (the common case),     * a bean name will be automatically generated.     * 

The custom name applies only if the Configuration class is picked up via * component scanning or supplied directly to a {@link AnnotationConfigApplicationContext}. * If the Configuration class is registered as a traditional XML bean definition, * the name/id of the bean element will take precedence. * @return the suggested component name, if any (or empty String otherwise) * @see org.springframework.beans.factory.support.DefaultBeanNameGenerator */ @AliasFor(annotation = Component.class) String value() default "";}

根据上面的代码可知,其是Spring框架中最常见的注解@Component的复用类。下面笔者将通过AnnotatedBeanDefinitionReader类对该注解的解析进行详细的解读

1.AnnotatedBeanDefinitionReader

根据前文的描述,我们知道针对main()方法所在的类进行注解解析的是通过BeanDefinitionLoader来操作的,而具体的解析操作其实是

其内部的属性annotatedReader来实现的。可能有点云里雾里,笔者此处就直接进入至对应类看个究竟

构造函数

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");        Assert.notNull(environment, "Environment must not be null");        this.registry = registry;        // @Conditional注解表达式解析类        this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);        // processor接口集合注册        AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);    }

由上面可知,在构造函数初始化的过程中,顺便还注册了一发BeanDefinitionRegistryPostProcessor集合,这个接口会在ApplicationContext#refresh()操作中会被统一调用。

AnnotationConfigUtils#registerAnnotationConfigProcessors()

注解配置类的注册,具体配置了哪些processor,笔者此处粗看下

public static Set
registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) { .... .... Set
beanDefs = new LinkedHashSet<>(4); // @Configuration注解解析处理类 if (!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)); } // @Autowired注解解析处理类 if (!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注解解析处理类 if (!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)); } // @WebServiceDef/@EJB/@Resource/@PostConstruct/@PreDestroy注解解析 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注解解析 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)); } .... .... return beanDefs; }

笔者此处只关注针对@Configuration注解spring是如何解析的,本文就重点分析ConfigurationClassPostProcessor处理类

2.ConfigurationClassPostProcessor

我们直接去查看其复写的postProcessBeanFactory()方法,里面出现了关键的处理方法

@Override    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {        int factoryId = System.identityHashCode(beanFactory);        if (this.factoriesPostProcessed.contains(factoryId)) {            throw new IllegalStateException(                    "postProcessBeanFactory already called on this post-processor against " + beanFactory);        }        this.factoriesPostProcessed.add(factoryId);        if (!this.registriesPostProcessed.contains(factoryId)) {            // 关键方法            processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);        }                // 对bean工厂的含Configuration注解实例进行CGLIB代理        enhanceConfigurationClasses(beanFactory);        // 对类型为ImportAware的bean进行额外处理        beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));    }

笔者此处只追踪processConfigBeanDefinitions()方法,看下对@Configuration注解是如何处理的

ConfigurationClassPostProcessor#processConfigBeanDefinitions()

源码有点长,部分省略

/**     * Build and validate a configuration model based on the registry of     * {@link Configuration} classes.     */    public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {        List
configCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames(); // 遍历注册在bean工厂上的所有bean,筛选出未加载过的@Configuration类 for (String beanName : candidateNames) { // 避免重复加载 BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) || ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) { if (logger.isDebugEnabled()) { logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); } } // 以当前类循环遍历注解类以查找有无@Configuration注解 else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } } // 无@Configuration注解bean则直接返回 if (configCandidates.isEmpty()) { return; } .... .... // 解析@Configuration ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); Set
candidates = new LinkedHashSet<>(configCandidates); Set
alreadyParsed = new HashSet<>(configCandidates.size()); do { parser.parse(candidates); parser.validate(); ... candidates.clear(); // 检查是否含有新的bean没有被解析 if (registry.getBeanDefinitionCount() > candidateNames.length) { ... } } while (!candidates.isEmpty()); ... }

@Configuration注解的搜寻策略是根据当前类如果不存在此注解则会根据当前类的注解的注解再搜索,依次类推

由上可知具体的解析类为ConfigurationClassParser,我们关注下它的公有方法parse()

3.ConfigurationClassParser#parse()

源码如下

public void parse(Set
configCandidates) { this.deferredImportSelectors = new LinkedList<>(); // 对不同类型的bean调用不同的parse负载方法 for (BeanDefinitionHolder holder : configCandidates) { BeanDefinition bd = holder.getBeanDefinition(); try { if (bd instanceof AnnotatedBeanDefinition) { parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); } else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) { parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName()); } else { parse(bd.getBeanClassName(), holder.getBeanName()); } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex); } } // 针对DeferredImportSelector延迟选择类作下处理 processDeferredImportSelectors(); }

我们继续对上述的代码作下分析

4.ConfigurationClassParser#doProcessConfigurationClass()

首先分析下parse()方法,内部均会调用如下的代码

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)            throws IOException {        // 优先遍历其内部类,找寻其被@Bean和@Configuration等下述注解修饰的内部类,对内部类进行注入工厂        processMemberClasses(configClass, sourceClass);        // Process any @PropertySource annotations        for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(                sourceClass.getMetadata(), PropertySources.class,                org.springframework.context.annotation.PropertySource.class)) {            if (this.environment instanceof ConfigurableEnvironment) {                processPropertySource(propertySource);            }            else {                logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +                        "]. Reason: Environment must implement ConfigurableEnvironment");            }        }        // Process any @ComponentScan annotations        Set
componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // The config class is annotated with @ComponentScan -> perform the scan immediately Set
scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if needed for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } } // Process any @Import annotations processImports(configClass, sourceClass, getImports(sourceClass), true); // Process any @ImportResource annotations AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); if (importResource != null) { String[] resources = importResource.getStringArray("locations"); Class
readerClass = importResource.getClass("reader"); for (String resource : resources) { String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } } // Process individual @Bean methods Set
beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } // Process default methods on interfaces processInterfaces(configClass, sourceClass); // Process superclass, if any if (sourceClass.getMetadata().hasSuperClass()) { String superclass = sourceClass.getMetadata().getSuperClassName(); if (superclass != null && !superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) { this.knownSuperclasses.put(superclass, configClass); // Superclass found, return its annotation metadata and recurse return sourceClass.getSuperClass(); } } // No superclass -> processing is complete return null; }

上述代码阐述了对@PropertySource@ComponentScan@Import@ImportResource@Bean注解的处理,并且会递归遍历父类来进行相同的解析。

笔者下面便针对上述的五个注解分别作下简单的分析

@PropertySource注解

分析前我们先看下@PropertySource注解的代码

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Repeatable(PropertySources.class)public @interface PropertySource {    /**     * Indicate the name of this property source. If omitted, a name will     * be generated based on the description of the underlying resource.     * @see org.springframework.core.env.PropertySource#getName()     * @see org.springframework.core.io.Resource#getDescription()     */    String name() default "";    /**     * Indicate the resource location(s) of the properties file to be loaded.     * 

Both traditional and XML-based properties file formats are supported * — for example, {@code "classpath:/com/myco/app.properties"} * or {@code "file:/path/to/file.xml"}. *

Resource location wildcards (e.g. **/*.properties) are not permitted; * each location must evaluate to exactly one {@code .properties} resource. *

${...} placeholders will be resolved against any/all property sources already * registered with the {@code Environment}. See {@linkplain PropertySource above} * for examples. *

Each location will be added to the enclosing {@code Environment} as its own * property source, and in the order declared. */ String[] value(); /** * Indicate if failure to find the a {@link #value() property resource} should be * ignored. *

{@code true} is appropriate if the properties file is completely optional. * Default is {@code false}. * @since 4.0 */ boolean ignoreResourceNotFound() default false; /** * A specific character encoding for the given resources, e.g. "UTF-8". * @since 4.3 */ String encoding() default ""; /** * Specify a custom {@link PropertySourceFactory}, if any. *

By default, a default factory for standard resource files will be used. * @since 4.3 * @see org.springframework.core.io.support.DefaultPropertySourceFactory * @see org.springframework.core.io.support.ResourcePropertySource */ Class

factory() default PropertySourceFactory.class;}


然后我们再看下处理方法ConfigurationClassParser#processPropertySource()

private void processPropertySource(AnnotationAttributes propertySource) throws IOException {        String name = propertySource.getString("name");        if (!StringUtils.hasLength(name)) {            name = null;        }        String encoding = propertySource.getString("encoding");        if (!StringUtils.hasLength(encoding)) {            encoding = null;        }        String[] locations = propertySource.getStringArray("value");        Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");        boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");        Class
factoryClass = propertySource.getClass("factory"); PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ? DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass)); for (String location : locations) { try { // if resource has ${},use environment to resolve it String resolvedLocation = this.environment.resolveRequiredPlaceholders(location); // via DefaultResourceLoader to resolve file grammer or classpath grammer Resource resource = this.resourceLoader.getResource(resolvedLocation); // store into environment addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding))); } catch (IllegalArgumentException | FileNotFoundException | UnknownHostException ex) { // Placeholders not resolvable or resource not found when trying to open it if (ignoreResourceNotFound) { if (logger.isInfoEnabled()) { logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage()); } } else { throw ex; } } } }

针对上述代码作下小结

  1. 属性value,支持"classpath:"和"file://"方式加载,可指定多个资源路径,以,分隔。并且支持properties/xml后缀

  2. 属性ignoreResourceNotFound,是否忽略找不到的资源,默认为false,即会对找不到的资源抛出异常

  3. 支持在文件路径添加${}表达式,其会被Enviroment环境的对应属性所解析

  4. @PropertySource主要用于加载外部文件资源,最终加载的资源会被保存至Enviroment环境的Map集合中,可通过Environment#getProperty()方法获取

@ComponentScan

@Component注解扫描类,先看下注解的本身源码

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Repeatable(ComponentScans.class)public @interface ComponentScan {    /**     * Alias for {@link #basePackages}.     * 

Allows for more concise annotation declarations if no other attributes * are needed — for example, {@code @ComponentScan("org.my.pkg")} * instead of {@code @ComponentScan(basePackages = "org.my.pkg")}. */ @AliasFor("basePackages") String[] value() default {}; @AliasFor("value") String[] basePackages() default {}; Class

[] basePackageClasses() default {}; Class
nameGenerator() default BeanNameGenerator.class; Class
scopeResolver() default AnnotationScopeMetadataResolver.class; ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT; /** * Controls the class files eligible for component detection. *

Consider use of {@link #includeFilters} and {@link #excludeFilters} * for a more flexible approach. */ String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN; /** * Indicates whether automatic detection of classes annotated with {@code @Component} * {@code @Repository}, {@code @Service}, or {@code @Controller} should be enabled. */ boolean useDefaultFilters() default true; Filter[] includeFilters() default {}; /** * Specifies which types are not eligible for component scanning. * @see #resourcePattern */ Filter[] excludeFilters() default {}; /** * Specify whether scanned beans should be registered for lazy initialization. *

Default is {@code false}; switch this to {@code true} when desired. * @since 4.1 */ boolean lazyInit() default false; /** * Declares the type filter to be used as an {@linkplain ComponentScan#includeFilters * include filter} or {@linkplain ComponentScan#excludeFilters exclude filter}. */ @Retention(RetentionPolicy.RUNTIME) @Target({}) @interface Filter { /** * The type of filter to use. *

Default is {@link FilterType#ANNOTATION}. * @see #classes * @see #pattern */ FilterType type() default FilterType.ANNOTATION; /** * Alias for {@link #classes}. * @see #classes */ @AliasFor("classes") Class

[] value() default {}; @AliasFor("value") Class
[] classes() default {}; String[] pattern() default {}; }}

此处注解的配置就和spring的<context:component-scan>配置一样,本文就不延伸了


再看下对应的解析类ComponentScanAnnotationParser

public Set
parse(AnnotationAttributes componentScan, final String declaringClass) { // if useDefaultFilters true,@Repository/@Service/@Controller will be also considered to resolve ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader); Class
generatorClass = componentScan.getClass("nameGenerator"); boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass); scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator : BeanUtils.instantiateClass(generatorClass)); ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy"); if (scopedProxyMode != ScopedProxyMode.DEFAULT) { scanner.setScopedProxyMode(scopedProxyMode); } else { Class
resolverClass = componentScan.getClass("scopeResolver"); scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass)); } // the resourcePattern of files.default to **/**.class scanner.setResourcePattern(componentScan.getString("resourcePattern")); // includeFilters and excludeFilters configuration for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) { for (TypeFilter typeFilter : typeFiltersFor(filter)) { scanner.addIncludeFilter(typeFilter); } } for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) { for (TypeFilter typeFilter : typeFiltersFor(filter)) { scanner.addExcludeFilter(typeFilter); } } // if lazyInit is true,the beanDefination will be loading when first to use boolean lazyInit = componentScan.getBoolean("lazyInit"); if (lazyInit) { scanner.getBeanDefinitionDefaults().setLazyInit(true); } Set
basePackages = new LinkedHashSet<>(); String[] basePackagesArray = componentScan.getStringArray("basePackages"); for (String pkg : basePackagesArray) { String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg), ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); Collections.addAll(basePackages, tokenized); } // load concret class's packageName for (Class
clazz : componentScan.getClassArray("basePackageClasses")) { basePackages.add(ClassUtils.getPackageName(clazz)); } // be aware of this. when basePackages and basePackageClasses property are not configurated,it will load current annotated class's packageName if (basePackages.isEmpty()) { basePackages.add(ClassUtils.getPackageName(declaringClass)); } scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) { @Override protected boolean matchClassName(String className) { return declaringClass.equals(className); } }); return scanner.doScan(StringUtils.toStringArray(basePackages)); }

具体的解析过程笔者就不展开了,对上述的代码稍微作下小结

  1. 属性useDefaultFilters,默认为true。表明支持也对@Repository/@Service/@Controller注解进行扫描

  2. 属性resourcePattern,默认为**/**.class。代表扫描何种模式的文件

  3. 属性basePackagesbasePackageClasses,代表扫描的包名列表。如果都没有指定的话,其会以@ComponentScan注解的当前类所在的包名作为扫描路径

  4. @ComponentScan注解类默认是不扫描所注解的当前类的

@Import

此注解用于引入相应的类来进行多元的解析,扩展性比较好。我们可以看下其源码

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Import {    /**     * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}     * or regular component classes to import.     */    Class
[] value();}

再看下具体的解析方法processImports()

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,            Collection
importCandidates, boolean checkForCircularImports) { // if current sourceClass has no @Import annotation.return directly if (importCandidates.isEmpty()) { return; } if (checkForCircularImports && isChainedImportOnStack(configClass)) { this.problemReporter.error(new CircularImportProblem(configClass, this.importStack)); } else { this.importStack.push(configClass); try { // recursively for (SourceClass candidate : importCandidates) { // 1. aim to process ImportSelector interface if (candidate.isAssignable(ImportSelector.class)) { Class
candidateClass = candidate.loadClass(); ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); // configurate common properties ParserStrategyUtils.invokeAwareMethods( selector, this.environment, this.resourceLoader, this.registry); // store DeferredImportSelector interface if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) { this.deferredImportSelectors.add( new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector)); } else { // recursively process @Import based on importClassNames String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection
importSourceClasses = asSourceClasses(importClassNames); processImports(configClass, currentSourceClass, importSourceClasses, false); } } // 2. aim to process ImportBeanDefinitionRegistrar interface else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { Class
candidateClass = candidate.loadClass(); ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class); ParserStrategyUtils.invokeAwareMethods( registrar, this.environment, this.resourceLoader, this.registry); configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); } else { // 3. aim to process it as an @Configuration class this.importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); processConfigurationClass(candidate.asConfigClass(configClass)); } } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", ex); } finally { this.importStack.pop(); } } }

针对上述的代码作下小结

  1. @Import注解中的属性value,支持的类型有ImportSelector.classImportBeanDefinitionRegistrar.class接口和普通的@Configuration注解类

  2. ImportSelector.class接口的selectImports()方法用于引入更多的筛选类以满足不同注解的解析。这利于扩展,用户可自定义去实现

  3. ImportBeanDefinitionRegistrar接口的registerBeanDefinitions()方法主要用于注册特定的beanDefinition。这也利于扩展,用户可自定义去实现

  4. @Import注解对于带有@Configuration的普通类则会再次执行本文开头的方法递归解析

  5. @Import注解的最终目的也是为了将特定的bean注册至spring上下文的bean工厂中

@ImportResource

根据注释描述,其类似于spring中的<import/>标签,用于引入外部的<beans>配置。具体的笔者就不阐述了,有兴趣的读者可自行分析。

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documentedpublic @interface ImportResource {    @AliasFor("locations")    String[] value() default {};    /**     * Resource locations from which to import.     * 

Supports resource-loading prefixes such as {@code classpath:}, * {@code file:}, etc. *

Consult the Javadoc for {@link #reader} for details on how resources * will be processed. * @since 4.2 * @see #value * @see #reader */ @AliasFor("value") String[] locations() default {}; /** * {@link BeanDefinitionReader} implementation to use when processing * resources specified via the {@link #value} attribute. *

By default, the reader will be adapted to the resource path specified: * {@code ".groovy"} files will be processed with a * {@link org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader GroovyBeanDefinitionReader}; * whereas, all other resources will be processed with an * {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader XmlBeanDefinitionReader}. * @see #value */ Class

reader() default BeanDefinitionReader.class;}

@Bean

此注解主要用于对类的方法上,用于将指定的方法返回的值包装成BeanDefinition,并注入至spring中的bean工厂中。具体笔者也不分析了,有兴趣的读者可自行分析

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Bean {    /**     * Alias for {@link #name}.     * 

Intended to be used when no other attributes are needed, for example: * {@code @Bean("customBeanName")}. * @since 4.3.3 * @see #name */ @AliasFor("name") String[] value() default {}; @AliasFor("value") String[] name() default {}; Autowire autowire() default Autowire.NO; /** * The optional name of a method to call on the bean instance during initialization. * Not commonly used, given that the method may be called programmatically directly * within the body of a Bean-annotated method. *

The default value is {@code ""}, indicating no init method to be called. * @see org.springframework.beans.factory.InitializingBean * @see org.springframework.context.ConfigurableApplicationContext#refresh() */ String initMethod() default ""; String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;}

小结

根据上述的代码描述我们基本了解@Configuration注解是如何被解析的,读者只需要依次阅读下来基本就明白了。大致逻辑如下

  1. 获取bean工厂里的所有beanDefinition,对含有@Configuration注解进行筛选得到ConfigurationCandidate集合

  2. 遍历ConfigurationCandidate集合,对@Configuration注解进行解析,其中包括@Bean@Import@PropertySource@ImportResource@ComponentScan注解的解析。其中bean注册顺序为 内部类>@ComponentScan>@Import导入的类>@Bean>@ImportResource>ImportBeanDefinitionRegistrar

  3. 对Import导入的类型为DeferredImportSelector进行解析,其中涉及@AutoConfigureBefore的注解排序解析。一般是用于springboot的自带配置类解析,其的bean解析顺序是最靠后

  4. 通过ConfigurationClassBeanDefinitionReader类对上述@Import导入的类、@Bean注解的类方法、@ImportResource注解的类均注册至bean工厂中

  5. 对于@Import导入的非ImportBeanDefinitionRegistrar/ImportSelector实现类,即使其上面没有@Configuration注解其也会被注入至bean工厂;有则可能会被重载

基于上述的解读,那么@SpringBootApplication注解的解读就迫在眉睫了,里面肯定蕴含了一些玄机等待我们去发现。

转载于:https://www.cnblogs.com/question-sky/p/9406819.html

你可能感兴趣的文章
java二维数组矩阵_获取从二维数组矩阵的行和列在Java中
查看>>
scala mysql连接池_Scala 操作Redis使用连接池工具类RedisUtil
查看>>
css背景图片高斯模糊_CSS3 filter(滤镜) 制作图片高斯模糊无需JS
查看>>
matlab综合实验题库,数学实验matlab题库答案
查看>>
oracle wri$_adv_objects突增,SYSTEM Tablespace — oracle-tech
查看>>
python抓取oracle数据,python爬虫,抓取oracle-base上的一些常用脚本
查看>>
oracle分页用子查询,[亲测]Oracle查询--子查询,分页查询(二)
查看>>
oracle动态语句怎么传参数值,DATAX动态参数数据传递
查看>>
php怎么设置文本区域,PHP txt下载不写文本区域内容
查看>>
linux各个目录名称,描述Linux发行版的系统目录名称命名规则以及用途
查看>>
linux 脚本里切换用户密码,shell,切换用户,执行指定,脚本
查看>>
linux配置无密码登陆,Linux下配置ssh无密码登录
查看>>
linux视频对讲qt方案,QT下视频通话的实现
查看>>
顺序串c语言,数据结构c语言实现定长顺序串
查看>>
linux 安装qq2014,Ubuntu14.04安装wine QQ
查看>>
android定位坑简书,Android 定位遇到的异常
查看>>
android udp开发,Android UDP
查看>>
html如何实现3d动画,[HTML5] HTML5 3D爱心动画及其制作过程
查看>>
html让控件垂直,css中实现元素垂直且水平居中的六种方法
查看>>
剔除所有html标签保留p标签,iOS-去掉字符串中的html标签的几种方法
查看>>