springboot之@SpringBootApplication注解淺析


  首先是springboot應用程序的入口類代碼  

@SpringBootApplication
public class HelloApplication {

    public static void main(String[] args) {
        SpringApplication.run(HelloApplication.class,args);
    }
}

  @SpringBootApplication注解的源碼

  

/** *表明當前類是一個springboot項目 */
//標准該注解使用的目標,類,接口,抽象類等
@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 @interface SpringBootApplication {
    ......
}

  可以看出該注解是一個組合注解,主要由@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan三大注解組成,下面我們來一一分析這三大注解。

  @SpringBootConfiguration

/**
* 表示當前類為一個SpringBoot應用程序
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

	@AliasFor(annotation = Configuration.class)
	boolean proxyBeanMethods() default true;

}

  由上可以看出@SpringBootConfiguration注解作用是標注當前類是一個SpringBoot配置,所以在SpringBoot項目的main方法類中可以通過@Bean的方式注入bean。

   @EnableAutoConfiguration  

/**
 * 啟動Spring Application Context的自動配置,通過引入的jar(以啟動器的方式),猜測需要自動配置
 * 的bean
 * 使用@SpringBootApplication注解時,將自動開啟上下文的配置
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	/**
	 * Exclude specific auto-configuration classes such that they will never be applied. 排除特定的自動配置類,使其永遠不會應用
	 * @return the classes to exclude
	 */
	Class<?>[] exclude() default {};

	/**
	 * Exclude specific auto-configuration class names such that they will never be  排除特定的自動配置類名,使其永遠不會應用
	 * applied.
	 * @return the class names to exclude
	 * @since 1.3.0
	 */
	String[] excludeName() default {};

}

  該注解類似於Spring中其它的常見注解一樣,字面意義為開啟自動配置,但是該注解仍是一個組合注解,組成的兩大注解一個是@AutoConfigurationPackage,@Import

  @AutoConfigurationPackage

/**
* 表示自動配置包
* @Import注解,表示將AutoConfigurationPackages.Registrar導入容器中
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

}

  AutoConfigurationPackages源碼 參看博客(https://blog.csdn.net/andy_zhang2007/article/details/78652907) 

/**
 * Class for storing auto-configuration packages for reference later 
 * (e.g. by JPA entity scanner).
 * 用於存儲自動配置包以供以后參考的抽象類 (例如,通過JPA實體掃描器)
 */
public abstract class AutoConfigurationPackages {
	//slfj日志
	private static final Log logger = LogFactory.getLog(AutoConfigurationPackages.class);

	private static final String BEAN = AutoConfigurationPackages.class.getName();

	/**
	 * Determine if the auto-configuration base packages for the given
     * bean factory are available.
     * 確定給定bean工廠的自動配置基本軟件包是否可用
	 * @param beanFactory the source bean factory
	 * @return true if there are auto-config packages available
	 */
	public static boolean has(BeanFactory beanFactory) {
		return beanFactory.containsBean(BEAN) && !get(beanFactory).isEmpty();
	}

	/**
	 * Return the auto-configuration base packages for the given bean factory.
	 * 返回給定bean工廠的自動配置基本包
	 * @param beanFactory the source bean factory
	 * @return a list of auto-configuration packages
	 * @throws IllegalStateException if auto-configuration is not enabled
	 */
	public static List<String> get(BeanFactory beanFactory) {
		try {
			return beanFactory.getBean(BEAN, BasePackages.class).get();
		}
		catch (NoSuchBeanDefinitionException ex) {
			throw new IllegalStateException("Unable to retrieve @EnableAutoConfiguration base packages");
		}
	}

	/**
	 * Programmatically registers the auto-configuration package names. Subsequent
	 * invocations will add the given package names to those that have already been
	 * registered. You can use this method to manually define the base packages that will
	 * be used for a given {@link BeanDefinitionRegistry}. Generally it's recommended that
	 * you don't call this method directly, but instead rely on the default convention
	 * where the package name is set from your {@code @EnableAutoConfiguration}
	 * configuration class or classes.
	 * @param registry the bean definition registry
	 * @param packageNames the package names to set
	 */
	public static void register(BeanDefinitionRegistry registry, String... packageNames) {
		if (registry.containsBeanDefinition(BEAN)) {
			BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
			ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
			constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
		}
		else {
			GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
			beanDefinition.setBeanClass(BasePackages.class);
			beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
			beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
			registry.registerBeanDefinition(BEAN, beanDefinition);
		}
	}

	private static String[] addBasePackages(ConstructorArgumentValues constructorArguments, String[] packageNames) {
		String[] existing = (String[]) constructorArguments.getIndexedArgumentValue(0, String[].class).getValue();
		Set<String> merged = new LinkedHashSet<>();
		merged.addAll(Arrays.asList(existing));
		merged.addAll(Arrays.asList(packageNames));
		return StringUtils.toStringArray(merged);
	}

	/**
	 * {@link ImportBeanDefinitionRegistrar} to store the base package from 
	 * the importing configuration.
	 * 用於存儲導入配置的基本包
	 */
	static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

		@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
			register(registry, new PackageImport(metadata).getPackageName());
		}

		@Override
		public Set<Object> determineImports(AnnotationMetadata metadata) {
			return Collections.singleton(new PackageImport(metadata));
		}

	}

	/**
	 * Wrapper for a package import.
	 * 包裝導入的包
	 */
	private static final class PackageImport {

		private final String packageName;

		PackageImport(AnnotationMetadata metadata) {
			this.packageName = ClassUtils.getPackageName(metadata.getClassName());
		}

		String getPackageName() {
			return this.packageName;
		}

		@Override
		public boolean equals(Object obj) {
			if (obj == null || getClass() != obj.getClass()) {
				return false;
			}
			return this.packageName.equals(((PackageImport) obj).packageName);
		}

		@Override
		public int hashCode() {
			return this.packageName.hashCode();
		}

		@Override
		public String toString() {
			return "Package Import " + this.packageName;
		}

	}

	/**
	 * Holder for the base package (name may be null to indicate no scanning).
	 */
	static final class BasePackages {

		private final List<String> packages;

		private boolean loggedBasePackageInfo;

		BasePackages(String... names) {
			List<String> packages = new ArrayList<>();
			for (String name : names) {
				if (StringUtils.hasText(name)) {
					packages.add(name);
				}
			}
			this.packages = packages;
		}

		List<String> get() {
			if (!this.loggedBasePackageInfo) {
				if (this.packages.isEmpty()) {
					if (logger.isWarnEnabled()) {
						logger.warn("@EnableAutoConfiguration was declared on a class "
								+ "in the default package. Automatic @Repository and "
								+ "@Entity scanning is not enabled.");
					}
				}
				else {
					if (logger.isDebugEnabled()) {
						String packageNames = StringUtils.collectionToCommaDelimitedString(this.packages);
						logger.debug("@EnableAutoConfiguration was declared on a class in the package '" + packageNames
								+ "'. Automatic @Repository and @Entity scanning is enabled.");
					}
				}
				this.loggedBasePackageInfo = true;
			}
			return this.packages;
		}

	}

}

  @Import(AutoConfigurationImportSelector.class)

    @Import表示導入AutoConfigurationImportSelector類中的元素。

      AutoConfigurationImportSelector表示給容器導入非常多的自動配置類(xxxAutoConfiguration),給容器中導入這個場景需要的所有組件,並配置好這些組件

     @ComponentScan配置掃描包范圍,默認掃描當前類所在的包及其子包。

  總結: @SpringBootApplication是springboot項目的核心注解,該注解是一個組合注解,由@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan三個注解組合而成,@SpringBootConfiguration注解表示當前類為一個SpringBoot配置類,所以可以在當前類中通過@Bean方式向容器中注入組件,@EnableAutoConfiguration注解則是表示開啟自動配置,SpringBoot會根據我們導入的啟動器猜測我們的需要的組件,幫我們自動導入和配置,@ComponentScan表示配置掃描包范圍,默認掃描當前類所在包,以及其子包。

  (由於筆者的水平有限,對SpringBoot的理解不夠深刻,隨着以后的學習,也會不斷的對博客中的內容進行修改,完善。)

 

  

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM