SpringBoot源码分析

news/2024/9/28 19:26:53

Springboot源码分析

1、SpringApplication初始化

从run()方法进入,可以看到Springboot首先创建了SpringApplication,然后调用SpringApplication的run()方法。

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return (new SpringApplication(primarySources)).run(args);
}

创建SpringApplication,首先要执行它的构造方法。

//SpringApplication.class
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.sources = new LinkedHashSet();this.bannerMode = Mode.CONSOLE;this.logStartupInfo = true;this.addCommandLineProperties = true;this.addConversionService = true;this.headless = true;this.registerShutdownHook = true;this.additionalProfiles = new HashSet();this.isCustomEnvironment = false;this.lazyInitialization = false;this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));//webApplication类型一般是servletthis.webApplicationType = WebApplicationType.deduceFromClasspath();//设置初始化器,加载spring.factories文件中的ApplicationContextInitializerthis.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));//设置监听器,加载spring.factories文件中的ApplicationListenerthis.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));//启动类的类型,我们的是com.example.demo.DemoApplicationthis.mainApplicationClass = this.deduceMainApplicationClass();
}

1.1 & 1.2 设置初始化器和监听器

上面的setInitializers和setListeners去初始化spring.factories文件中对应的类,然后放入ArrayList中。比如这些:

org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
1.2.1 getSpringFactoriesInstances

在getSpringFactoriesInstances()方法中,springboot首先去所有spring.factories找到对应的类,然后通过反射创建类的实例。

//SpringApplication.class
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {return this.getSpringFactoriesInstances(type, new Class[0]);
}private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {ClassLoader classLoader = this.getClassLoader();//获取spring.factories中对应类的类名,以ApplicationListenser为例,springboot会去文件中找到所有ApplicationListener对应的类,放入Set中。Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));//反射创建类的实例对象,放到List中返回List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);AnnotationAwareOrderComparator.sort(instances);return instances;
}private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) {List<T> instances = new ArrayList(names.size());Iterator var7 = names.iterator();while(var7.hasNext()) {String name = (String)var7.next();try {//获取类信息Class<?> instanceClass = ClassUtils.forName(name, classLoader);Assert.isAssignable(type, instanceClass);//获取构造器Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);//创建实例T instance = BeanUtils.instantiateClass(constructor, args);instances.add(instance);} catch (Throwable var12) {throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, var12);}}return instances;
}
1.2.2 loadFactoryNames()

进入SpringFactoriesLoader.loadFactoryNames(type, classLoader)看一下,这里还是以ApplicationListener为例,这时的type是org.springframework.context.ApplicationListener,classLoader是AppClassLoader。

//SpringFactoriesLoader.class
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {String factoryTypeName = factoryType.getName();return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);if (result != null) {return result;} else {try {Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");MultiValueMap<String, String> result = new LinkedMultiValueMap();while(urls.hasMoreElements()) {URL url = (URL)urls.nextElement();UrlResource resource = new UrlResource(url);Properties properties = PropertiesLoaderUtils.loadProperties(resource);Iterator var6 = properties.entrySet().iterator();while(var6.hasNext()) {Map.Entry<?, ?> entry = (Map.Entry)var6.next();String factoryTypeName = ((String)entry.getKey()).trim();String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());int var10 = var9.length;for(int var11 = 0; var11 < var10; ++var11) {String factoryImplementationName = var9[var11];result.add(factoryTypeName, factoryImplementationName.trim());}}}cache.put(classLoader, result);return result;} catch (IOException var13) {throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);}}
}

loadSpringFactories()方法会遍历所有的spring.factories文件,把所有的类的名字保存到map中,然后调用getOrDefault()方法就可以获取到所有的ApplicationListner对应的类名了。

image-20240908150805908

SpringApplication初始化完了,接下来该执行run()方法了

2、SpringApplication执行run()方法

//SpringApplication.class
public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();stopWatch.start();//这里是Spring的上下文ConfigurableApplicationContext context = null;Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();this.configureHeadlessProperty();//获取运行监听器,获取到的是EventPublishingRunListener,这是springboot的启动监听器SpringApplicationRunListeners listeners = this.getRunListeners(args);//启动监听器listeners.starting();Collection exceptionReporters;try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);//2.1 准备环境ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);this.configureIgnoreBeanInfo(environment);//Banner打印类Banner printedBanner = this.printBanner(environment);//2.2 创建应用上下文context = this.createApplicationContext();exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);//2.3 准备上下文this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);//2.4 刷新上下文this.refreshContext(context);//刷新上下文后置处理this.afterRefresh(context, applicationArguments);stopWatch.stop();if (this.logStartupInfo) {(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);}//发布上下文启动完成事件listeners.started(context);this.callRunners(context, applicationArguments);} catch (Throwable var10) {this.handleRunFailure(context, var10, exceptionReporters, listeners);throw new IllegalStateException(var10);}try {listeners.running(context);return context;} catch (Throwable var9) {this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);throw new IllegalStateException(var9);}
}

2.1 prepareEnvironment()准备环境

//SpringApplication.class
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {//根据前面推断的webApplicationType创建对应的环境,servlet对应的是StandardServletEnvironment()ConfigurableEnvironment environment = this.getOrCreateEnvironment();//根据参数配置环境,包括命令行参数,启动类传入的参数this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());ConfigurationPropertySources.attach((Environment)environment);//执行完这个,配置文件中的配置进行就被加载到环境中了listeners.environmentPrepared((ConfigurableEnvironment)environment);//环境和SpringApplication绑定起来this.bindToSpringApplication((ConfigurableEnvironment)environment);if (!this.isCustomEnvironment) {environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());}ConfigurationPropertySources.attach((Environment)environment);return (ConfigurableEnvironment)environment;
}
2.1.1 根据参数配置环境configureEnvironment()
//SpringApplication.class
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {if (this.addConversionService) {ConversionService conversionService = ApplicationConversionService.getSharedInstance();environment.setConversionService((ConfigurableConversionService)conversionService);}//配置命令行中的参数this.configurePropertySources(environment, args);//激活相应的配置文件,例如application.propertires或者application-dev.properties等this.configureProfiles(environment, args);
}

如果我启动的dev环境,执行完上面的代码后环境中是这样的

image-20240908162616219

2.1.2 listeners.environmentPrepared()

执行完这句代码后,配置文件中的信息就被加载到环境中了,如下图。具体怎么做到的还没看懂

image-20240908164518735

2.2 创建应用上下文createApplicationContext()

//SpringApplication.class
protected ConfigurableApplicationContext createApplicationContext() {Class<?> contextClass = this.applicationContextClass;if (contextClass == null) {try {switch (this.webApplicationType) {case SERVLET:contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");break;case REACTIVE:contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");break;default:contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");}} catch (ClassNotFoundException var3) {throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);}}return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}

这个函数也是根据前面的webApplicationType创建应用上下文,servlet对应的是AnnotationConfigServletWebServerApplicationContext,当初始化完这个类时,其实也会创建一个IOC容器beanFactory,如下图所示。这是因为AnnotationConfigServletWebServerApplicationContext会继承GenericApplicationContext,当执行完这个类的构造函数,会创建一个DefaultListableBeanFactory()的beanFactory,这个就是IOC容器。

image-20240908165849323

2.3 准备上下文prepareContext()

//SpringApplication.class
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {//设置上下文环境context.setEnvironment(environment);this.postProcessApplicationContext(context);//初始化所有的ApplicationContextInitializerthis.applyInitializers(context);//发布上下文准备完成事件listeners.contextPrepared(context);if (this.logStartupInfo) {this.logStartupInfo(context.getParent() == null);this.logStartupProfileInfo(context);}//获取IOC容器beanFactoryConfigurableListableBeanFactory beanFactory = context.getBeanFactory();//将springApplicationArguments和springBootBanner注册成单例beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {beanFactory.registerSingleton("springBootBanner", printedBanner);}if (beanFactory instanceof DefaultListableBeanFactory) {((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}if (this.lazyInitialization) {context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());}//2.3.1 获取资源,其中primarySources就是我们定义的启动类DemoApplicationSet<Object> sources = this.getAllSources();Assert.notEmpty(sources, "Sources must not be empty");//2.3.2 加载启动类,注入IOC容器this.load(context, sources.toArray(new Object[0]));//发布完成加载事件listeners.contextLoaded(context);
}
2.3.1 & 2.3.2 获取启动类,注入IOC容器

当执行完getAllSources(),可以获取到启动类DemoApplication

image-20240908173716481

重点看load()方法,进入load()。

//SpringApplication.class
protected void load(ApplicationContext context, Object[] sources) {if (logger.isDebugEnabled()) {logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));}//创建BeanDefinitionLoaderBeanDefinitionLoader loader = this.createBeanDefinitionLoader(this.getBeanDefinitionRegistry(context), sources);if (this.beanNameGenerator != null) {loader.setBeanNameGenerator(this.beanNameGenerator);}if (this.resourceLoader != null) {loader.setResourceLoader(this.resourceLoader);}if (this.environment != null) {loader.setEnvironment(this.environment);}//加载启动类loader.load();
}

其中getBeanDefinitionRegistry(context)方法会将之前的上下文context强转成BeanDefinitionRegistry,他们之间的继承关系很复杂,可以转换成不同的类完成不同的方法。createBeanDefinitionLoader()方法创建了一个BeanDefinitionLoader(),点进去看看。

//BeanDefinitionLoader.class
BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {Assert.notNull(registry, "Registry must not be null");Assert.notEmpty(sources, "Sources must not be empty");this.sources = sources;//注解形式的Bean阅读器this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);//xml形式的Bean阅读器this.xmlReader = new XmlBeanDefinitionReader(registry);if (this.isGroovyPresent()) {this.groovyReader = new GroovyBeanDefinitionReader(registry);}//类路径扫描器this.scanner = new ClassPathBeanDefinitionScanner(registry);this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
}

一直跟进load()。

//BeanDefinitionLoader.class
private int load(Class<?> source) {if (this.isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {GroovyBeanDefinitionSource loader = (GroovyBeanDefinitionSource)BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);this.load(loader);}//启动类会走进这里if (this.isEligible(source)) {//将启动类注册进beanDefinitionMapthis.annotatedReader.register(new Class[]{source});return 1;} else {return 0;}
}

进入register()方法。

//AnnotatedBeanDefinitionReader.class
public void register(Class<?>... componentClasses) {Class[] var2 = componentClasses;int var3 = componentClasses.length;for(int var4 = 0; var4 < var3; ++var4) {Class<?> componentClass = var2[var4];this.registerBean(componentClass);}}public void registerBean(Class<?> beanClass) {this.doRegisterBean(beanClass, (String)null, (Class[])null, (Supplier)null, (BeanDefinitionCustomizer[])null);
}private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name, @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier, @Nullable BeanDefinitionCustomizer[] customizers) {//将beanClass(这里是启动类DemoApplication)封装成AnnotatedGenericBeanDefinitionAnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);if (!this.conditionEvaluator.shouldSkip(abd.getMetadata())) {abd.setInstanceSupplier(supplier);ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);abd.setScope(scopeMetadata.getScopeName());String beanName = name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry);AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);int var10;int var11;if (qualifiers != null) {Class[] var9 = qualifiers;var10 = qualifiers.length;for(var11 = 0; var11 < var10; ++var11) {Class<? extends Annotation> qualifier = var9[var11];if (Primary.class == qualifier) {abd.setPrimary(true);} else if (Lazy.class == qualifier) {abd.setLazyInit(true);} else {abd.addQualifier(new AutowireCandidateQualifier(qualifier));}}}if (customizers != null) {BeanDefinitionCustomizer[] var13 = customizers;var10 = customizers.length;for(var11 = 0; var11 < var10; ++var11) {BeanDefinitionCustomizer customizer = var13[var11];customizer.customize(abd);}}//创建BeanDefinitionHolder,就是把BeanDefinition包装了一下,添加了一些属性和方法。BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);//将启动类BeanDefinition注册到IOC容器的map中。BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);}
}

registerBeanDefinition()方法实际会调用BeanDefinitionRegistry的registerBeanDefinition()方法,它在DefaultListableBeanFactory实现类里实现的,之前说过这个就是IOC容器。

//DefaultListableBeanFactory.class
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {Assert.hasText(beanName, "Bean name must not be empty");Assert.notNull(beanDefinition, "BeanDefinition must not be null");if (beanDefinition instanceof AbstractBeanDefinition) {try {((AbstractBeanDefinition)beanDefinition).validate();} catch (BeanDefinitionValidationException var8) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", var8);}}//IOC容器beanFactory的beanDefinitionMapBeanDefinition existingDefinition = (BeanDefinition)this.beanDefinitionMap.get(beanName);if (existingDefinition != null) {if (!this.isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);}if (existingDefinition.getRole() < beanDefinition.getRole()) {if (this.logger.isInfoEnabled()) {this.logger.info("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");}} else if (!beanDefinition.equals(existingDefinition)) {if (this.logger.isDebugEnabled()) {this.logger.debug("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");}} else if (this.logger.isTraceEnabled()) {this.logger.trace("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");}this.beanDefinitionMap.put(beanName, beanDefinition);} else {if (this.hasBeanCreationStarted()) {synchronized(this.beanDefinitionMap) {this.beanDefinitionMap.put(beanName, beanDefinition);List<String> updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1);updatedDefinitions.addAll(this.beanDefinitionNames);updatedDefinitions.add(beanName);this.beanDefinitionNames = updatedDefinitions;this.removeManualSingletonName(beanName);}} else {//启动阶段会走到这里,以beanName为key,beanDefinition为value存入beanDefinitionMap中this.beanDefinitionMap.put(beanName, beanDefinition);this.beanDefinitionNames.add(beanName);this.removeManualSingletonName(beanName);}this.frozenBeanDefinitionNames = null;}if (existingDefinition == null && !this.containsSingleton(beanName)) {if (this.isConfigurationFrozen()) {this.clearByTypeCache();}} else {this.resetBeanDefinition(beanName);}}

注册完之后可以看到demoApplication已经被添加到beanFactory的beanDefinitionMap中了。

image-20240908180912921

2.4、刷新上下文refreshContext()

Springboot最终在这里调用Spring的refresh()方法,从refreshContext()一直跟进去会到AbstractApplicationContext的refresh()方法。

//AbstractApplicationContext.class
public void refresh() throws BeansException, IllegalStateException {synchronized(this.startupShutdownMonitor) {this.prepareRefresh();//2.4.1 获取容器ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();//准备beanFactory,这个方法就是为beanFactory添加一些组件和配置,比如后置处理器、类加载器、依赖处理器等等this.prepareBeanFactory(beanFactory);try {//在上下文添加一些BeanFactory后置处理器,处理器在beanDefinition加载完后,bean实例化之前执行this.postProcessBeanFactory(beanFactory);//2.4.2 调用BeanFactory后置处理器,这一步会进行IOC容器的初始化this.invokeBeanFactoryPostProcessors(beanFactory);//注册bean的后置处理器,上面是BeanFactory后置处理器this.registerBeanPostProcessors(beanFactory);this.initMessageSource();this.initApplicationEventMulticaster();this.onRefresh();this.registerListeners();//上面这些不看了,直接从这开始//2.4.3 完成所有单例bean的初始化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();}}
}
2.4.1 obtainFreshBeanFactory()获取容器

之前说过Springboot在创建应用上下文时,触发了GenericApplicationContext的构造函数,创建了beanFactory,这个方法就是获取之前创建的beanFactory。

//AbstractApplicationContext.class
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {this.refreshBeanFactory();return this.getBeanFactory();
}

这两个方法都是模板方法,由子类GenericApplicationContext实现

//GenericApplicationContext.class
protected final void refreshBeanFactory() throws IllegalStateException {if (!this.refreshed.compareAndSet(false, true)) {throw new IllegalStateException("GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");} else {this.beanFactory.setSerializationId(this.getId());}
}public final ConfigurableListableBeanFactory getBeanFactory() {return this.beanFactory;
}
2.4.2 invokeBeanFactoryPostProcessors()
public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
...//默认的beanFactoryPostProcessors有三个,SharedMetadataReaferFactoryContextInitializer、ConfigurationWArningsApplicationContextInitializer、ConfigFileApplicationListenerIterator var6 = beanFactoryPostProcessors.iterator();//循环将它们注册到beanDefinitionMap中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);}}currentRegistryProcessors = new ArrayList();//这里postProcessorNames是org.springframework.context.annotation.internalConfigurationAnnotationProcessor,internalConfigurationAnnotationProcessor是专门用来处理Configuration注解的类。postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);String[] var16 = postProcessorNames;var9 = postProcessorNames.length;int var10;String ppName;for(var10 = 0; var10 < var9; ++var10) {ppName = var16[var10];if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {//通过getBean,从实例化了ConfigurationClassPostProcessor到容器中currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));processedBeans.add(ppName);}}sortPostProcessors(currentRegistryProcessors, beanFactory);registryProcessors.addAll(currentRegistryProcessors);//对于使用基于@Configuration注解和@bean注入的对象, 此时的processor类型为ConfigurationClassPostprocessor, 且在此处执行//如果是@Autowired和@Value注解注入的对象,processor类型应该是AutowiredAnnotationBeanPostProcessor,这些东西是在AnnotationConfigUtils类下定义的//2.4.2.1 进去重点看看此时的processor类型为ConfigurationClassPostprocessor的postProcessBeanDefinitionRegistry()做了什么invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);...
}
2.4.2.1 invokeBeanDefinitionRegistryPostProcessors()

一直跟进这个方法,知道ConfigurationClassPostProcessor.class中的parser.parse(candidates)方法,看下面这张图,这时候的只有这个启动类,接下来继续跟进这个方法。

image-20240909162509314

//ConfigurationClassParser.class
public void parse(Set<BeanDefinitionHolder> configCandidates) {Iterator var2 = configCandidates.iterator();while(var2.hasNext()) {BeanDefinitionHolder holder = (BeanDefinitionHolder)var2.next();BeanDefinition bd = holder.getBeanDefinition();try {//会走这里,之前看到过启动类是被封装成AnnotatedBeanDefinition的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);}}//springboot自动装配的入口this.deferredImportSelectorHandler.process();
}

继续跟进parse()方法直到ConfigurationClassParser.class的doProcessConfigurationClass()方法。

//ConfigurationClassParser.classprotected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {...do {//主要就是这个方法进行处理sourceClass = this.doProcessConfigurationClass(configClass, sourceClass, filter);} while(sourceClass != null);this.configurationClasses.put(configClass, configClass);}}protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter) throws IOException {//递归处理内部类,启动类是没有内部类的,如果是启动类这里进去什么都不做if (configClass.getMetadata().isAnnotated(Component.class.getName())) {this.processMemberClasses(configClass, sourceClass, filter);}//处理@PropertySource注解配置的属性,对于启动类来说,这里也没有Iterator var4 = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class, PropertySource.class).iterator();AnnotationAttributes importResource;while(var4.hasNext()) {importResource = (AnnotationAttributes)var4.next();if (this.environment instanceof ConfigurableEnvironment) {this.processPropertySource(importResource);} else {this.logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment");}}//处理@ComponentScan注解,扫描项目中的bean,启动类上包含这个注解Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {Iterator var14 = componentScans.iterator();while(var14.hasNext()) {AnnotationAttributes componentScan = (AnnotationAttributes)var14.next();//2.4.2.2 扫描项目中的beanSet<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());Iterator var8 = scannedBeanDefinitions.iterator();while(var8.hasNext()) {BeanDefinitionHolder holder = (BeanDefinitionHolder)var8.next();BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();if (bdCand == null) {bdCand = holder.getBeanDefinition();}if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {//todo:解析扫描到的bean,这里又循环会去了,可能是再从它们所在的包继续向下扫描?this.parse(bdCand.getBeanClassName(), holder.getBeanName());}}}}//2.4.2.3 递归处理@Import注解,SpringBoot中经常用的各种@Enable***注解基本都是封装的@Importthis.processImports(configClass, sourceClass, this.getImports(sourceClass), filter, true);...return null;}
2.4.2.2 扫描bean

主要看看怎么从扫描bean的,这里会调用doScan()方法扫描bean,然后将beanDefinition注册到beanDefinitionMap中。

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {...for(int var5 = 0; var5 < var4; ++var5) {String basePackage = var3[var5];//从basePackage下找beanSet<BeanDefinition> candidates = this.findCandidateComponents(basePackage);Iterator var8 = candidates.iterator();while(var8.hasNext()) {BeanDefinition candidate = (BeanDefinition)var8.next();...if (this.checkCandidate(beanName, candidate)) {BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);beanDefinitions.add(definitionHolder);//注册beanDefinitionthis.registerBeanDefinition(definitionHolder, this.registry);}}}return beanDefinitions;
}

执行完上面代码,看看扫描到了那些bean,这两个bean是我们提前写好的。

image-20240909171009383

@Configuration
public class Config {@Beanpublic MyService getMyService(MyService myService){return myService;}
}

回到2.4.2.1的doProcessConfigurationClass(),此时代码执行到processImports()方法了,这个方法是处理@Import注解的。

2.4.2.3 processImports()

这个方法会处理@Import注解,这里主要从SpringBoot自动配置的角度看看这个方法,因为springboot开启自动配置的注解@EnableAutoConfiguration封装了@Import注解。进入processImports()代码。

image-20240909180847627

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter, boolean checkForCircularImports) {//先看下面那段话if (!importCandidates.isEmpty()) {if (checkForCircularImports && this.isChainedImportOnStack(configClass)) {this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));} else {this.importStack.push(configClass);try {Iterator var6 = importCandidates.iterator();while(var6.hasNext()) {SourceClass candidate = (SourceClass)var6.next();Class candidateClass;if (candidate.isAssignable(ImportSelector.class)) {candidateClass = candidate.loadClass();ImportSelector selector = (ImportSelector)ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry);Predicate<String> selectorFilter = selector.getExclusionFilter();if (selectorFilter != null) {exclusionFilter = exclusionFilter.or(selectorFilter);}//AutoConfigurationImportSelector会走这里if (selector instanceof DeferredImportSelector) {this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector)selector);} else {String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());Collection<SourceClass> importSourceClasses = this.asSourceClasses(importClassNames, exclusionFilter);this.processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);}} else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {//ImportBeanDefinitionRegistrar会candidateClass = candidate.loadClass();ImportBeanDefinitionRegistrar registrar = (ImportBeanDefinitionRegistrar)ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, this.environment, this.resourceLoader, this.registry);configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());} else {
//当真正需要注册需要自动配置的类时会走这里,不过要走到这里的入口是在2.4.2.1中提到的  this.deferredImportSelectorHandler.process()这个方法                    this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());//又回到了这个方法,在2.4.2.1介绍启动类注册的时候说过了,就是注册到把beanDefinition注册到Map中,那这里就是去注册那些自动配置类了。                      this.processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);}}} catch (BeanDefinitionStoreException var17) {throw var17;} catch (Throwable var18) {throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", var18);} finally {this.importStack.pop();}}}
}

看这张图,传入的两个importCandidates一个是ImportBeanDefinitionRegistrar类,一个是ImportSelector类,对于AutoConfigurationImportSelector,更准确的说他是一个DeferredImportSelector。从名字也能看出他是延迟执行的,所以这时并不会将自动配置的类注入近来,在2.4.2.1中说过,springboot自动配置的入口是在 this.deferredImportSelectorHandler.process()这个方法下。在看上面的代码,两个不同的类会走不同的if或else。

image-20240909180322097

一直跟进代码,直到走到this.deferredImportSelectorHandler.process()。这个方法在ConfigurationClassParser.class的parse()方法下。

//ConfigurationClassParser.class
public void process() {List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;this.deferredImportSelectors = null;try {if (deferredImports != null) {DeferredImportSelectorGroupingHandler handler = ConfigurationClassParser.this.new DeferredImportSelectorGroupingHandler();deferredImports.sort(ConfigurationClassParser.DEFERRED_IMPORT_COMPARATOR);deferredImports.forEach(handler::register);//重点是这个handler.processGroupImports();}} finally {this.deferredImportSelectors = new ArrayList();}}

在跟进processGroupImports()方法。

//ConfigurationClassParser.class
public void processGroupImports() {Iterator var1 = this.groupings.values().iterator();while(var1.hasNext()) {...grouping.getImports().forEach((entry) -> {ConfigurationClass configurationClass = (ConfigurationClass)this.configurationClasses.get(entry.getMetadata());try {//又到了这个方法了,之前也提过,会走processImports()的最后一个elseConfigurationClassParser.this.processImports(configurationClass, ConfigurationClassParser.this.asSourceClass(configurationClass, exclusionFilter), Collections.singleton(ConfigurationClassParser.this.asSourceClass(entry.getImportClassName(), exclusionFilter)), exclusionFilter, false);} ...});}}

这时才真正开始注册自动配置类的beanDefinition,这时的processImports()会进入processImports()的最后一个else,processConfigurationClass()这个方法前面说过了,将beanDefinition注册到Map中。

//ConfigurationClassParser.class   
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter, boolean checkForCircularImports) {...try {Iterator var6 = importCandidates.iterator();while(var6.hasNext()) {...} else {this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());this.processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);}}} ...}

这部分就到这里了,回到2.4。

2.4.3 finishBeanFactoryInitialization()初始化bean

之前只是将bean的beanDefinition注册到IOC容器中,但是还没有初始化,bean的完整生命周期是在这个方法中完成的。我们以自定义的bean为例来看看bean的初始化。跟进代码,到AbstractBeanFactory.class的doGetBean时在断点上添加条件,这个条件就是beanName=myController时才会进去。

image-20240910165937503

//AbstractBeanFactory.class
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {String beanName = this.transformedBeanName(name);Object sharedInstance = this.getSingleton(beanName);Object bean;if (sharedInstance != null && args == null) {//...} else {//...try {//获取bean的beanDefinitionRootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);this.checkMergedBeanDefinition(mbd, beanName, args);//...//如果是单例beanif (mbd.isSingleton()) {//获取bean,这里是从缓冲池里拿,在初始化bean的过程中会把他们放到缓冲池sharedInstance = this.getSingleton(beanName, () -> {try {//bean的初始化过程是在这里完成的return this.createBean(beanName, mbd, args);} catch (BeansException var5) {this.destroySingleton(beanName);throw var5;}});bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);} else if (mbd.isPrototype()) {//...} else {//...}} catch (BeansException var26) {this.cleanupAfterBeanCreationFailure(beanName);throw var26;}}//...
}

然后一直跟进代码createBean()方法,直到AbstractAutowireCapableBeanFactory.class的doCreateBean()方法,这里完成了bean的初始化,主要包括三个步骤:1、创建bean实例。2、为bean实例进行属性注入。3、调用bean的初始化方法。

//AbstractAutowireCapableBeanFactory.class
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper == null) {//2.4.3.1 创建bean实例instanceWrapper = this.createBeanInstance(beanName, mbd, args);}Object bean = instanceWrapper.getWrappedInstance();//...//如果bean是单例且允许循环引用且正在创建,暴露一个bean的早期对象工厂,放到三级缓存boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);if (earlySingletonExposure) {//...this.addSingletonFactory(beanName, () -> {return this.getEarlyBeanReference(beanName, mbd, bean);});}Object exposedObject = bean;try {//2.4.3.2 属性注入this.populateBean(beanName, mbd, instanceWrapper);//2.4.3.3 调用初始化方法exposedObject = this.initializeBean(beanName, exposedObject, mbd);} catch (Throwable var18) {if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {throw (BeanCreationException)var18;}throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);}if (earlySingletonExposure) {Object earlySingletonReference = this.getSingleton(beanName, false);if (earlySingletonReference != null) {if (exposedObject == bean) {exposedObject = earlySingletonReference;} else if (!this.allowRawInjectionDespiteWrapping && this.hasDependentBean(beanName)) {String[] dependentBeans = this.getDependentBeans(beanName);Set<String> actualDependentBeans = new LinkedHashSet(dependentBeans.length);String[] var12 = dependentBeans;int var13 = dependentBeans.length;for(int var14 = 0; var14 < var13; ++var14) {String dependentBean = var12[var14];if (!this.removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {actualDependentBeans.add(dependentBean);}}//...}}}try {this.registerDisposableBeanIfNecessary(beanName, bean, mbd);return exposedObject;} catch (BeanDefinitionValidationException var16) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", var16);}
}
2.4.3.1 createBeanInstance()创建bean实例
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {Class<?> beanClass = this.resolveBeanClass(mbd, beanName, new Class[0]);if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && //...} else {Supplier<?> instanceSupplier = mbd.getInstanceSupplier();if (instanceSupplier != null) {return this.obtainFromSupplier(instanceSupplier, beanName);} else if (mbd.getFactoryMethodName() != null) {return this.instantiateUsingFactoryMethod(beanName, mbd, args);} else {//...if (resolved) {//...} else {Constructor<?>[] ctors = this.determineConstructorsFromBeanPostProcessors(beanClass, beanName);if (ctors == null && mbd.getResolvedAutowireMode() != 3 && !mbd.hasConstructorArgumentValues() && ObjectUtils.isEmpty(args)) {ctors = mbd.getPreferredConstructors();//对于myController这个我们自定义的bean,会进入instantiateBean()方法,通过BeanUtils创建bean实例return ctors != null ? this.autowireConstructor(beanName, mbd, ctors, (Object[])null) : this.instantiateBean(beanName, mbd);} else {return this.autowireConstructor(beanName, mbd, ctors, args);}}}}
}
2.4.3.2 populateBean()属性注入
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {if (bw == null) {//...} else {//...boolean hasInstAwareBpps = this.hasInstantiationAwareBeanPostProcessors();//...if (hasInstAwareBpps) {if (pvs == null) {pvs = mbd.getPropertyValues();}Iterator var9 = this.getBeanPostProcessors().iterator();//使用后置处理器进行属性注入,对于通过@Autowired注入的属性,使用ImportAwareBeanPostProcessor这个处理器,用它完成属性注入,它是InstantiationAwareBeanPostProcessor的一个实现类。while(var9.hasNext()) {BeanPostProcessor bp = (BeanPostProcessor)var9.next();if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor)bp;//假如MyController中注入了MyService,在这里会先去初始化MyService,流程和MyController一样,之后再回到MyController的生命周期进行属性注入。PropertyValues pvsToUse = ibp.postProcessProperties((PropertyValues)pvs, bw.getWrappedInstance(), beanName);//...}}}//...}
}

进入postProcessProperties()看看注入逻辑。

//AutowriedAnnotationBeanPostProcessor.class
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {//寻找要注入的属性InjectionMetadata metadata = this.findAutowiringMetadata(beanName, bean.getClass(), pvs);try {//注入metadata.inject(bean, beanName, pvs);return pvs;} catch (BeanCreationException var6) {throw var6;} catch (Throwable var7) {throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", var7);}
}

findAutowiringMetadata()可以找到要注入的属性,看下面的图,找到了要注入到MyController中的MyService。

image-20240910213820166

然后执行inject()方法进行注入,注入MyService时会先去初始化MyService,流程是一样的。

2.4.3.3 initializeBean()调用初始化方法
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {if (System.getSecurityManager() != null) {AccessController.doPrivileged(() -> {this.invokeAwareMethods(beanName, bean);return null;}, this.getAccessControlContext());} else {//调用Aware接口的实现,设置相关的beanName、beanClassLoader、BeanFactory等,自己定义的bean好像不会走这。this.invokeAwareMethods(beanName, bean);}Object wrappedBean = bean;if (mbd == null || !mbd.isSynthetic()) {//在初始化之前调用BeanPostProcessor,为了方便扩展吧wrappedBean = this.applyBeanPostProcessorsBeforeInitialization(bean, beanName);}try {//调用初始化方法this.invokeInitMethods(beanName, wrappedBean, mbd);} catch (Throwable var6) {throw new BeanCreationException(mbd != null ? mbd.getResourceDescription() : null, beanName, "Invocation of init method failed", var6);}if (mbd == null || !mbd.isSynthetic()) {//初始化之后调用BeanPostProcessorwrappedBean = this.applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);}return wrappedBean;
}

接下来看一下invokeInitMethods()方法,有两个初始化方法,第一个是实现InitializingBean接口并重写afterPropertiesSet()方法,第二种方法是设置init-method方法。springboot会判断我们有没有定义这些方法,如果有就按顺序执行就好了。

protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd) throws Throwable {//判断bean有没有实现InitializingBean接口并重写afterPropertiesSet()方法,如果判断为ture,那就调用afterPropertiesSet()方法进行初始化。boolean isInitializingBean = bean instanceof InitializingBean;if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {if (this.logger.isTraceEnabled()) {this.logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");}if (System.getSecurityManager() != null) {try {AccessController.doPrivileged(() -> {((InitializingBean)bean).afterPropertiesSet();return null;}, this.getAccessControlContext());} catch (PrivilegedActionException var6) {throw var6.getException();}} else {//调用afterPropertiesSet()进行初始化((InitializingBean)bean).afterPropertiesSet();}}//判断bean是否配置了init-method方法,如果判断为true,通过反射得到method对象,然后调用init-method,完成最后的初始化。if (mbd != null && bean.getClass() != NullBean.class) {String initMethodName = mbd.getInitMethodName();if (StringUtils.hasLength(initMethodName) && (!isInitializingBean || !"afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) {this.invokeCustomInitMethod(beanName, bean, mbd);}}}

自定义这两种方法也很简单,如下:

//实现InitializingBean接口并重写afterPropertiesSet()方法
@Controller
public class MyController implements InitializingBean {@Autowiredprivate MyService myService;public void testController(){return;}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("method 1");}
}//配置了init-method方法
@Configuration
public class Config {@Bean(initMethod = "test")public MyService getMyService(MyService myService){return myService;}
}
@Service
public class MyService {public void test(){System.out.println("haha");}
}

到这里单例bean就初始化完了,结束。

3、补充:循环依赖

Spring解决循环依赖的流程如下图所示。

没有AOP的情况下:

image-20240911223909057

Spring通过三级缓存解决循环依赖问题,假设A和B发生了循环依赖,在A的生命周期中,在A创建了实例之后,会生成一个A的对象工厂并将其放入三级缓存,在A进行属性注入时,发生A依赖了B,这时Spring会先去创建B,和A的流程一样,当B进行属性注入时发现依赖了A,这时Spring又会调用doGetBean()想去创建A,但是在创建A之前,会先调用getSingleton()方法从缓存中取,这时Spring从三级缓存中调用A的对象工厂来生成一个A的早期对象,然后把它放到二级缓存。将A注入B之后,B会走完剩下的生命周期,然后通过getSingleton()方法将B放入一级缓存。接下来,A会走完剩下的生命周期,并且会把A放入一级缓存。就这样,A和B就都初始化完了。

有AOP的情况下:

image-20240911223622854

AOP动态代理的处理一般是发生在bean初始化之后的,也就是initializeBean()方法中的最后执行applyBeanPostProcessorsAfterInitialization()时生成代理对象,但是当出现循环依赖时,可以发现bean的AOP代理提前发生了。我觉得将AOP代理提前才是为什么需要第三级缓存的原因把。

还是看上面这个例子,当A经过实例化createInstance之后,会生成一个早期bean工厂放入三级缓存,代码是这样的,可以看到它传入了一个lambda表达时,当三级缓存中的早期bean工厂执行getObject()时会触发这个lambda表达式调用getEarlyBeanReference(),也就是当B需要注入A时,需要走到这一步。如果A被AOP代理了,这个这个方法会去调wrapIfNecessary()方法生成A的代理对象。如果没有三级缓存,那么就无法提前生成bean的代理对象,那么B中就会注入A的原始对象,但是当A经过初始化之后再去生成AOP代理对象,就会出现多个A,这不符合单例模式。

this.addSingletonFactory(beanName, () -> {return this.getEarlyBeanReference(beanName, mbd, bean);
});

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/59494.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

Python打包完成后报错,如何解决?

大家好,我是Python进阶者。 一、前言 前几天在Python最强王者交流群【钟爱一生】问了一个Python打包处理数据的问题,问题如下: 打包完成后报错:发生错误:Missing optional dependency openpyxl.Use pip or conda to install openpyxl.哪位大佬帮我看一下错在哪了?二、实现…

HashMap源码分析

HashMap源码分析在jdk1.8中,HashMap的数据结构如上图所示,是由Node数组+链表/红黑树组成的,每个K-V对保存在一个Node结点中,看一下Node结点的定义,其实就是一个Map.Entry<K,V>的实现类,包括key的hash值,key,value和一个next指针。 static class Node<K,V> …

408存储系统大题笔记

咸鱼25计组强化课P2的笔记,有点简陋。 课后需要把第三章的课后大题(真题做一下) Cache类题目做题要注意的点!!PA的位数=Cache地址总位数 Cache总行数 Cache块大小 第2和第3相乘得Cache数据区总大小(!=Cache总大小) 映射方式 一致性问题:写策略(直写/回写) 替换算法 CPU…

idea中的docker部署配置

注意:确认本地已安装docker环境 第一步:idea安装docker插件:设置-插件-docker第二步:配置Dockerfile文件FROM harbor.chint.com/wz-build-env-public/openjdk:17 AS base# 项目的端口,内部服务端口 EXPOSE 8808# 切换到容器内部的 /workdir目录 WORKDIR /workdir# 添加要运…

第十一章 图论 Part8

dijkstra(朴素版) 拓扑排序目录单源最短路径算法dijkstra(朴素版)适用范围(权值不能为负数的单源最短路径)思路算法正确性拓扑排序思路心得 单源最短路径算法 dijkstra(朴素版) 适用范围(权值不能为负数的单源最短路径) 思路 基本类似Prim算法,只是新加入(确定)点…

Dockerfile 实战指南:轻松掌握容器化部署!

Dockerfile 非常重要,在实际工作中,使用 Docker 绝不是敲敲一些常用命令即可。Dockerfile 几乎贯穿微服务的全部内容,务必掌握。Dockerfile 非常重要,在实际工作中,使用 Docker 绝不是敲敲一些常用命令即可。Dockerfile 几乎贯穿微服务的全部内容,务必掌握。 不要求能从头…

Hodgkin-Huxley Model 完全推导

Hodgkin-Huxley Model 膜电流计算的相关公式的完全推导,从物理、数学到公式推导。Ciallo~(∠・ω< )⌒★ 我是赤川鹤鸣。本文假设您已经初步了解了 Hodgkin-Huxley Model,这里只是针对其中的公式的一些推导。不会对其优缺点、特性、应用等进行详述。物理基础知识如果已学…

PbootCMS自定义前台404错误页面

PbootCMS已经内置支持自定义内容地址错误情况下错误页面的自定义功能,只需要在站点根目录下定义404.html文件即可扫码添加技术【解决问题】专注中小企业网站建设、网站安全12年。熟悉各种CMS,精通PHP+MYSQL、HTML5、CSS3、Javascript等。承接:企业仿站、网站修改、网站改版、…