網上不少教程默認使用SpringBootApplcation註解,且只用這個便可掃描啓動類包下全部的bean。 而官方默認教程使用的是@EnableAutoConfiguration,這個註解和SpringBootApplication註解的區別是什麼?html
If you don’t want to use @SpringBootApplication, the @EnableAutoConfiguration and @ComponentScan annotations that it imports defines that behaviour so you can also use that instead.java
意思是若是你不想用@SpringBootApplication 可使用@EnableAutoConfiguration and @ComponentScan 註解來代替它git
不少開發者喜歡它們的app能auto-configuration, component scan 和在啓動類中作額外配置。那麼@SpringBootApplication能夠知足你的這三項要求redis
從流程圖可知,在refreshContext時,會調用上層的refresh方法觸發invokeBeanFactoryPostProcessors,在到後邊會觸發到的ComponentScanAnnotationParser類中spring
在此方法中,咱們能夠斷定出咱們的basePackages目錄以方便咱們掃描。app
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) { 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)); } scanner.setResourcePattern(componentScan.getString("resourcePattern")); 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); } } 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); } for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) { basePackages.add(ClassUtils.getPackageName(clazz)); } // 默認使用@componentScan 不寫basePackages則使用此處作爲基礎包路徑 if (basePackages.isEmpty()) { // declaringClass=com.ricky.SpringBootApplication05 // 即 com.ricky 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)); }
會掃描咱們啓動類packageSearchPath所在目錄下的全部class,最終裝載到Set candidates對象中。ide
private Set<BeanDefinition> scanCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<>(); try { String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); boolean traceEnabled = logger.isTraceEnabled(); boolean debugEnabled = logger.isDebugEnabled(); for (Resource resource : resources) { if (traceEnabled) { logger.trace("Scanning " + resource); } if (resource.isReadable()) { try { MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); if (isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setResource(resource); sbd.setSource(resource); if (isCandidateComponent(sbd)) { if (debugEnabled) { logger.debug("Identified candidate component class: " + resource); } candidates.add(sbd); } else { if (debugEnabled) { logger.debug("Ignored because not a concrete top-level class: " + resource); } } } else { if (traceEnabled) { logger.trace("Ignored because not matching any filter: " + resource); } } } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to read candidate component class: " + resource, ex); } } else { if (traceEnabled) { logger.trace("Ignored because not readable: " + resource); } } } } catch (IOException ex) { throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex); } return candidates; }
項目目錄結構spring-boot
掃描到的目錄this
掃描到類spa
源碼能夠解釋 爲何官方說通常可使用@EnableAutoConfiguration和 @ComponentScan 進行替代。
@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 { @AliasFor(annotation = EnableAutoConfiguration.class) Class<?>[] exclude() default {}; @AliasFor(annotation = EnableAutoConfiguration.class) String[] excludeName() default {}; @AliasFor(annotation = ComponentScan.class, attribute = "basePackages") String[] scanBasePackages() default {}; @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses") Class<?>[] scanBasePackageClasses() default {}; }
只支持org.springframework.boot.autoconfigure包下的
@SpringBootApplication(exclude = HibernateJpaAutoConfiguration.class)
只支持org.springframework.boot.autoconfigure包下的
@SpringBootApplication(excludeName = "org.springframework.boot.autoconfigure.orm.jpaHibernateJpaAutoConfiguration.class")
@SpringBootApplication(scanBasePackages = {"com.ricky","com.ricky02"})
能夠自定義
@Configuration @ComponentScan public class Ricky03Config { }
@SpringBootApplication(scanBasePackages = {"com.ricky","com.ricky02"},scanBasePackageClasses = Ricky03Config.class)
和實現上述功能同樣的方式,須要EnableAutoConfiguration、ComponentScan或者配合Import
@Configuration @ComponentScan public class Ricky04Config { }
@EnableAutoConfiguration @ComponentScan(basePackages = {"com.ricky","com.ricky02","com.ricky03"},basePackageClasses = {Ricky04Config.class}) //@Import({Ricky04Config.class}) //ricky04目錄的也掃描進來 public class SpringBootApplication06_3 { /** * 開啓SpringBoot服務 * @param args */ public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(SpringBootApplication06_3.class, args); String[] names = context.getBeanDefinitionNames(); for (String name:names){ System.out.println(name); } context.close(); } }
import通常能夠引入咱們的@configuration相關的類。 咱們能夠利用import和Configuration來實現開啓 動態注入bean對象的場景。
這裏是假定咱們redis實例都是經過公共模塊來調用,相似單獨的SB項目模塊獨立管理。在其餘模塊使用的時候須要引入該模塊。
公共模塊中
/** * 自定義開啓redis * */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(RedisConfig.class) public @interface EnableZDRedis { }
公共模塊中
@Configuration @ConditionalOnClass(Jedis.class) public class RedisConfig { @Bean @ConditionalOnMissingBean public Jedis jedis(){ return new Jedis("127.0.0.1",6379); } }
調用模塊中,開啓咱們定義的註解便可。
@SpringBootApplication @EnableZDRedis public class SpringBootApplication06_2 { @AutoWired private Jedis jedis; }