SpringBoot 自动配置原理

SpringBoot 自动配置原理

注: 本文使用的springboot版本为 2.7.11

1、@SpringBootApplication

字面分析,这个注解是标注一个Spring Boot应用。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {@Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}
), @Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {}

其中,我们需要着重分析三个注解:@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan

我们依次来进行分析

2、@SpringBootConfiguration

我们进入这个注解里面

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {}

我们可以看到 @SpringBootConfiguration 其实是 @Configuration 的派生注解,那么他们的功能就是一样了,标注这个类是一个配置类。只不过@SpringBootConfiguration是springboot的注解,而@Configuration是spring的注解。那么 @Configuration又是个什么呢?

2.1、 @Configuration

我们知道,spring之前的配置方式是通过xml文件,就像这样,将我们需要的对象以 标签的形式注入到Spring容器中。

 <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="student" class="com.x.pojo.Student"></bean></beans>

这样需要我们一个一个的写,当类很多的时候,会很麻烦。这时我们就可以用注解的方式去实现。使用注解的时候,要在配置文件中打开注解支持

<!--指定要扫描的包,这个包下的注解就会生效-->
<context:component-scan base-package="com.x"/>
<!--开启注解配置-->
<context:annotation-config/> 

我们在类上添加一个 @Component 注解,就可以将类标记为Spring中的bean了

@Component //组件 
public class User {//等价于 <property name="name" value="嘿嘿"/>public String name;@Value("嘿嘿")public void setName(String name) {this.name = name;}
}

基于 @Component 扩展的注解还有三个: @controller、@service、@repository

1@controller:   controller控制器层(注入服务)
2@service :      service服务层(注入dao)
3@repository :  dao持久层(实现dao访问)
4@component:  标注一个类为Spring容器的Bean,(把普通pojo实例化到spring容器中,相当于配置文件中的<bean id="" class=""/>

这里不具体讲四者有什么区别,只是希望大家简单的知道 @service 标记的类,说说这个类时service层的实现就够了。

回归正题:

Spring 3.0 起,支持了另外一种方式,基于Java类的配置方式。

@Configuration
public class AppConfig {@Beanpublic User user(){return new User();}
}

@Configuration 就代表这是一个 配置类。 AppConfig 就相当于我们的 applicationContext.xml

@Bean
User getUser(){return new User();//返回要注入bean的对象
}
// getUser 相当于 id
// 返回值就相当于 class 属性
<bean id="user" class="com.x.pojo.User"></bean>

这时候我们发现一个问题 @Bean 和 @Component 都可以将对象交给SPring 托管,那二者有啥区别呢

2.2、@Bean 和 @Component 的区别

@Component(和@Service和@Repository)用于自动检测和使用类路径扫描自动配置bean。注释类和bean之间存在隐式的一对一映射(即每个类一个bean)。
@Bean用于显式声明单个bean,而不是让Spring像上面那样自动执行它。它将bean的声明与类定义分离,并允许您精确地创建和配置bean

该怎么理解这段话呢?

首先 我们知道 @Component 是放在类上的,这样就可以将这个类标记为bean,并交给spring托管。我们完完全全就是在 这个类的 “源码”上进行的配置。那如果我们引入的是第三方的类,不知道源码,那我们怎么用@Component注释呢?这个时候就要用 @Bean了。也就是所谓的声明与类定义分离。我们不需要知道类是怎么定义的。我们只需要声明一个这个类的对象?

那可能又有疑问了,既然是声明对象为什么不这样写呢?

@Configuration
public class AppConfig {@BeanUser user;
}

现在我们去看@Bean的源码:这是源码的第一句话

Indicates that a method produces a bean to be managed by the Spring container.

中文意思是:指示方法生成要由 Spring 容器管理的 Bean。这样就应该能明白为啥放在方法上了吧。

关于@SpringBootConfiguration 就先到这里,它就是将这个类标记为了配置类。

3、@EnableAutoConfiguration

这个注解是自动配置的核心!!!也是重点和难点

//@EnableAutoConfiguration 源码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {...}

这里面我们需要关注两个注解 @AutoConfigurationPackage@Import({AutoConfigurationImportSelector.class})

3.1、@AutoConfigurationPackage

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({AutoConfigurationPackages.Registrar.class})
public @interface AutoConfigurationPackage {...}

从注解来看,@AutoConfigurationPackage中使用注解@Import导入了AutoConfigurationPackages.Registrar.class到容器中。

首先我们先来了解一下 @Import 这个注解

@Import

@Import注解用来帮助我们把一些需要定义为Bean的类导入到IOC容器里面,@Import可以添加在@SpringBootApplication(启动类)、@Configuration(配置类)、@Component(组件类)对应的类上。

1@Import引入普通的类可以帮助我们把普通的类定义为Bean@SpringBootApplication
// 通过@Import注解把ImportBean添加到IOC容器里面去(跟直接在bean类上加@Component一样)
@Import(ImportBean.class) 
public class MyBatisApplication {public static void main(String[] args) {SpringApplication.run(MyBatisApplication.class, args);}
}2@Import还可以引入一个@Configuration修饰的类(引入配置类),从而把让配置类生效(配置类下的所有Bean添加到IOC容器里面去)。在自定义starter的时候经常会用到。这个后面会讲到,先知道就行
注意:如果配置类在标准的SpringBoot包结构下(SpringBootApplication启动类包的根目录下)。是不需要@Import导入配置类的,SpringBoot自动帮做了。上面的情况一般用于@Configuration配置类不在标准的SpringBoot包结构下面。所以一般在自定义starter的时候用到。

现在,我们继续,我们进入 AutoConfigurationPackages.Registrar 这个类中,这个类是AutoConfigurationPackages的内部类

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {Registrar() {}public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {AutoConfigurationPackages.register(registry, (String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0]));}public Set<Object> determineImports(AnnotationMetadata metadata) {return Collections.singleton(new PackageImports(metadata));}
}

该类的重点方法为registerBeanDefinitions()

public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(
registry, 
(String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0])
);}

我们来分析一下AutoConfigurationPackages.register()的第二个参数

(String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0])

大概意思就是 一个PackageImports对象,获得了 所在的包名

我们进入类 PackageImports的源码中分析

PackageImports(AnnotationMetadata metadata) {//从提供的 AnnotationMetadata 对象中获取 AutoConfigurationPackage 注解的属性。//创建一个 AnnotationAttributes 对象,使用从注解属性中获取的值。AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(AutoConfigurationPackage.class.getName(), false));//初始化一个名为 packageNames 的 List<String>,并将其填充为从注解属性中获取的基础包名称。		List<String> packageNames = new ArrayList<>(Arrays.asList(attributes.getStringArray("basePackages")));//遍历注解属性中指定的 basePackageClasses,并将它们的包名添加到 packageNames 列表中。		for (Class<?> basePackageClass : attributes.getClassArray("basePackageClasses")) {packageNames.add(basePackageClass.getPackage().getName());}//如果没有指定基础包,就将提供的 metadata 所代表的类的包名添加到 packageNames 列表中。if (packageNames.isEmpty()) {packageNames.add(ClassUtils.getPackageName(metadata.getClassName()));}//最后,创建一个从收集到的包名构建的不可修改的列表(通过 Collections.unmodifiableList 方法),并将其赋值给类的 packageNames 字段。this.packageNames = Collections.unmodifiableList(packageNames);}

在这个构造方法中将元数据即启动类AnnotationMetadata metadata经过处理

获取标签注解信息,注解信息里面的 basePackages 和 basePackageClasses是否有数据。
basePackages、 basePackageClasses为注解@AutoConfigurationPackage中的属性。
如果没有数据则获取注解所在的类的名字目录,放到List中
获得packageNames属性也就是启动类所在的包。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们debug一下,发现确实如此

(String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0])

就是返回了 启动类所在的包名

然后回到这个方法,第一个参数是个注册表,第二个参数是 启动类所在的包

public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(
registry, 
(String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0])
);}

我们进 register()的源码中看一下

public static void register(BeanDefinitionRegistry registry, String... packageNames) {if (registry.containsBeanDefinition(BEAN)) {BasePackagesBeanDefinition beanDefinition = (BasePackagesBeanDefinition)registry.getBeanDefinition(BEAN);beanDefinition.addBasePackages(packageNames);} else {registry.registerBeanDefinition(BEAN, new BasePackagesBeanDefinition(packageNames));}
}

这个方法的if语句为判断registry这个参数中是否已经注册了AutoConfigurationPackages的类路径所对应的bean(AutoConfigurationPackages)。如若已经被注册,则把上面分析的第二个参数所获取的包(启动类所在的包的名称)添加到这个bean的定义中。如若没有,则注册这个bean并且把包名设置到该bean的定义中。

总结:@AutoConfigurationPackage就是添加该注解的类所在的包作为自动配置包进行管理。他的实现就是依赖于工具类AutoConfigurationPackages中的内部类Registrar对所标注的包进行注册

3.2、@Import({AutoConfigurationImportSelector.class})

中文意思就是自动配置导入选择器,它会导入哪些组件的选择器呢

类中有两个方法:

selectImports()
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

它调用了另外一个方法:

getAutoConfigurationEntry()
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}AnnotationAttributes attributes = getAttributes(annotationMetadata);List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);configurations = removeDuplicates(configurations);Set<String> exclusions = getExclusions(annotationMetadata, attributes);checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);configurations = getConfigurationClassFilter().filter(configurations);fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationEntry(configurations, exclusions);
}

我们先debug一下,看看 configurations 这个集合里面都是什么

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

全都是xxxAutoConfig类,但是我们并不是全都要

configurations = removeDuplicates(configurations);Set<String> exclusions = getExclusions(annotationMetadata, attributes);checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);configurations = getConfigurationClassFilter().filter(configurations);fireAutoConfigurationImportEvents(configurations, exclusions);

还进行了一系列的检查啥的,去重,去掉不要的,被过滤掉的

它又调用了另外一个方法。

getCandidateConfigurations()
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {List<String> configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()));ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).forEach(configurations::add);//如果是空的,则提示 在META-INF/spring.factories中没有自动配置类Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");return configurations;
}

这里有句断言: 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.”);

意思是:“在 META-INF/spring.factories 中没有找到自动配置类。如果您使用自定义包装,请确保该文件是正确的。“

这个方法中的返回内容是通过类SpringFactoriesLoader中的静态方法loadFactoryNames()进行获取的。那么就继续进入loadFactoryNames()

loadFactoryNames()
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {ClassLoader classLoaderToUse = classLoader;if (classLoader == null) {classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}String factoryTypeName = factoryType.getName();return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

在这个方法中classLoaderToUse是一个关键变量,在这个方法中通过所传进来的参数获得,如果为空则直接获取SpringFactoriesLoader的ClassLoader。然后将第二个参数factoryType的类名传入变量factoryTypeName,最后放入loadSpringFactories(),那么我们还需要知道这个方法实现了什么:

loadSpringFactories()
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {Map<String, List<String>> result = cache.get(classLoader);if (result != null) {return result;}result = new HashMap<>();try {//public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);while (urls.hasMoreElements()) {//获取META-INF/spring.factories的详细地址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();String[] factoryImplementationNames =StringUtils.commaDelimitedListToStringArray((String) entry.getValue());for (String factoryImplementationName : factoryImplementationNames) {result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()).add(factoryImplementationName.trim());}}}// Replace all lists with unmodifiable lists containing unique elementsresult.replaceAll((factoryType, implementations) -> implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));cache.put(classLoader, result);}catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" +FACTORIES_RESOURCE_LOCATION + "]", ex);}return result;}

我们俩看一下 result里面是啥

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

该方法作用是加载所有依赖的路径META-INF/spring.factories文件,通过map结构保存,key为文件中定义的一些标识工厂类,value就是能自动配置的一些工厂实现的类,value用list保存并去重。

------------------------------------------>public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader)
{...return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
//已经得知loadSpringFactories根据一个类加载器获得了所有的配置文件,所获得的所有配置进行getOrDefault()处理,如果存在与factoryTypeName相对应的List则返回,如果没有则获取一个空List,由loadSpringFactories可知只要classLoader不为空,则可获取所有配置文件。因此可以判断返回了foryType类名所对应的所有配置-------------------------------------->protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes){List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());...return configurations;
}
//再来分析参数一getSpringFactoriesLoaderFactoryClass()
protected Class<?> getSpringFactoriesLoaderFactoryClass() {return EnableAutoConfiguration.class;
}
//获取到了类EnableAutoConfiguration。参数二就是当前类中的全局变量beanClassLoader。
//通过这两个参数可以获得配置文件中key为EnableAutoConfiguration的所有value。因此可以判断当前方法返回的是以List的配置文件中所有的自动配置的类。---------------------------------->protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) 
{...return new AutoConfigurationEntry(configurations, exclusions);
}
//这个方法是将获取的所有自动配置类进行去重、排除、过滤等一系列的操作然后返回处理完成后的配置,并将其包装为AutoConfigurationEntry
------------------------------>@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) 
{...AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
//最后回到梦开始的地方,这一步是将所有的配置类转成字符串数组,然后通过@Import的使用方法————将数组内的所有配置导入到容器中。

总结:@EnableAutoConfiguration的实现方式是导入配置文件META-INF/spring.factories中EnableAutoConfiguration所对应的所有的类

4、@ComponentScan

@ComponentScan主要就是定义扫描的路径从中找出标识了需要装配的类自动装配到spring的bean容器中。部分源码如下:我们先部了解其他的东西,先知道它的基本作用

/*** 控制符合组件检测条件的类文件   默认是包扫描下的  **/*.class* @return*/
String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
/*** 是否对带有@Component @Repository @Service @Controller注解的类开启检测,默认是开启的* @return*/boolean useDefaultFilters() default true;

有源码可知

SpringBoot默认包扫描机制: 从启动类所在包开始,扫描当前包及其子级包下的所有文件。这也就是为什么我们在创建 package时,要在启动类的同级目录了,只有这样,我们定义的class才能被扫描到(当然,比在统一目录也可以,这是就需要我们手动指定扫描路径了,比较麻烦)。

在一个springboot项目中,我们发现没有applicationContext.xml文件,那我们的类是怎么交给 spring托管呢。就是@SpringBootApplication,它的存在使得我们启动类本身就是一个配置类(相当于配置文件了),同时 @ComponentScan 也会去扫描所有的类,将加有相应注解的类交给spring托管。

5、@Conditional

了解完自动装配的原理后,我们来关注一个细节问题,自动配置类必须在一定的条件下才能生效;

@Conditional派生注解(Spring注解版原生的@Conditional作用)

作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

那么多的自动配置类,必须在一定的条件下才能生效;也就是说,我们加载了这么多的配置类,但不是 所有的都生效了。

我们怎么知道哪些自动配置类生效?

我们可以通过启用 debug=true属性;来让控制台打印自动配置报告,这样我们就可以很方便的知道哪 些自动配置类生效;

#开启springboot的调试类
debug=true

Positive matches:(自动配置类启用的:正匹配)

Negative matches:(没有启动,没有匹配成功的自动配置类:负匹配)

Unconditional classes: (没有条件的类)

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

哈希竞猜游戏开发源码部署方案

随着互联网技术的发展&#xff0c;越来越多的人开始关注网络安全问题&#xff0c;而哈希算法作为一种重要的加密技术&#xff0c;在网络安全领域得到了广泛应用。其中&#xff0c;哈希竞猜游戏作为一种新型的网络安全挑战赛&#xff0c;也受到了越来越多人的关注。本文将介绍哈…

应用层——HTTP协议

文章目录 HTTP协议1.HTTP简介2.认识URL3.urlencode和urldecode4.HTTP协议格式&#xff08;1&#xff09;HTTP请求协议格式&#xff08;2&#xff09;HTTP响应协议格式 5.HTTP的方法6.HTTP的状态码7.HTTP常见的Header8.Cookie和Session HTTP协议 1.HTTP简介 HTTP&#xff08;Hy…

centos7 安装网络文件共享NFS详细过程

网络文件系统&#xff0c;英文Network File System(NFS)&#xff0c;是由SUN公司研制的UNIX表示层协议(presentation layer protocol)&#xff0c;能使使用者访问网络上别处的文件就像在使用自己的计算机一样。 多个服务器之间需要共享文件&#xff0c;通过NFS服务共享是一个简…

说说你对React Router的理解?常用的Router组件有哪些?

一、是什么 react-router等前端路由的原理大致相同,可以实现无刷新的条件下切换显示不同的页面 路由的本质就是页面的URL发生改变时,页面的显示结果可以根据URL的变化而变化,但是页面不会刷新 因此,可以通过前端路由可以实现单页(SPA)应用 react-router主要分成了几个不…

如何向MapInfo Pro添加自定义符号?

用户可以在MapInfo Pro中创建和使用自己的自定义图像作为符号。要访问这些自定义符号&#xff0c;请将它们放在CUSTSYMB目录中&#xff0c;然后从“符号样式”对话框&#xff08;Style>符号样式&#xff09;的“字体”列表中的“自定义符号”选项中选择它们。MapInfo Pro中的…

微信小程序_02

能够使用WXML模版语法渲染页面结构 数据绑定 1、数据绑定的基本原则 在data中定义数据在WXML中使用数据 2、在data中定义页面的数据 ​ 在页面对应的.js文件中&#xff0c;把数据定义到data对象中即可&#xff1a; Page({data:{//字符串类型的数据info:init data,//数组类…

信驰达科技加入车联网联盟(CCC),推进数字钥匙发展与应用

CCC)的会员。 图 1 深圳信驰达正式成为车联网联盟(CCC)会员 车联网联盟(CCC)是一个跨行业组织&#xff0c;致力于推动智能手机与汽车连接解决方案的技术发展。CCC涵盖了全球汽车和智能手机行业的大部分企业&#xff0c;拥有150多家成员公司。CCC成员公司包括智能手机和汽车制造…

navicat创建MySql定时任务

navicat创建MySql定时任务 前提 需要root用户权限 需要开启定时任务 1、开启定时任务 1.1 查看定时任务是否开启 mysql> show variables like event_scheduler;1.2 临时开启定时任务(下次重启后失效) set global event_scheduler on;1.3 设置永久开启定时任务 查看my…

2011年12月13日 Go生态洞察:从零到Go,在谷歌首页上的24小时飞跃

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

MacOS下VMware Fusion配置静态IP

前言 在虚拟机安装系统后&#xff0c;默认是通过DHCP动态分配的IP&#xff0c;这会导致每次重启虚拟机ip都可能会改变&#xff0c;使用起来会有很多不便。 配置静态IP 查看主机网关地址 cat /Library/Preferences/VMware\ Fusion/vmnet8/nat.conf 查看主机DNS&#xff0c;m…

云课五分钟的一些想法

起源 自中学起&#xff0c;就积极学习和掌握互联网相关知识&#xff0c;到如今已经快30年了。 个人也全程经历了从信息时代的互联网&#xff08;硬&#xff09;到智能时代的大模型&#xff08;软&#xff09;。 整体信息到智能的基础设施&#xff0c;由硬到软&#xff0c;机…

51单片机应用从零开始(二)

目录 1. 什么是单片机系统 1.1 单片机本身 1.2 构成单片机系统——单片机外围器件 2. 如何控制一个发光二极管 2.1 硬件设计&#xff08;系统电路图 &#xff09; 2.2 硬件设计&#xff08;搭建硬件电路的器材 &#xff09; 2.3 软件设计&#xff08;中文描述的程…

个人怎么投资伦敦金?

伦敦金是一种被广泛交易的黄金合约&#xff0c;是投资者参与黄金市场的一种交易方式。伦敦金投资也是黄金交易中最为方便快捷的一个种类&#xff0c;在黄金交易市场中占有较大的比例&#xff0c;每天都有来自全球各地的投资者参与买卖&#xff0c;是实现财富增益的一个有效途径…

钉钉API与集简云无代码开发连接:电商平台与营销系统的自动化集成

连接科技与能源&#xff1a;钉钉API与集简云的一次集成尝试 在数字化时代&#xff0c;许多公司面临着如何将传统的工作方式转变为更智能、高效的挑战。某能源科技有限公司也不例外&#xff0c;他们是一家专注于能源科技领域的公司&#xff0c;产品包括节能灯具、光伏逆变器、电…

PayPal的CISO谈GenAI如何提高网络安全

在最近一个季度(2023财年第二季度)&#xff0c;PayPal报告收入为73亿美元&#xff0c;同比增长7%&#xff0c;5%的交易增长和37%的增值服务收入增长带来了强劲的季度业绩。截至2022年&#xff0c;PayPal的营收为275亿美元。 在进入PayPal之前&#xff0c;Keren创建了两家网络安…

个微协议开发/微信个人号二次开发/ipad协议/api接口

E云管家&#xff0c;是完整的第三方服务平台&#xff0c;并基于IPAD协议8.0.37开发出的最新个微API服务框架。 你可以 通过API 实现 个性化微信功能 &#xff08;例云发单助手、社群小助手、客服系统、机器人等&#xff09;&#xff0c;用来自动管理微信消息。用户仅可一次对接…

研究前沿 | Science:单细胞测序助力绘制迄今最完善的灵长类动物前大脑发育图谱

引言 大脑发育的关键分子机制在啮齿动物中已有所了解&#xff0c;但在灵长类动物中仍然不清楚&#xff0c;这限制了研究者对高级认知能力起源和功能障碍的理解。此外&#xff0c;在包括人类在内的灵长类动物中&#xff0c;关于轴突投射路径上的丘脑区域和皮层区域多样化的早期分…

[WSL] 安装MySQL8

安装版本 mysql --version mysql Ver 8.0.35-0ubuntu0.20.04.1 for Linux on x86_64 ((Ubuntu))安装步骤 sudo apt-get update sudo apt-get upgradesudo apt-get install mysql-server apt install mysql-client apt install libmysqlclient-devsudo usermod -d /var/lib/m…

基于Python+Django的酒店管理系统网站平台开发

一、介绍 酒店管理系统。基于Python开发&#xff0c;前端使用HTML、CSS、BootStrap等技术搭建页面&#xff0c;后端使用Django框架处理用户响应请求&#xff0c;主要功能如下&#xff1a; 分为普通用户和管理员两个角色普通用户&#xff1a;登录、注册、查看房间详情、收藏、…

SOLIDWORKS实用技巧之焊件轮廓应用

1.焊件轮廓库官方下载入口 焊件轮廓可以自制&#xff0c;也可以从软件中在线下载获取直接使用&#xff0c;如图1&#xff0c;联网状态按ctrl左键点击下载&#xff0c;解压后获得库文件。 图1 图2 2.库放置的位置和配置 从SOLIDWORKS2014版起&#xff0c;软件焊件轮廓库支持可…