『SpringBoot 源码分析』自动配置

news2024/12/28 21:31:16

『SpringBoot 源码分析』自动装配

  • 基于 2.2.9.RELEASE
  • 问题:Spring Boot 到底是如何进行自动配置的,都把哪些组件进行了自动配置?
  1. 首先创建测试主程序
package com.lagou;

@SpringBootApplication//标注在类上说明这个类是`SpringBoot`的主配置类
public class SpringBootMytestApplication{

	public static void main(String[] args) {
		SpringApplication.run(SpringBootMytestApplication.class, args);
	}
}
  1. 创建测试 Controller
package com.lagou.controller;

@RestController
public class TestController {

	@RequestMapping("/test")
	public String test(){
		System.out.println("源码环境构建成功...");
		return "源码环境构建成功";
	}
}

@SpringBootConfiguration

  1. 首先在主程序当中,我们通常会在主程序标记一个 @SpringBootApplication 注解
@SpringBootApplication // 1. 标注在类上说明这个类是`SpringBoot`的主配置类
public class SpringBootMytestApplication{

	public static void main(String[] args) {
		SpringApplication.run(SpringBootMytestApplication.class, args);
	}
}
  1. 当点击进去注解之后,会发现注解是个组合注解,其中 @SpringBootConfiguration@EnableAutoConfiguration@ComponentScan 是 Spring 定义的
@Target(ElementType.TYPE) // 注解的适用范围,Type 表示注解可以描述在类、接口、注解或枚举中
@Retention(RetentionPolicy.RUNTIME) // 表示注解的生命周期,Runtime 运行时
@Documented // 表示注解可以记录在 javadoc 中
@Inherited // 表示可以被子类继承该注解
@SpringBootConfiguration  // 1. 标明该类为配置类
@EnableAutoConfiguration  // 2. 启动自动配置功能
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) // 3. 注解扫描
public @interface SpringBootApplication {

	// 根据class来排除特定的类,使其不能加入spring容器,传入参数value类型是class类型。
	@AliasFor(annotation = EnableAutoConfiguration.class)
	Class<?>[] exclude() default {};

	// 根据classname 来排除特定的类,使其不能加入spring容器,传入参数value类型是class的全类名字符串数组
	@AliasFor(annotation = EnableAutoConfiguration.class)
	String[] excludeName() default {};

	// 扫描特定的包,参数类似是Class类型数组。
	@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
	Class<?>[] scanBasePackageClasses() default {};

	@AliasFor(annotation = Configuration.class)
	boolean proxyBeanMethods() default true;

}
  1. @SpringBootConfiguration:当我们再点击进去之后,会发现其实是个包装了 @Configuration 的注解,也就是说标注了 @SpringBootApplication 的类,其实是主配置类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration // 1. 能力同Configuration
public @interface SpringBootConfiguration {

	@AliasFor(annotation = Configuration.class)
	boolean proxyBeanMethods() default true;
}

@EnableAutoConfiguration

  1. 上面只是阐述了 @SpringBootConfiguration 是标注主配置类的一个关键配置,但是并没有引入自动装配的组件,还有一个注解 @EnableAutoConfiguration,用于启动自动配置功能
  • 重点:Spring 很多以 Enable 开头的注解,其作用是借助 @Import 来收集并注册特定场景的 bean,并加载到 IOC 容器中
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage // 1. 自动配置包
@Import(AutoConfigurationImportSelector.class) // 2. Spring的底层注解@Import,给容器中导入一个组件;
public @interface EnableAutoConfiguration {

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
	Class<?>[] exclude() default {};
	String[] excludeName() default {};
}
  1. 其中 @AutoConfigurationPackage 其实也是使用 @Import 导入组件 AutoConfigurationPackages.Registrar.class
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 1. Spring 的底层注解 @Import,给容器中导入一个组件
// 导入的组件是 AutoConfigurationPackages.Registrar.class
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
  1. 当导入 AutoConfigurationPackages.Registrar.class 时,会调用 registerBeanDefinitions() 将信息注入到注册中心
public abstract class AutoConfigurationPackages {
    ...
	static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

		@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
			// 1. 将注解标注的元信息传入,获取到相应的包名
			// 其中 new PackageImport(metadata).getPackageName() 获取到的是报名是 com.lagou
			register(registry, new PackageImport(metadata).getPackageName());
		}
     ...
	}
}
  1. 当调用 register() 方法,首先判断注册中心,是否已经包含名为 org.springframework.boot.autoconfigure.AutoConfigurationPackages 的 BasePackages.classbeanDefinition,没有的话,则创建,并且把包名 com.lagou 注册进去,供之后使用,例如给 JPA entity 的扫描器用来扫描开发人员通过注解 @Entity 定义的 entity 类
public abstract class AutoConfigurationPackages {
	
	// 1. 类名为 org.springframework.boot.autoconfigure.AutoConfigurationPackages
	private static final String BEAN = AutoConfigurationPackages.class.getName();
	...
	public static void register(BeanDefinitionRegistry registry, String... packageNames) {
		// 1. 这里参数 packageNames 缺省情况下就是一个字符串,是使用了注解
		// @SpringBootApplication 的 Spring Boot 应用程序入口类所在的包

		if (registry.containsBeanDefinition(BEAN)) {
			// 如果该 bean 已经注册,则将要注册包名称添加进去
			BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
			ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
			constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
		}
		else {
			// 2. 如果该 bean 尚未注册,则注册该 bean,参数中提供的包名称会被设置到 bean 定义中去
			GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
			beanDefinition.setBeanClass(BasePackages.class);// Holder for the base package
			beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);// packageNames放入BasePackages属性中
			beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
			registry.registerBeanDefinition(BEAN, beanDefinition);
		}
	}
	...
	static final class BasePackages {
		...
	}
}

@Import(AutoConfigurationImportSelector.class)

  1. AutoConfigurationImportSelector 可以帮助 Springboot 应用将所有符合条件的 @Configuration 配置都加载到当前 IOC 容器中。从继承的关系就可以看到,AutoConfigurationImportSelector 实现了各种 Aware,可以获取到 IOC 容器的关键配置。同时实现了 DeferredImportSelector 接口,而关键的自动配置相关逻辑,就在 DeferredImportSelectorGrouping.getImports() 方法里
    在这里插入图片描述
  2. 我们先直接来看调用,首先 DeferredImportSelectorGrouping.getImports() 会遍历 deferredImports 属性,然后通过 DeferredImportSelector.Group 来处理自动装配
class ConfigurationClassParser {
    ...
	private static class DeferredImportSelectorGrouping {
	    
		private final DeferredImportSelector.Group group;
		private final List<DeferredImportSelectorHolder> deferredImports = new ArrayList<>();
		...
		public Iterable<Group.Entry> getImports() {
			// 1. 遍历 this.deferredImports 属性 
			for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
				// 2. 其中 deferredImport.getConfigurationClass() 为 ConfigurationClass: beanName 'springBootMytestApplication', com.lagou.SpringBootMytestApplication
				// deferredImport.getImportSelector() 为 AutoConfigurationImportSelector
				// this.group 为 AutoConfigurationImportSelector.AutoConfigurationGroup
				this.group.process(deferredImport.getConfigurationClass().getMetadata(),
						deferredImport.getImportSelector());
			}
			return this.group.selectImports();
		}
	}
}
  1. 当调用 process() 方法时,会先将 deferredImportSelector 转换为 AutoConfigurationImportSelector,然后继续调用 getAutoConfigurationEntry() 方法
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	...
	private static class AutoConfigurationGroup
			implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
		
		@Override
		public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
			...
			// 1. 调用 getAutoConfigurationEntry() 方法得到自动配置类放入 autoConfigurationEntry 对象中
			// annotationMetadata 为 com.lagou.SpringBootMytestApplication 的元数据
			AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
					.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
			...
		}
	}
}
  1. 接下来会调用 getCandidateConfigurations() 方法,去扫描所有的配置类
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	...
	protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
		// 1. 得到 spring.factories 文件配置的所有自动配置类
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		...
	}
}
  1. 接下来继续会去调用 SpringFactoriesLoader.loadFactoryNames() 方法,主要是去获取所有 spring.factories 中,key 为 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的所有装配类
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		...
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		// 1. 这个方法需要传入两个参数 getSpringFactoriesLoaderFactoryClass() 和 getBeanClassLoader()
		// getSpringFactoriesLoaderFactoryClass() 这个方法返回的是 EnableAutoConfiguration.class
		// getBeanClassLoader() 这个方法返回的是 beanClassLoader(类加载器)
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
				+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}
}
  1. 当调用 loadFactoryNames() 时,会先扫描所有的 META-INF/spring.factories 下的内容,然后拿到键为 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的所有值,并返回
public final class SpringFactoriesLoader {
	
	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
	...
	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		String factoryTypeName = factoryType.getName();
		// 2. 返回 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的值
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
	}

	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
			// 1. 扫描所有 META-INF/spring.factories 的内容
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryTypeName, factoryImplementationName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}
}
  • META-INF/spring.factories 所在的位置
    在这里插入图片描述
  • 示例内容如下
    在这里插入图片描述
  1. 当拿到自动装配类以后,回到 getAutoConfigurationEntry() 方法,继续执行一些过滤步骤,包括移除重新的配置类,排除 exclude 指定的类等等
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	...
	protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
		// 获取是否有配置spring.boot.enableautoconfiguration属性,默认返回true
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);

		// 1. 得到 spring.factories 文件配置的所有自动配置类
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);

		// 2. 利用 LinkedHashSet 移除重复的配置类
		configurations = removeDuplicates(configurations);

		// 3. 得到要排除的自动配置类,比如注解属性 exclude 的配置类
		// 比如:@SpringBootApplication(exclude = FreeMarkerAutoConfiguration.class)
		// 将会获取到 exclude = FreeMarkerAutoConfiguration.class 的注解数据
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);

		// 4. 检查要被排除的配置类,因为有些不是自动配置类,故要抛出异常
		checkExcludedClasses(configurations, exclusions);

		// 5. 将要排除的配置类移除
		configurations.removeAll(exclusions);

		// 6. 过滤 @ConditionOnxxx 生效的配置
		// 因为从 spring.factories 文件获取的自动配置类太多,如果有些不必要的自动配置类都加载进内存,会造成内存浪费,因此这里需要进行过滤
		configurations = filter(configurations, autoConfigurationMetadata);

		// 7. 获取了符合条件的自动配置类后,此时触发 AutoConfigurationImportEvent 事件,
		// 目的是告诉 ConditionEvaluationReport 条件评估报告器对象来记录符合条件的自动配置类
		fireAutoConfigurationImportEvents(configurations, exclusions);

		// 8. 将符合条件和要排除的自动配置类封装进AutoConfigurationEntry对象,并返回
		return new AutoConfigurationEntry(configurations, exclusions);
	}
}
  1. 我们简单看看 filter() 流程,核心点在于过滤所有 @ConditionOnxxx 的条件,例如 CacheAutoConfiguration,它得存在相应的 bean 时,才会进行装配
    在这里插入图片描述
  2. 完成过滤以后,返回到 process() 中,还会把返回的 AutoConfigurationEntry 装进 autoConfigurationEntries 属性集合中以及把符合条件的自动装配的类作为 key,存入 entries 属性集合中
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	...
	private static class AutoConfigurationGroup
			implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
		
		@Override
		public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
			...
			// 调用 getAutoConfigurationEntry() 方法得到自动配置类放入 autoConfigurationEntry 对象中
			// annotationMetadata 为 com.lagou.SpringBootMytestApplication 的元数据
			AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
					.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);

			// 1. 又将封装了自动配置类的 autoConfigurationEntry 对象装进autoConfigurationEntries集合
			this.autoConfigurationEntries.add(autoConfigurationEntry);
			// 2. 遍历刚获取的自动配置类
			for (String importClassName : autoConfigurationEntry.getConfigurations()) {
				// 这里符合条件的自动配置类作为 key,annotationMetadata 作为值放进 entries 集合
				// 举例 key 为:org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration
				this.entries.putIfAbsent(importClassName, annotationMetadata);
			}
		}
	}
}
  1. 当处理完类的读取以后,DeferredImportSelectorGrouping 还需要执行一下 selectImports() 方法
class ConfigurationClassParser {
    ...
	private static class DeferredImportSelectorGrouping {
	    
		private final DeferredImportSelector.Group group;
		private final List<DeferredImportSelectorHolder> deferredImports = new ArrayList<>();
		...
		public Iterable<Group.Entry> getImports() {
			// 遍历 this.deferredImports 属性 
			for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
				// 其中 deferredImport.getConfigurationClass() 为 ConfigurationClass: beanName 'springBootMytestApplication', com.lagou.SpringBootMytestApplication
				// deferredImport.getImportSelector() 为 AutoConfigurationImportSelector
				// this.group 为 AutoConfigurationImportSelector.AutoConfigurationGroup
				this.group.process(deferredImport.getConfigurationClass().getMetadata(),
						deferredImport.getImportSelector());
			}
			// 1. 再次进行排除某些类,并且按照 @Order 注解排序
			return this.group.selectImports();
		}
	}
}
  1. 主要是对自动装配类再进行一次过滤,然后根据 @Order 进行排序
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	...
	private static class AutoConfigurationGroup
			implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
			
		private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>();
		private final List<AutoConfigurationEntry> autoConfigurationEntries = new ArrayList<>();

		...
		@Override
		public Iterable<Entry> selectImports() {// 进行exclude排除和order排除
			if (this.autoConfigurationEntries.isEmpty()) {
				return Collections.emptyList();
			}
			// 1. 这里得到所有要排除的自动配置类的 set 集合
			Set<String> allExclusions = this.autoConfigurationEntries.stream()
					.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
			// 这里得到经过滤后所有符合条件的自动配置类的set集合
			Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
					.map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
					.collect(Collectors.toCollection(LinkedHashSet::new));
			// 移除掉要排除的自动配置类
			processedConfigurations.removeAll(allExclusions);

			// 2. 对标注有 @Order 注解的自动配置类进行排序
			return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
					.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
					.collect(Collectors.toList());
		}
	}
}
  1. 总结
  • 源码流程
    在这里插入图片描述
  • 自动配置类总结
    在这里插入图片描述

HttpEncodingAutoConfiguration

  1. HttpEncodingAutoConfiguration 装配为例来简单再回归一下装配过程,首先肯定是去先扫描 META-INF/spring.factories 下所有的文件 key 为 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的值,经过一定条件过滤以后,再装入到容器当中。其中就包含了 org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration
    在这里插入图片描述
  2. HttpEncodingAutoConfiguration 为什么没被过滤掉呢?我们简单看看它上面条件
// 表示这是一个配置类,和以前编写的配置文件一样,也可以给容器中添加组件
@Configuration(proxyBeanMethods = false)
// 启动指定类的 ConfigurationProperties 功能;将配置文件中对应的值和 HttpEncodingProperties 绑定起来;
@EnableConfigurationProperties(HttpProperties.class)
// Spring 底层 @Conditional 注解,根据不同的条件,如果满足指定的条件,整个配置类里面的配置就会生效。
// 判断当前应用是否是 web 应用,如果是,当前配置类生效
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
// 判断当前项目有没有这个 CharacterEncodingFilter: SpringMVC中进行乱码解决的过滤器
@ConditionalOnClass(CharacterEncodingFilter.class)
// 判断配置文件中是否存在某个配置 spring.http.encoding.enabled 如果不存在,判断也是成立的
// matchIfMissing = true 表示即使我们配置文件中不配置spring.http.encoding.enabled=true,也是默认生效的
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
	
	// 它已经和 SpringBoot 配置文件中的值进行映射了
	private final HttpProperties.Encoding properties;

	// 只有一个有参构造器的情况下,参数的值就会从容器中拿
	public HttpEncodingAutoConfiguration(HttpProperties properties) {
		this.properties = properties.getEncoding();
	}
	...
}
  • @EnableConfigurationProperties:该注解起到的作用是说把配置文件(如 application.yml)中的 spring.http 开头的属性,都装到 HttpProperties 类中。例如我们想设置 spring.http.encoding.charset=utf-8,它就会自动装到对应属性中
@ConfigurationProperties(prefix = "spring.http")
public class HttpProperties {
	private final Encoding encoding = new Encoding();
	...
	public static class Encoding {

		public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
		// 1. 覆盖这个属性
		private Charset charset = DEFAULT_CHARSET;
		...
	}
}
  • @ConditionalOnWebApplication:这个是条件之一,要求的是启动的时候,容器类型是 ConditionalOnWebApplication.Type.SERVLET,只要在 pom.xml 配置了 spring-boot-starter-web,这一定会设置成这个类型
  • @ConditionalOnClass 会判断 classpath 是否存在 CharacterEncodingFilter.class 这个类,只要在 pom.xml 配置了 spring-boot-starter-web,肯定会有这个类
  • @ConditionalOnProperty 要求必须在 application.yml 中,配置了 spring.http.encoding.enabled 属性,但是属性上 matchIfMissing 允许缺失,所以这个条件也是通过的
  1. 满足了上面的所有条件后,自动装配类就没被过滤掉,加到容器中来

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

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

相关文章

PHP序列化,反序列化

一.什么是序列化和反序列化 php类与对象 类是定义一系列属性和操作的模板&#xff0c;而对象&#xff0c;就是把属性进行实例化&#xff0c;完事交给类里面的方法&#xff0c;进行处理。 <?php class people{//定义类属性&#xff08;类似变量&#xff09;,public 代表可…

【计算机网络笔记】第一章

1、计算机网络定义 计算机网络主要是由一些通用的、可编程的硬件&#xff08;包含CPU、计算机、手机、智能电器…&#xff09;互连而成的&#xff0c;而这些硬件并非专门用来实现某一特定目的&#xff08;例如&#xff0c;传送数据或视频信号&#xff09;。这些可编程的硬件能…

matplotlib库的用法——各种图的绘制

matplotlib是一个流行的Python绘图库&#xff0c;用于创建各种静态、动态、交互式可视化。以下是一些基本的用法&#xff1a; 线图 plt.plot([1, 2, 3, 4]) plt.ylabel(Some Numbers) plt.show()散点图 x [1,2,3,4,5] y [2,3,4,5,6] plt.scatter(x, y) plt.show() 条形图 …

浅析 String

浅析String 一、创建字符串二、字符串与常量池三、字符串的不可变性四、字符串的拼接五、StringBuilder和StringBuffer 一、创建字符串 //三种常用的构造字符串的方式public static void main(String[] args) {String str1 "hello";String str2 new String("w…

Effective Java笔记(26)请不要使用原生态类型

首先介绍一些术语 。 声明中具有一个或者多个类型参数&#xff08; type parameter &#xff09;的类或者接口&#xff0c;就是泛型&#xff08; generic &#xff09;类或者接口 。 例如&#xff0c;List 接口就只有单个类型参数 E &#xff0c;表示列表的元素类型 。这个接口…

码云 Gitee + Jenkins 配置教程

安装jdk 安装maven 安装Jenkins https://blog.csdn.net/minihuabei/article/details/132151292?csdn_share_tail%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22132151292%22%2C%22source%22%3A%22minihuabei%22%7D 插件安装 前往 Manage Jen…

聊聊汽车电子的话题

当谈到汽车电子时&#xff0c;有许多有趣的话题可以探讨。以下是一些可能感兴趣的话题&#xff1a; 自动驾驶技术&#xff1a;自动驾驶技术正变得越来越先进&#xff0c;它们如何在汽车中实现&#xff1f;它们将如何改变我们的交通方式以及对道路安全的影响&#xff1f; 电动汽…

【LeetCode每日一题】——85.最大矩形

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 矩阵 二【题目难度】 困难 三【题目编号】 85.最大矩形 四【题目描述】 给定一个仅包含 0 …

【Java split】split() 函数分割空字符串后数组长度为1的原因以及规避措施(105)

问题现象: import java.util.ArrayList; import java.util.Arrays; import java.util.List;public class test06 {public static void main(String[] args) {// Java split()函数 分割空字符串长度为1的解释&#xff1b;String s2 "";String[] arr2 s2.split(&quo…

[SWPUCTF 2022 新生赛]numgame

这道题有点东西网页一段计算框&#xff0c;只有加和减数字&#xff0c;但是永远到大不了20&#xff0c;页面也没啥特别的&#xff0c;准备看源码&#xff0c;但是打不开&#xff0c;我以为是环境坏掉了&#xff0c;看wp别人也这样&#xff0c;只不过大佬的开发者工具可以打开&a…

elementUi select下拉框触底加载异步分页数据

在Element UI中&#xff0c;可以通过监听select下拉框的visible-change事件来实现触底加载下一页的效果。 方式一&#xff1a;利用elementUi的事件 具体步骤如下&#xff1a; 首先&#xff0c;在select组件中设置&#xff1a;visible-change"handleVisibleChange"…

Unity之获取用户地理位置

1.直接利用三方API获取&#xff1a; 1.1 利用bilibili的api 【未知稳定性】 public void Awake() {StartCoroutine(GetLocationInfoNew());}/// <summary>/// 利用bilibili的接口通过ip直接获取城市信息/// </summary>IEnumerator GetLocationInfoNew() {//UnityW…

在 Linux 上以 All-in-One 模式安装 KubeSphere

官方文档&#xff1a;https://www.kubesphere.io/zh/docs/v3.3/quick-start/all-in-one-on-linux/ 操作系统 最低配置 Ubuntu&#xff1a; 16.04,18.04, 20.04, 22.04 2 核 CPU&#xff0c;4 GB 内存&#xff0c;40 GB 磁盘空间Debian Buste&#xff1a;Stretch 2 核 CPU&am…

Leetcode周赛 | 2023-8-5

2023-8-5 题1体会我的代码 题2体会我的代码 题1 体会 一开始是觉得这道题是贪心的&#xff0c;选出现次数最多的元素&#xff0c;但是发现&#xff0c;当有多个元素出现次数均为最多时&#xff0c;似乎很难处理&#xff0c;就放弃了。转而问ChatGPT &#xff0c;结果让自己走上…

可视化高级绘图技巧100篇-总论

前言 优秀的数据可视化作品可以用三个关键词概括&#xff1a;准确、清晰、优雅。 准确&#xff1a;精准地反馈数据的特征信息&#xff08;既不遗漏也不冗余&#xff0c;不造成读者疏漏&误读细节&#xff09; 清晰&#xff1a;获取图表特征信息的时间越短越好 优雅&…

吃瓜教程-Task05

目录 支持向量机 间隔与支持向量 SVM基本型 对偶问题 kkt条件 例子 对偶问题 例子 对偶问题原理解释 软间隔与正则化 替代损失函数 支持向量回归 例子 支持向量机 间隔与支持向量 在样本空间中&#xff0c;划分超平面可通过如下线性方程来描述: 样本空间中任意点x到…

Doris(四)-聚合模型的使用

pre 前言 这里使用聚合模型&#xff0c;可以在导入数据的时候&#xff0c;就将部分数据做预处理&#xff0c;提高查询效率。 同样&#xff0c;因为是预处理&#xff0c;因此&#xff0c;数据细节会丢失。 1, 建表语句 create table if not exists user_landing_record_new …

基于Java+SpringBoot+Vue的篮球竞赛预约平台设计与实现(源码+LW+部署文档等)

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

框框大学之——教育技术学

清一色劝退的教育技术学。。。。。。 https://www.kkdaxue.com/?current1&major%E6%95%99%E8%82%B2%E6%8A%80%E6%9C%AF%E5%AD%A6&pageSize10&sortFieldcreateTime&sortOrderdescend 总结&#xff1a; 1 杂而不经 2 摆烂劝退居多 3 适合躺平 4 考公不行 5 要多…

探秘手机隐藏的望远镜功能:开启后,观察任何你想看的地方

当今的智能手机不仅仅是通信工具&#xff0c;它们蕴藏着各种隐藏的功能&#xff0c;其中之一就是让你拥有望远镜般的观察能力。是的&#xff0c;你没有听错&#xff01;今天我们将探秘手机中隐藏的望远镜功能&#xff0c;这项神奇的功能可以让你打开后&#xff0c;轻松观察任何…