本篇内容介绍了“Java中复合注解@SpringBootApplication怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

对SpringBoot工程的自动配置很感兴趣,于是学习其源码并整理了其中一些内容,如果有错误请大家指正~话不多说,直接上源码;

@SpringBootApplication注解的源码如下:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(excludeFilters={@Filter(type=FilterType.CUSTOM,classes=TypeExcludeFilter.class),@Filter(type=FilterType.CUSTOM,classes=AutoConfigurationExcludeFilter.class)})public@interfaceSpringBootApplication{...}

可以看到这是一个复合注解,一共包括7个不同的注解,下面对这7个不同的注解进行分析。

注解
注解1:@Target({ElementType.TYPE})

用来表示注解作用范围,TYPE表示作用范围为类或接口。

注解2:@Retention(RetentionPolicy.RUNTIME)

注解3:@Documented

表明这个注释是由 javadoc记录的。

注解4:@Inherited

放在注解上,当父类加了@SpringBootApplication注解时,子类也会继承这个注解(对接口的实现类无效)。

注解5:@SpringBootConfiguration

底层仍是@Configuration注解, 源码如下:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Configurationpublic@interfaceSpringBootConfiguration{}注解6:@ComponetScan

@ComponentScan这个注解在Spring中很重要,它对应XML配置中的元素@ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。

可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages。

注解:@EnableAutoConfiguration

个人感觉@EnableAutoConfiguration这个Annotation最为重要它的作用可以概括为:借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器。

其源码如下:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)public@interfaceEnableAutoConfiguration{StringENABLED_OVERRIDE_PROPERTY="spring.boot.enableautoconfiguration";Class<?>[]exclude()default{};String[]excludeName()default{};}

这里需要关注@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)两个注解。

注释:@AutoConfigurationPackage

源码如下:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)public@interfaceEnableAutoConfiguration{StringENABLED_OVERRIDE_PROPERTY="spring.boot.enableautoconfiguration";Class<?>[]exclude()default{};String[]excludeName()default{};}

可以发现这个注解的核心其实也是Import注解,表示对于标注该注解的类的包,应当使用AutoConfigurationPackages注册。接着看Registrar这个类:

staticclassRegistrarimplementsImportBeanDefinitionRegistrar,DeterminableImports{@Override//metadata是我们注解所在的元信息publicvoidregisterBeanDefinitions(AnnotationMetadatametadata,BeanDefinitionRegistryregistry){//将我们注解所在包下所有的组件进行注册register(registry,newPackageImport(metadata).getPackageName());}@OverridepublicSet<Object>determineImports(AnnotationMetadatametadata){returnCollections.singleton(newPackageImport(metadata));}}

这个类中的核心方法是register方法:

privatestaticfinalStringBEAN=AutoConfigurationPackages.class.getName();publicstaticvoidregister(BeanDefinitionRegistryregistry,String...packageNames){if(registry.containsBeanDefinition(BEAN)){BeanDefinitionbeanDefinition=registry.getBeanDefinition(BEAN);ConstructorArgumentValuesconstructorArguments=beanDefinition.getConstructorArgumentValues();constructorArguments.addIndexedArgumentValue(0,addBasePackages(constructorArguments,packageNames));}else{GenericBeanDefinitionbeanDefinition=newGenericBeanDefinition();beanDefinition.setBeanClass(BasePackages.class);beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,packageNames);beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);registry.registerBeanDefinition(BEAN,beanDefinition);}}

register方法的逻辑非常清晰:如果这个bean已经被注册,就获取它的构造函数参数值,并将包名添加进去;否则就创建一个新的bean定义并进行注册。通过@AutoConfigurationPackage这个注解,可以将注解所在包下所有的组件进行注册。

注解:@Import(AutoConfigurationImportSelector.class)

这个注解导入了AutoConfigurationImportSelector这个类这个类的核心方法是selectImports方法,实现ImportSelector接口。方法基于我们在pom.xml文件中配置的jar包和组件进行导入。所以方法返回的是一个Class全路径的String数组,返回的Class会被Spring容器管理。方法源码如下:

@OverridepublicString[]selectImports(AnnotationMetadataannotationMetadata){if(!isEnabled(annotationMetadata)){returnNO_IMPORTS;}AutoConfigurationMetadataautoConfigurationMetadata=AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);AutoConfigurationEntryautoConfigurationEntry=getAutoConfigurationEntry(autoConfigurationMetadata,annotationMetadata);returnStringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}

这个方法的结构也很清晰,首先通过isEnabled方法判断是否需要进行导入,如果需要导入的话,通过loadMetadata方法获取配置信息,并通过getAutoConfigurationEntry进行自动装配。isEnabled方法源码如下:

protectedbooleanisEnabled(AnnotationMetadatametadata){if(getClass()==AutoConfigurationImportSelector.class){returngetEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY,Boolean.class,true);}returntrue;}

这个方法通过EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY这个配置项进行判断是否需要自动配置,默认为true。loadMetadata方法源码如下:

protectedstaticfinalStringPATH="META-INF/"+"spring-autoconfigure-metadata.properties";publicstaticAutoConfigurationMetadataloadMetadata(ClassLoaderclassLoader){returnloadMetadata(classLoader,PATH);}staticAutoConfigurationMetadataloadMetadata(ClassLoaderclassLoader,Stringpath){try{Enumeration<URL>urls=(classLoader!=null)?classLoader.getResources(path):ClassLoader.getSystemResources(path);Propertiesproperties=newProperties();while(urls.hasMoreElements()){properties.putAll(PropertiesLoaderUtils.loadProperties(newUrlResource(urls.nextElement())));}returnloadMetadata(properties);}catch(IOExceptionex){thrownewIllegalArgumentException("Unabletoload@ConditionalOnClasslocation["+path+"]",ex);}}staticAutoConfigurationMetadataloadMetadata(Propertiesproperties){returnnewPropertiesAutoConfigurationMetadata(properties);}

可以看到这个方法会加载META-INF/spring-autoconfigure-metadata.properties下的所有配置信息并包装成AutoConfigurationMetadata对象返回。

注:spring-autoconfigure-metadata.properties文件在spring-boot-autoconfigure-2.1.9.RELEASE.jar/META-INF下。

getAutoConfigurationEntry方法源码如下:

protectedAutoConfigurationEntrygetAutoConfigurationEntry(AutoConfigurationMetadataautoConfigurationMetadata,AnnotationMetadataannotationMetadata){if(!isEnabled(annotationMetadata)){returnEMPTY_ENTRY;}AnnotationAttributesattributes=getAttributes(annotationMetadata);List<String>configurations=getCandidateConfigurations(annotationMetadata,attributes);configurations=removeDuplicates(configurations);Set<String>exclusions=getExclusions(annotationMetadata,attributes);checkExcludedClasses(configurations,exclusions);configurations.removeAll(exclusions);configurations=filter(configurations,autoConfigurationMetadata);fireAutoConfigurationImportEvents(configurations,exclusions);returnnewAutoConfigurationEntry(configurations,exclusions);}

这个方法是AutoConfiguration的主流程方法,可以将这个方法的每一行看做一个步骤,那么处理步骤如下:

1.加载配置了@EnableAutoConfiguration注解的属性值getAttribute方法:

protectedAnnotationAttributesgetAttributes(AnnotationMetadatametadata){Stringname=getAnnotationClass().getName();AnnotationAttributesattributes=AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name,true));Assert.notNull(attributes,()->"Noauto-configurationattributesfound.Is"+metadata.getClassName()+"annotatedwith"+ClassUtils.getShortName(name)+"?");returnattributes;}

2.得到META-INF/spring.factories文件中以@EnableAutoConfiguration完全限定类名做key的value,getCandidateConfigurations方法:

protectedList<String>getCandidateConfigurations(AnnotationMetadatametadata,AnnotationAttributesattributes){List<String>configurations=SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());Assert.notEmpty(configurations,"NoautoconfigurationclassesfoundinMETA-INF/spring.factories.Ifyou"+"areusingacustompackaging,makesurethatfileiscorrect.");returnconfigurations;}protectedClass<?>getSpringFactoriesLoaderFactoryClass(){returnEnableAutoConfiguration.class;}

其中,SpringFactoriesLoader.loadFactoryNames()这个方法的作用是使用给定的类加载器从META-INF/spring.factories加载给定类型的工厂实现的完全限定类名;

3.去重;

4.得到需要排除的类的类名,这些类可以在@EnableAutoConfiguration注解中配置;

5.检查这两个集合;

6.把需要排除的类移除;

7.根据OnBeanCondition、OnClassCondition等条件进行过滤(有兴趣可以深入了解);

8.广播事件,得到AutoConfigurationImportListener所有实现类,然后生成事件进行广播;

9.把需要装配和排除的类完全限定名封装成了AutoConfigurationEntry对象返回。

因此,@EnableAutoConfiguration可以简单总结为:从classpath中搜寻所有的META-INF/spring.factories配置文件,并将其中EnableAutoConfiguration对应的配置项通过反射实例化为对应的标注了@Configuration的IoC容器配置类,并加载到IoC容器。

“Java中复合注解@SpringBootApplication怎么使用”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!