返回顶部
首页 > 资讯 > 后端开发 > Python >Spring配置类源码分析详解
  • 354
分享到

Spring配置类源码分析详解

2024-04-02 19:04:59 354人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

目录spring配置类解析源码解析配置类解析源码分析判断配置类解析配置类配置类的处理总结扩展点spring配置类解析源码解析 上一篇分析spring的启动过程中,会把BeanDefi

spring配置类解析源码解析

上一篇分析spring的启动过程中,会把BeanDefinitionReGIStryPostProcessor接口类型的子类添加到启动的过程中,其中它的一个子类ConfigurationClassPostProcessor是用来处理配置类。下面来分析spring如何处理配置类。

那么首先要知道什么是配置类?先简单来说常见的配置类就是添加了@Configuration、@ComponentScan等注解的类。后续分析源码的过程中在详细介绍。

配置类解析源码分析

上一篇分析过spring启动的过程会添加一些类用于后续bean的生命周期使用,启动过程中会执行ConfigurationClassPostProcessorprocessConfigBeanDefinitions方法。展示部分重要的代码

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		List<BeanDefinitionHolder> confiGCandidates = new ArrayList<>();
		//拿到启动过程中注入的类的名称
		String[] candidateNames = registry.getBeanDefinitionNames();
		for (String beanName : candidateNames) {
			BeanDefinition beanDef = registry.getBeanDefinition(beanName);
			if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
				if (logger.isDebugEnabled()) {
					logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
				}
			}
			//判断是不是配置类
			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}
		// Return immediately if no @Configuration classes were found
		if (configCandidates.isEmpty()) {
			return;
		}
		// Sort by previously determined @Order value, if applicable
		// 通过@Order可以排序,升序排序,order越下越靠前
		configCandidates.sort((bd1, bd2) -> {
			int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
			int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
			return Integer.compare(i1, i2);
		});
		// Parse each @Configuration class
		//构建一个配置类的解析器
		ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);
		Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
		Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
		// 递归解析配置类,有可能通过解析一个配置类,得到了其他的配置类,比如扫描和Import
		do {
			StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
			//配置类解析
			parser.parse(candidates);
			parser.validate();
			// 从解析器parse得到配置类,移除已经解析过的,剩下的就是新增的
			Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
			configClasses.removeAll(alreadyParsed);
			// Read the model and create bean definitions based on its content
			if (this.reader == null) {
				this.reader = new ConfigurationClassBeanDefinitionReader(
						registry, this.sourceExtractor, this.resourceLoader, this.environment,
						this.importBeanNameGenerator, parser.getImportRegistry());
			}
			// 把parser.parse(candidates);解析到的但是未生成BeanDefinition的配置 生成对应的BeanDefinition
			this.reader.loadBeanDefinitions(configClasses);
			alreadyParsed.addAll(configClasses);
			processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();
			// candidates中存的是BeanDefinition,configClasses中存的是ConfigurationClass
			candidates.clear();
			//loadBeanDefinitions方法会增加很多BeanDefinition 如果发现BeanDefinition增加了,则有可能增加了配置类
			//这个if对新增的BeanDefinition做处理
			if (registry.getBeanDefinitionCount() > candidateNames.length) {
				String[] newCandidateNames = registry.getBeanDefinitionNames();
				Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
				Set<String> alreadyParsedClasses = new HashSet<>();
				for (ConfigurationClass configurationClass : alreadyParsed) {
					alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
				}
				//遍历新增的BeanDefinition
				for (String candidateName : newCandidateNames) {
					if (!oldCandidateNames.contains(candidateName)) {
						BeanDefinition bd = registry.getBeanDefinition(candidateName);
						//判断新增的BeanDefinition是不是一个配置类
						if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
								!alreadyParsedClasses.contains(bd.getBeanClassName())) {
							//如果是配置类 添加到这个集合当中 交给do while 循环解析配置类
							candidates.add(new BeanDefinitionHolder(bd, candidateName));
						}
					}
				}
				candidateNames = newCandidateNames;
			}
		}
		while (!candidates.isEmpty());
	}

整个流程就是 判断哪些类是配置类,根据@order注解排序,构建一个配置类的解析器,利用do while 解析配置类。先来看看spring是如何判断哪些类是配置类的。

判断配置类

判断配置类的方法是ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)

public static boolean checkConfigurationClassCandidate(
			BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
		String className = beanDef.getBeanClassName();
		if (className == null || beanDef.getFactoryMethodName() != null) {
			return false;
		}
		// AnnotationMetadata表示某个类的注解信息,但是并一定要加载这个类
		AnnotationMetadata metadata;
		// 如果AnnotatedBeanDefinition,则直接取AnnotationMetadata
		if (beanDef instanceof AnnotatedBeanDefinition &&
				className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
			// Can reuse the pre-parsed metadata from the given BeanDefinition...
			metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
		}
		// 如果是AbstractBeanDefinition,则解析beanClass得到AnnotationMetadata
		else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
			// Check already loaded Class if present...
			// since we possibly can't even load the class file for this Class.
			Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
			if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||
					BeanPostProcessor.class.isAssignableFrom(beanClass) ||
					aopInfrastructureBean.class.isAssignableFrom(beanClass) ||
					EventListenerFactory.class.isAssignableFrom(beanClass)) {
				return false;
			}
			metadata = AnnotationMetadata.introspect(beanClass);
		}
		else {
			try {
				MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
				metadata = metadataReader.getAnnotationMetadata();
			}
			catch (IOException ex) {
				if (logger.isDebugEnabled()) {
					logger.debug("Could not find class file for introspecting configuration annotations: " +
							className, ex);
				}
				return false;
			}
		}
		//如果该类有@Configuration注解 表示是一个配置类
		Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
		// 存在@Configuration,并且proxyBeanMethods不为false(为true或为null)时,就是Full配置类
		if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
		// 存在@Configuration,并且proxyBeanMethods为false时,是lite配置类
		// 或者不存在@Configuration,但是只要存在@Component、@ComponentScan、@Import、@ImportResource四个中的一个,就是lite配置类
		// 或者不存在@Configuration,只要存在@Bean注解了的方法,就是lite配置类
		else if (config != null || isConfigurationCandidate(metadata)) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
		}
		else {
			return false;
		}
		// It's a full or lite configuration candidate... Let's determine the order value, if any.
		Integer order = getOrder(metadata);
		if (order != null) {
			beanDef.setAttribute(ORDER_ATTRIBUTE, order);
		}
		return true;
	}

首先就是拿到这个类的BeanDefinition的注解,根据注解判断有没有@Configuration,如果有该注解并且其属性proxyBeanMethods=ture,默认也是true,那么就是full配置类,如果为fasle那么就是lite配置类。这两种配置类跟代理有关可以先不用管。同时还有个或的判断isConfigurationCandidate(metadata),如果满足这个条件那么也是lite配置类。

public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
		//如果是一个接口类型,那么直接返回false 就不是一个配置类
		if (metadata.isInterface()) {
			return false;
		}
		// 只要存在@Component、@ComponentScan、@Import、@ImportResource四个中的一个,就是lite配置类
		for (String indicator : candidateIndicators) {
			if (metadata.isAnnotated(indicator)) {
				return true;
			}
		}
		// 只要存在@Bean注解了的方法,就是lite配置类
		return hasBeanMethods(metadata);
	}
static {
		candidateIndicators.add(Component.class.getName());
		candidateIndicators.add(ComponentScan.class.getName());
		candidateIndicators.add(Import.class.getName());
		candidateIndicators.add(ImportResource.class.getName());
	}

其中candidateIndicators是一个集合,包含了@Component、@ComponentScan、@Import、@ImportResource这几个注解。

因此根据上述代码可以知道什么是配置类

类上有@Component、@ComponentScan、@Import、@ImportResource、@Configuration任意一个注解的类,或者包含有@bean注解的类都是配置类。

解析配置类

拿到了配置类,排序后,构建一个配置类的解析器就开始解析。开头讲的do while 就是用来解析配置类的。核心代码在于parser.parse(candidates);,这个方法中核心处理在于processConfigurationClass这个方法。

protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
		// 条件注解判断
		if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
			return;
		}
		ConfigurationClass existingClass = this.configurationClasses.get(configClass);
		if (existingClass != null) {
			if (configClass.isImported()) {
				// OrderService导入了AccountService,UserService也导入了AccountService,就会符合这个条件
				if (existingClass.isImported()) {
					existingClass.mergeImportedBy(configClass);
				}
				// Otherwise ignore new imported config class; existing non-imported class overrides it.
				return;
			}
			else {
				// Explicit bean definition found, probably replacing an import.
				// Let's remove the old one and Go with the new one.
				this.configurationClasses.remove(configClass);
				this.knownSuperclasses.values().removeIf(configClass::equals);
			}
		}
		// Recursively process the configuration class and its superclass hierarchy.
		SourceClass sourceClass = asSourceClass(configClass, filter);
		//循环 解析了该类后 解析父类
		do {
			//核心方法 真正解析类 --> 解析类上的各种配置注解并做出对应处理
			sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
		}
		while (sourceClass != null);
		// ConfigurationClass重写了equals方法,只要两个ConfigurationClass对应的className相等就可以
		this.configurationClasses.put(configClass, configClass);
	}

其中核心代码在于doProcessConfigurationClass(configClass, sourceClass, filter)开始解析配置类,这里也有个do while ,这个do while 就是循环解决配置类的父类,如果配置类存在父类,那么就会执行解析的逻辑,如果没有就会推出循环。那么接着看如何解析配置类。

	protected final SourceClass doProcessConfigurationClass(
			ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
			throws IOException {
		//对@Component注解做处理
		if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
			// 处理内部类
			proceSSMemberClasses(configClass, sourceClass, filter);
		}
		//对@PropertySource注解做处理
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}
		//对@ComponentScan注解做处理
		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
				// 底层调用doScan 进行扫描所有的bean得到BeanDefinition
				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) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					// 检查扫描出来的BeanDefinition是不是配置类(full和lite)
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						//如果是配置类递归执行 解析配置类的方法
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}
		// getImports(sourceClass)会拿到所有导入的类
		//对@Import注解做处理
		processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
		//对@ImportResource注解做处理 @ImportResource导入一个xml作为配置文件 这里暂时没有解析xml文件
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
		if (importResource != null) {
			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
		// 解析配置类中的@Bean,但并没有真正处理@Bean,只是暂时找出来
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}
		// Process default methods on interfaces
		// 解析配置类所实现的接口中的@Bean,但并没有真正处理@Bean,只是暂时找出来
		processInterfaces(configClass, sourceClass);
		// Process superclass, if any
		//如果有父类就返回父类
		if (sourceClass.getMetadata().hasSuperClass()) {
			String superclass = sourceClass.getMetadata().getSuperClassName();
			if (superclass != null && !superclass.startsWith("java") &&
					!this.knownSuperclasses.containsKey(superclass)) {
				this.knownSuperclasses.put(superclass, configClass);
				// Superclass found, return its annotation metadata and recurse
				return sourceClass.getSuperClass();
			}
		}
		//如果没有父类 那么这个类就解析完了
		return null;
	}

大致流程就是解析配置类上的注解,根据得到的注解,做出不同的处理。如何处理通过下面的流程图来分析。doProcessConfigurationClass方法流程图:

需要注意的是除了@ComponentScan的处理会把扫描到的类注册成BeanDefinition以外,其他的处理都是记录到相关属性,后续在把这些类拿出来做处理。

那么到此parser.parse(candidates);方法就执行完成。

配置类的处理

解析完配置类后,会得到很多新的配置类,或者bean对象。通过this.reader.loadBeanDefinitions(configClasses);方法创建这些新的类的BeanDefinition。该方法的底层核心方法是loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);

private void loadBeanDefinitionsForConfigurationClass(
			ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
		if (trackedConditionEvaluator.shouldSkip(configClass)) {
			String beanName = configClass.getBeanName();
			if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
				this.registry.removeBeanDefinition(beanName);
			}
			this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
			return;
		}
		//将Importe和@component的内部类 生成BeanDefinition
		if (configClass.isImported()) {
			registerBeanDefinitionForImportedConfigurationClass(configClass);
		}
		//解析所有的@bean 生成对应的BeanDefinition
		for (BeanMethod beanMethod : configClass.getBeanMethods()) {
			loadBeanDefinitionsForBeanMethod(beanMethod);
		}
		//解析导入进来的xml文件 生成bean对应的BeanDefinition
		loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
		//执行实现了ImportBeanDefinitionRegistrar接口的方法
		loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
	}

doProcessConfigurationClass(configClass, sourceClass, filter)方法会把根据配置类上的注解做处理,比如吧Importe导入的类和@component的内部类,收集起来,所有@bean需要创建的bean封装成BeanMethod对象收集起来,ImportedResources导入的xml文件等都没有处理。那么loadBeanDefinitionsForConfigurationClass这个方法就是处理这些类的,把这类的对应的BeanDefinition创建出来。

由于创建出很多BeanDefinition,那么还需要判断哪些BeanDefinition是配置类。如果是配置加入到集合当中。通过开头讲的do while去处理这个集合。

总结

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

一:在启动Spring时,需要传入一个配置类,例如上面传入AppConfig.class。ApplicationContext会根据AppConfig类封装为一个BeanDefinition,这种BeanDefinition我们把它称为配置类BeanDefinition。

二:ConfigurationClassPostProcessor中会把配置类BeanDefinition取出来

三:构造一个ConfigurationClassParser用来解析配置类BeanDefinition,并且会生成一个配置类对象ConfigurationClass

四:如果配置类上存在@Component注解,那么解析配置类中的内部类(这里有递归,如果内部类也是配置类的话)

五:如果配置类上存在@PropertySource注解,那么则解析该注解,并得到PropertySource对象,并添加到environment中去

六:如果配置类上存在@ComponentScan注解,那么则解析该注解,进行扫描,扫描得到一系列的BeanDefinition对象,然后判断这些BeanDefinition是不是也是配置类BeanDefinition(只要存在@Component注解就是配置类,所以基本上扫描出来的都是配置类),如果是则继续解析该配置类,(也有递归),并且会生成对应的ConfigurationClass

七:如果配置类上存在@Import注解,那么则判断Import的类的类型:

如果是ImportSelector,那么调用执行selectImports方法得到类名,然后在把这个类当做配置类进行解析 也是递归如果是ImportBeanDefinitionRegistrar,那么则生成一个ImportBeanDefinitionRegistrar实例对象,并添加到配置类对象中(ConfigurationClass)的importBeanDefinitionRegistrars属性中。

八:如果配置类上存在@ImportResource注解,那么则把导入进来的资源路径存在配置类对象中的importedResources属性中

九:如果配置类中存在@Bean的方法,那么则把这些方法封装为BeanMethod对象,并添加到配置类对象中的beanMethods属性中

十:如果配置类实现了某些接口,则看这些接口内是否定义了@Bean的默认方法

十一:如果配置类有父类,则把父类当做配置类进行解析

十二:AppConfig这个配置类会对应一个ConfigurationClass,同时在解析的过程中也会生成另外的一些ConfigurationClass,接下来就利用reader来进一步解析ConfigurationClass

  • 如果ConfigurationClass是通过@Import注解导入进来的,则把这个类生成一个BeanDefinition,同时解析这个类上@Scope,@Lazy等注解信息,并注册BeanDefinition
  • 如果ConfigurationClass中存在一些BeanMethod,也就是定义了一些@Bean,那么则解析这些@Bean,并生成对应的BeanDefinition,并注册
  • 如果ConfigurationClass中导入了一些资源文件,比如xx.xml,那么则解析这些xx.xml文件,得到并注册BeanDefinition
  • 如果ConfigurationClass中导入了一些ImportBeanDefinitionRegistrar,那么则执行对应的registerBeanDefinitions进行BeanDefinition的注册

扩展点

通过spring对配置类处理,可以了解一些知识点,

  • @Import 可以导入一些类,并且这些以配置类的形式注册到spring容器中(当然也会生成Bean对象)导入的类还可以实现ImportSelector或ImportBeanDefinitionRegistrar这两个接口做出额外的扩展
  • @ImportResource 导入xxx.xml配置文件
  • @PropertySource 导入xxx.properties文件到环境变量中

Import使用demo

//定义一个类
public class Aaaa {
	public void test(){
		System.out.println("-----------Aaaa----------");
	}
}
//定义一个Import类
public class ImportDemo {
	@Bean
	public Aaaa aaaa(){
		return new Aaaa();
	}
}

测试代码

@ComponentScan(basePackages = {"service","config","aop"})
@Configuration
@Import(ImportDemo.class)
public class AppConfig {
}
public class Test {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
		Aaaa aaaa = (Aaaa)context.getBean("aaaa");
		aaaa.test();
	}
}

这样可以把Aaaa这个类放入到spring容器中。 通过这种形式可以控制那些bean在那些条件下才导入到spring中 例如在某个注解上加上@Import(ImportDemo.class)。这样只有使用了该注解后才会导入这个bean。

到此这篇关于Spring配置类源码分析详解的文章就介绍到这了,更多相关Spring配置类内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Spring配置类源码分析详解

本文链接: https://lsjlt.com/news/167162.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Spring配置类源码分析详解
    目录spring配置类解析源码解析配置类解析源码分析判断配置类解析配置类配置类的处理总结扩展点spring配置类解析源码解析 上一篇分析spring的启动过程中,会把BeanDefi...
    99+
    2024-04-02
  • Hadoop源码分析二安装配置过程详解
    目录1、 创建用户2、 安装jdk3、 修改hosts4、 配置ssh免密登录5、 安装zookeeper解压:修改配置文件修改内容如下:配置环境变量启动6、 安装hadoop对于三...
    99+
    2024-04-02
  • Mybatis-Spring源码分析图解
    Mybatis-Spring 当我们使用mybatis和spring整合后为什么下面的代码可以运行? 一个问题: 我就写了个mapper接口为什么能用? 首先来看...
    99+
    2024-04-02
  • Spring cache源码分析
    今天小编给大家分享一下Spring cache源码分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。题外话:如何阅...
    99+
    2023-06-29
  • Spring refresh()源码分析
    今天小编给大家分享一下Spring refresh()源码分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。正文p...
    99+
    2023-07-05
  • Spring常用配置及解析类的示例分析
    这篇文章将为大家详细讲解有关Spring常用配置及解析类的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。springMVC配置用法的文章很多,但具体描述清楚的不多,这里主要介绍下常用的配置项的用法...
    99+
    2023-05-30
    spring
  • Spring工厂的反射和配置文件源码分析
    这篇文章主要介绍了Spring工厂的反射和配置文件源码分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Spring工厂的反射和配置文件源码分析文章都会有所收获,下面我们一起来看看吧。反射和配置文件学习 Spr...
    99+
    2023-07-05
  • java集合类源码分析之Set详解
    Set集合与List一样,都是继承自Collection接口,常用的实现类有HashSet和TreeSet。值得注意的是,HashSet是通过HashMap来实现的而TreeSet是通过TreeMap来实现的,所以HashSet和TreeS...
    99+
    2023-05-31
    java 集合类源码 set
  • Reactcommit源码分析详解
    目录总览commitBeforeMutationEffectscommitMutationEffects插入 dom 节点获取父节点及插入位置判断当前节点是否为单节点在对应位置插入节...
    99+
    2022-11-13
    React commit React commit源码
  • gokratos源码及配置解析
    目录相关类图流程解释代码案例相关类图 项目启动时一般前置条件为解析配置文件, 我们看下这块是怎么设计的. 流程解释 config 实现 config Interface 接口, ...
    99+
    2022-12-08
    go kratos源码配置 go kratos
  • Spring单数据源的配置详解
    目录前言一、生成项目骨架(SpringBoot),运行一个简单的程序二、选择原生Spring方式配置数据源前言 spring数据源的配置网络上有很多例子,这里我也来介绍一下单数据源配...
    99+
    2024-04-02
  • Spring多个数据源配置详解
    前言 在上篇文章讲到了如何配置单数据源,但是在实际场景中,会有需要配置多个数据源的场景,比如说,我们在支付系统中,单笔操作(包含查询、插入、新增)中需要操作主库,在批量查询或者对账单...
    99+
    2024-04-02
  • 通过JDK源码角度分析Long类详解
    概况Java的Long类主要的作用就是对基本类型long进行封装,提供了一些处理long类型的方法,比如long到String类型的转换方法或String类型到long类型的转换方法,当然也包含与其他类型之间的转换方法。除此之外还有一些位相...
    99+
    2023-05-30
    jdk源码 long类 j
  • Spring源码解析CommonAnnotationBeanPostProcessor
    目录概述registerAnnotationConfigProcessorsJSR 和 JSR-250CommonAnnotationBeanPostProcessor 分析总结概述...
    99+
    2022-11-13
    Spring CommonAnnotationBeanPostProcessor Spring 源码解析
  • Spring源码BeanFactoryPostProcessor详解
    Spring源码分析-BeanFactoryPostProcessor BeanFactoryPostProcessor接口是Spring提供的对Bean的扩展点,它的子接口是Bea...
    99+
    2024-04-02
  • 详解SpringBoot自动配置源码
    一、引导加载自动配置类 @SpringBootApplication注解相当于@SpringBootConfiguration、@EnableAutoConfiguration、@C...
    99+
    2024-04-02
  • 详解SpringBoot启动代码和自动装配源码分析
    目录一、SpringBoot启动代码主线分析二、SpringBoot自动装配原理分析1.自动装配的前置知识@Import2.@SpringApplication注解分析2.1@Spr...
    99+
    2024-04-02
  • Spring Boot 详细分析Conditional自动化配置注解
    目录1. Spring Boot Condition功能与作用2. Conditional条件化系列注解介绍3. Conditional条件化注解的实现原理4. Conditiona...
    99+
    2024-04-02
  • Java ConcurrentHashMap的源码分析详解
    目录概述ForwardingNode节点TreeNodeTreeBinSizeCtl初始化初始化流程查找插入扩容红黑树的读&写读操作写操作小结容器计数总结概述 Concurr...
    99+
    2023-03-02
    Java ConcurrentHashMap源码 Java ConcurrentHashMap
  • Spring源码解析之Configuration
    目录一、@Configuration1.1 未加@Configuration1.2 加上@Configuration1.3 Cglib动态代理二、源码跟踪2.1 Annotation...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作