以前调试源码的时候,老是在自认为是关键点的地方打上断点,而后就一步一步的调试到断点处。可是这种方式是错误并且低效的,由于全部的端点只是你想固然的认为它会走的,不少细节若是不注意的话就容易错过,而像Spring的这种超大工程,错过这些细节就可能错过了一个重要的知识点,而这个知识点就是你理解原理的重要一步。今天参考一个教学视频,发现他的调试方式与本身的方法不同,并且人家是高效。他是查看Debugger的调用栈,之前也看到过这玩意,可是不知道这是干吗。经过Debugger调用栈,能快速定位系统跑到你打的断点的地方调用的每个方法。下面与Idea为例。java
后置处理器web
执行到 ConfigurationClassPostProcessor.processConfigBeanDefinitions() 中,须要执行ConfigurationClassParser.parse(class),其中这个class就是你的主类 ,spring
//声明parse对象, ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); //这里获取到的对象就是SpringBoot的启动类 Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size()); do { parser.parse(candidates); parser.validate();
再来看看pase方法干了啥。websocket
public void parse(Set<BeanDefinitionHolder> configCandidates) { this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>(); for (BeanDefinitionHolder holder : configCandidates) { BeanDefinition bd = holder.getBeanDefinition(); try { if (bd instanceof AnnotatedBeanDefinition) { parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); } } } //parse方法 //获取到主启动类的注解@SpringBootApplication protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException { processConfigurationClass(new ConfigurationClass(metadata, beanName)); }
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { //省去 ------ // Recursively process the configuration class and its superclass hierarchy. // sourceClass --> SpringBoot的主启动类 SourceClass sourceClass = asSourceClass(configClass); do { sourceClass = doProcessConfigurationClass(configClass, sourceClass); }
doProcessConfigurationClasssocket
//处理启动类的注解,给他们各自的注解映射处理器 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 //处理ComponentScan注解,将对应的包名下的类加载到Spring容器里面 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 //将对应的包名下的类加载到Spring容器里面 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) { if (ConfigurationClassUtils.checkConfigurationClassCandidate( holder.getBeanDefinition(), this.metadataReaderFactory)) { parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName()); } } } } // Process any @Import annotations //处理@Import,getImports(sourceClass)返回@Import下的类的集合 //核心处理方法,将Import的类注入到容器里面,后期经过发射调用该类的方法 //getImports(sourceClass) //ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); // ParserStrategyUtils.invokeAwareMethods(selector, this.environment, this.resourceLoader, this.registry); processImports(configClass, sourceClass, getImports(sourceClass), true); // Process any @ImportResource annotations if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) { AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); 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.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; }
@Import(EnableAutoConfigurationImportSelector.class)
以**@Import(EnableAutoConfigurationImportSelector.class)**为例,实际在上面吧该有的主键加载到Spring容器后,会调用 ConfigurationClassParser.学习
private void processDeferredImportSelectors() { List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors; this.deferredImportSelectors = null; Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR); //循环获取Selector类 for (DeferredImportSelectorHolder deferredImport : deferredImports) { //返回的是主启动类 ConfigurationClass configClass = deferredImport.getConfigurationClass(); try { String[] imports = //deferredImport.getImportSelector() 获取到AutoConfigurationImportSelector对象 deferredImport.getImportSelector().selectImports(configClass.getMetadata()); //将上一步的获取须要加载的类所有加载到容器中 processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false); }
在 List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors; 这个方法里边,返回的是主启动类全部的加了@Import的方法 。通过调试,他的返回结果是ui
这样就完成了调用 AutoConfigurationImportSelector.selectImports方法. 该方法返回的结果是系统启动须要自动装配的类。结果以下:this
String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
0 = "org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration" 1 = "org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration" 2 = "org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration" 3 = "org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration" 4 = "org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration" 5 = "org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration" 6 = "org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration" 7 = "org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration" 8 = "org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration" 9 = "com.alibaba.boot.dubbo.actuate.autoconfigure.DubboEndpointAutoConfiguration" 10 = "com.alibaba.boot.dubbo.autoconfigure.DubboAutoConfiguration" 11 = "com.alibaba.boot.dubbo.actuate.autoconfigure.DubboHealthIndicatorAutoConfiguration" 12 = "org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration" 13 = "org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration" 14 = "org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration" 15 = "org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration" 16 = "org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration" 17 = "org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration" 18 = "org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration" 19 = "org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration" 20 = "org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration" 21 = "org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration" 22 = "org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration" 23 = "org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration"
最后,经过 processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false); 将须要的类加载到容器里面。lua
这找到了以前一直纠结的问题根源在哪里:原理都是根据反射区获取注解,而后再利用反射获取调用注解上面的属性来完成对象的赋值等操做。可是以前都觉得是显式的调用。xxx.getAnnotation().xxx()。可是一直没到到对应的代码,因此最近一直在纠结这一块的问题,后来在看别人的视屏看到人家的调试方法,瞬时恍然大悟,之前调试都是按照本身的主观意识去猜想程序的下一步会跳到哪一个类里,这样作的方式低效。而经过Debugger的调用栈,可以很清晰的看到程序要执行到断点处,会通过哪些方法,再根据实际状况在这些调用栈上面加上对应的断点来调试,这样不只效率能提升也能提高对源码学习的兴趣。spa