springboot默認掃描的路徑方式
springboot默認掃描的路徑
一般來說spring boot默認的掃描路徑是啟動類當前的包和子包
@SpringBootApplication @EnableTransactionManagement(proxyTargetClass = true) @MapperScan(basePackages = {"com.frame.springboot.dao", "com.frame.springboot.base"}) public class SpringbootApplication { public static void main(String[] args) { SpringApplication app = new SpringApplication(SpringbootApplication.class); app.addListeners(new MyApplicationStartedEventListener()); app.run(args); } static class MyApplicationStartedEventListener implements ApplicationListener<ApplicationStartedEvent> { private Logger logger = LoggerFactory.getLogger(MyApplicationStartedEventListener.class); @Override public void onApplicationEvent(ApplicationStartedEvent event) { SpringApplication app = event.getSpringApplication(); app.setBannerMode(Banner.Mode.OFF);// 不顯示banner信息 logger.info("==MyApplicationStartedEventListener=="); } } }
例如這個類的包和子類
接下來就從源碼角度來分析這是為什么
首先這個加載過程肯定是從refreshcontext中的操作,因此進入。
public void refresh() throws BeansException, IllegalStateException { synchronized(this.startupShutdownMonitor) { this.prepareRefresh(); ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory(); this.prepareBeanFactory(beanFactory); try { this.postProcessBeanFactory(beanFactory); this.invokeBeanFactoryPostProcessors(beanFactory); this.registerBeanPostProcessors(beanFactory); this.initMessageSource(); this.initApplicationEventMulticaster(); this.onRefresh(); this.registerListeners(); this.finishBeanFactoryInitialization(beanFactory); this.finishRefresh(); } catch (BeansException var9) { if (this.logger.isWarnEnabled()) { this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9); } this.destroyBeans(); this.cancelRefresh(var9); throw var9; } finally { this.resetCommonCaches(); } } }
這個操作實在初始化factorypostprocessors中進行的。
接下來進入方法
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, this.getBeanFactoryPostProcessors()); if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean("loadTimeWeaver")) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } }
在這里很明顯是把任務委托給了PostProcessorRegistrationDelegate。
接下來我們繼續(xù)點擊進入
invokeBeanFactoryPostProcessors的方法很長我只截取我們關心的代碼
public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) { Set<String> processedBeans = new HashSet(); int var9; ArrayList currentRegistryProcessors; String[] postProcessorNames; if (beanFactory instanceof BeanDefinitionRegistry) { BeanDefinitionRegistry registry = (BeanDefinitionRegistry)beanFactory; List<BeanFactoryPostProcessor> regularPostProcessors = new LinkedList(); List<BeanDefinitionRegistryPostProcessor> registryProcessors = new LinkedList(); Iterator var6 = beanFactoryPostProcessors.iterator(); while(var6.hasNext()) { BeanFactoryPostProcessor postProcessor = (BeanFactoryPostProcessor)var6.next(); if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) { BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor)postProcessor; registryProcessor.postProcessBeanDefinitionRegistry(registry); registryProcessors.add(registryProcessor); } else { regularPostProcessors.add(postProcessor); } } }
這里先來簡單的說一下,首先會調(diào)用傳入的beanFactoryPostProcessors的postProcessBeanDefinitionRegistry。
處理一般會調(diào)用
ConfigurationClassPostProcessor類,接下來進去看一下postProcessBeanDefinitionRegistry方法
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { int registryId = System.identityHashCode(registry); if (this.registriesPostProcessed.contains(registryId)) { throw new IllegalStateException("postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } else if (this.factoriesPostProcessed.contains(registryId)) { throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + registry); } else { this.registriesPostProcessed.add(registryId); this.processConfigBeanDefinitions(registry); } }
在這里最重要的還是processConfigBeanDefinitions方法,但是這個方法實在是太長了截取重要的步驟
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { //省略 parser.parse(candidates); }
這個方法主要是用來解析beandefinitions。
進入看一下
public void parse(Set<BeanDefinitionHolder> configCandidates) { this.deferredImportSelectors = new LinkedList(); Iterator var2 = configCandidates.iterator(); while(var2.hasNext()) { BeanDefinitionHolder holder = (BeanDefinitionHolder)var2.next(); BeanDefinition bd = holder.getBeanDefinition(); try { if (bd instanceof AnnotatedBeanDefinition) { this.parse(((AnnotatedBeanDefinition)bd).getMetadata(), holder.getBeanName()); } else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition)bd).hasBeanClass()) { this.parse(((AbstractBeanDefinition)bd).getBeanClass(), holder.getBeanName()); } else { this.parse(bd.getBeanClassName(), holder.getBeanName()); } } catch (BeanDefinitionStoreException var6) { throw var6; } catch (Throwable var7) { throw new BeanDefinitionStoreException("Failed to parse configuration class [" + bd.getBeanClassName() + "]", var7); } } this.processDeferredImportSelectors(); }
這由于我們使用的是注解來進行那么進入第一個parse,主要調(diào)用了processConfigurationClass 的方法
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { if (!this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { ConfigurationClass existingClass = (ConfigurationClass)this.configurationClasses.get(configClass); if (existingClass != null) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } return; } this.configurationClasses.remove(configClass); Iterator it = this.knownSuperclasses.values().iterator(); while(it.hasNext()) { if (configClass.equals(it.next())) { it.remove(); } } } ConfigurationClassParser.SourceClass sourceClass = this.asSourceClass(configClass); do { sourceClass = this.doProcessConfigurationClass(configClass, sourceClass); } while(sourceClass != null); this.configurationClasses.put(configClass, configClass); } }
這個方法獲取config的class和解析,其中主要方法是doProcessConfigurationClass
protected final ConfigurationClassParser.SourceClass doProcessConfigurationClass(ConfigurationClass configClass, ConfigurationClassParser.SourceClass sourceClass) throws IOException { Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { Iterator var13 = componentScans.iterator(); while(var13.hasNext()) { AnnotationAttributes componentScan = (AnnotationAttributes)var13.next(); Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); Iterator var7 = scannedBeanDefinitions.iterator(); while(var7.hasNext()) { BeanDefinitionHolder holder = (BeanDefinitionHolder)var7.next(); if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) { this.parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName()); } } } } }
只截取了其中的重要部分,看到componentScanParser.parse了,那么基本就想到是要去獲取Component組件了,為什么config會和component關系呢,請看一下注解內(nèi)部里面有一個@component。
接下來進去看一下
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) { Set<String> basePackages = new LinkedHashSet(); String[] basePackagesArray = componentScan.getStringArray("basePackages"); String[] var19 = basePackagesArray; int var21 = basePackagesArray.length; int var22; for(var22 = 0; var22 < var21; ++var22) { String pkg = var19[var22]; String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg), ",; \t\n"); basePackages.addAll(Arrays.asList(tokenized)); } Class[] var20 = componentScan.getClassArray("basePackageClasses"); var21 = var20.length; for(var22 = 0; var22 < var21; ++var22) { Class<?> clazz = var20[var22]; basePackages.add(ClassUtils.getPackageName(clazz)); } if (basePackages.isEmpty()) { basePackages.add(ClassUtils.getPackageName(declaringClass)); } }
這個代碼是為了尋找可以掃描的基礎包,但是在創(chuàng)建啟動類的時候我們并沒有設置也就是basePackages.isEmpty()==true。
接下來的操作是添加declaringClass路徑,那么這個類是什么呢。
這個類就是你的啟動類。
所以真正設置掃描路徑的代碼在這。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
多模塊maven的deploy集成gitlab?ci自動發(fā)版配置
這篇文章主要為大家介紹了多模塊maven項目deploy集成gitlab?ci自動發(fā)版的配置流程步驟,有需要的朋友可以借鑒參考下,希望能夠有所幫助2022-02-02微信小程序微信登錄的實現(xiàn)方法詳解(JAVA后臺)
通常我們在登錄微信小程序的時候都是通過授權(quán)登錄,下面這篇文章主要給大家介紹了關于微信小程序微信登錄的實現(xiàn)方法,文中通過實例代碼介紹的介紹的非常詳細,需要的朋友可以參考下2022-07-07Java基礎學習之關鍵字和變量數(shù)據(jù)類型的那些事
變量就是系統(tǒng)為程序分配的一塊內(nèi)存單元,用來存儲各種類型的數(shù)據(jù),下面這篇文章主要給大家介紹了關于Java基礎學習之關鍵字和變量數(shù)據(jù)類型的那些事,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2022-07-07