springboot情操陶冶-@Configuration注解解析

当前位置:首页众鑫集团最新线路 >

众鑫集团最新线路

springboot情操陶冶-@Configuration注解解析

时间:2019-12-02本站浏览次数:385

       

承接前文springboot情操陶冶-SpringApplication(二),本文将在前文的基础上分析下@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. * <p>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<BeanDefinitionHolder> registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) { .... .... Set<BeanDefinitionHolder> 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<BeanDefinitionHolder> 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); } } 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<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); do { parser.parse(candidates); parser.validate(); ... candidates.clear(); // 检查是否含有新的bean没有被解析 if (registry.getBeanDefinitionCount() > candidateNames.length) { ... } } while (!candidates.isEmpty()); ... }

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

3.ConfigurationClassParser#parse()

源码如下

public void parse(Set<BeanDefinitionHolder> 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 { // Recursively process any member (nested) classes first 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<AnnotationAttributes> 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<BeanDefinitionHolder> 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<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader"); for (String resource : resources) { String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } } // Process individual @Bean methods Set<MethodMetadata> 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. * <p>Both traditional and XML-based properties file formats are supported * &mdash; for example, {@code "classpath:/com/myco/app.properties"} * or {@code "file:/path/to/file.xml"}. * <p>Resource location wildcards (e.g. *&#42;/*.properties) are not permitted; * each location must evaluate to exactly one {@code .properties} resource. * <p>${...} placeholders will be resolved against any/all property sources already * registered with the {@code Environment}. See {@linkplain PropertySource above} * for examples. * <p>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. * <p>{@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. * <p>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<? extends PropertySourceFactory> 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<? extends PropertySourceFactory> 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; } } } }

针对上述代码作下小结

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

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

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

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

@ComponentScan

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

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Repeatable(ComponentScans.class)public @interface ComponentScan { /** * Alias for {@link #basePackages}. * <p>Allows for more concise annotation declarations if no other attributes * are needed &mdash; 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<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class; Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class; ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT; /** * Controls the class files eligible for component detection. * <p>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. * <p>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. * <p>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<BeanDefinitionHolder> 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<? extends BeanNameGenerator> 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<? extends ScopeMetadataResolver> 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<String> 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)); }

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

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

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

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

    @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<SourceClass> 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<SourceClass> 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(); } } }

针对上述的代码作下小结

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

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

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

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

@ImportResource

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

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documentedpublic @interface ImportResource { @AliasFor("locations") String[] value() default {}; /** * Resource locations from which to import. * <p>Supports resource-loading prefixes such as {@code classpath:}, * {@code file:}, etc. * <p>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. * <p>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<? extends BeanDefinitionReader> reader() default BeanDefinitionReader.class;}

@Bean

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

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Bean { /** * Alias for {@link #name}. * <p>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. * <p>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注解是如何被解析的,读者只需要依次阅读下来基本就明白了。基于上述的解读,那么@SpringBootApplication注解的解读就迫在眉睫了,里面肯定蕴含了一些玄机等待我们去发现。

, 1, 0, 9);




公司地址:河北省石家庄市灵寿县慈峪镇凤凰楼村44号
联系人:郭明 15811176832
周高标 13974836124
电话:18569099908 传真:fl44gb6uk0@sina.com
邮箱:zb8b5@162.com

粤公网安备 44030702001579号

众鑫集团最新网址@