springboot 原理分析之自动配置

一、Condition

        Condition 是在 Spring 4.0 增加的条件判断功能,通过这个可以功能可以实现选择性的创建 Bean 操作。比如说,只有满足某一个条件才能创建这个 Bean,否则就不创建。

        SpringBoot 是如何知道要创建哪个 Bean 的?比如 SpringBoot 是如何知道要创建 RedisTemplate 的?

        其实 springboot 就是使用 Condition 来进行判断,它会去判断你当前的环境里面有没有导入 redis 的 starter,如果导入了它就帮你创建 RedisTemplate

1.1 获取 Bean

        如果我们想要在 spring 中获取一个 Bean,该如何操作呢,接下来我们演示,首先创建一个名称为 springboot-condition 的工程,如下图:

        可以在启动类里面通过 ConfigurableApplicationContext 来获取自己想要的 Bean,如下代码:

@SpringBootApplication
public class SpringbootConditionApplication {public static void main(String[] args) {// 启动 springboot 应用,返回 Spring 的 IOC 容器ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);// 获取 bean,redisTemplateObject redisTemplate = context.getBean("redisTemplate");System.out.println(redisTemplate);}}

        启动工程,如下,可以发现,并没有找到名称为 redisTemplate 的 Bean,那是因为我们没有导入 redis 的依赖。

        我们在 pom.xml 中引入 redis 的依赖,如下,然后再次运行启动类

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

        可以看到获取到了,如下图 

1.2 需求

在 Spring 的 IOC 容器中有一个 User 的 Bean,现要求:

        需求一:如果导入了 redis 坐标,则加载该 Bean;若没导入,则不加载。

        需求二:将类的判断定义为动态的。判断哪个字节码文件存在可以动态指定。

1.3 需求一实现

        首先,我们创建一个 User 和一个 UserConfig 的配置类,如下:

package com.condition.springbootcondition.demo;public class User {}
import com.condition.springbootcondition.demo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class UserConfig {@Beanpublic User user(){return new User();}}

        运行 main 方法,可以发现,可以正常获取 user 对象,如下:

@SpringBootApplication
public class SpringbootConditionApplication {public static void main(String[] args) {// 启动 springboot 应用,返回 Spring 的 IOC 容器ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);// 获取 beanObject redisTemplate = context.getBean("user");System.out.println(redisTemplate);}
}

        可是需求里面要求的是如果存在 redis 坐标才创建,现在是存不存在都创建。

        我们可以让 spring 容器在创建 bean 的时候加上条件注解 conditional,来达到动态控制的效果,修改 UserConfig 代码,如下:

package com.condition.springbootcondition.config;import com.condition.springbootcondition.condition.ClassCondition;
import com.condition.springbootcondition.demo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;@Configuration
public class UserConfig {@Bean// 使用 @Conditional 注解需要传入一个实现了 Condition 接口的实现类,并指定匹配规则@Conditional(ClassCondition.class)public User user(){return new User();}}

        编写实现类 ClassCondition 它需要实现 Condition ,代码如下:

package com.condition.springbootcondition.condition;import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;public class ClassCondition implements Condition {// match 方法,返回 true 则创建 bean,返回 false 则不创建 bean@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 思路:判断 org.springframework.data.redis.core.RedisTemplate.class 是否存在boolean flag = true;try {Class<?> aClass = Class.forName("org.springframework.data.redis.core.RedisTemplate");} catch (ClassNotFoundException e) {flag = false;}return flag;}
}

        执行 main 方法进行测试,可以发现,如果注释掉了 redis 依赖则创建 bean 失败,未注释则创建成功。

1.4 需求二实现

        在实现需求一时,我们创建了一个 ClassCondition,他只能判断 redis 的坐标,太局限了。能不能采用一种动态的方式,任意的指定类是否存在于当前的环境中。

        首先,我们创建一个自定义注解 ConditionOnClass,这个注解要完成和 conditional 注解一摸一样的功能,

package com.condition.springbootcondition.condition;import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import java.util.Map;public class ClassCondition implements Condition {/**** @param context 上下文对象,用于获取环境,IOC 容器,ClassLoader 对象* @param metadata 注解元对象,可以用于获取注解定义的属性值* @return*/@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 通过注解属性值 value 指定坐标后创建 beanMap<String, Object> map = metadata.getAnnotationAttributes(ConditionOnClass.class.getName());// 遍历获取到的 value 数组String [] value =(String[]) map.get("value");boolean flag = true;try {for(String className:value) {Class<?> cls = Class.forName(className);}} catch (ClassNotFoundException e) {flag = false;}return flag;}
}

        将自定义注解作用到 UserConfig 类上,如下:

package com.condition.springbootcondition.config;import com.condition.springbootcondition.condition.ConditionOnClass;
import com.condition.springbootcondition.demo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configurationpublic class UserConfig {@Bean// 使用 @Conditional 注解需要传入一个实现了 Condition 接口的实现类,并指定匹配规则// @Conditional(ClassCondition.class)@ConditionOnClass("org.springframework.data.redis.core.RedisTemplate")public User user(){return new User();}
}

        此时就可以实现根据传入的类名判断 bean 是否被加载了,如下图:

1.5 总结

自定义条件

        1、定义条件类:自定义类实现 Condition 接口,重写 matches 方法,在 matches 方法中进行逻辑判断,返回 boolean 值 。 matches 方法两个参数:

# 上下文对象,可以获取属性值,获取类加载器,获取BeanFactory等
context# 元数据对象,用于获取注解属性
metadata

        2、判断条件: 在初始化 Bean 时,使用 @Conditional(条件类.class)注解

SpringBoot 提供的常用条件注解

        1、ConditionalOnProperty:判断配置文件中是否有对应属性和值才初始化 Bean

        2、ConditionalOnClass:判断环境中是否有对应字节码文件才初始化

        3、BeanConditionalOnMissingBean:判断环境中没有对应 Bean 才初始化 Bean

二、切换内置 web 服务器

2.1 内置服务器

        SpringBoot 的 web 环境中默认使用 tomcat 作为内置服务器,其实 SpringBoot 提供了 4 种内置服务器供我们选择,我们可以很方便的进行切换。

        首先我们引入 web 模块的依赖,然后启动工程,如下:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>

        可以看到,此时我们的工程是部署在 tomcat 服务器上的。 

2.2 内置服务器分类

        springboot 支持的内置 web 服务器一共有四种,分别为 Jetty、Netty、tomcat 和 Undertow,我们只需要导入服务器的坐标,就可以进行服务器的切换。

        修改 pom.xml 文件,添加 jetty 依赖,去除 tomcat 的依赖,启动工程,如下:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><!--排查 tomcat 的依赖--><exclusions><exclusion><artifactId>spring-boot-starter-tomcat</artifactId><groupId>org.springframework.boot</groupId></exclusion></exclusions>
</dependency>
<!-- 引入 jetty 的依赖-->
<dependency><artifactId>spring-boot-starter-jetty</artifactId><groupId>org.springframework.boot</groupId>
</dependency>

        此时就将工程部署到了 jetty 服务器上,如下 

三、Enable* 注解

        SpringBoot 中提供了很多 Enable 开头的注解,这些注解都是用于动态启用某些功能的。而其底层原理是使用 @Import 注解导入一些配置类,实现 Bean 的动态加载。

        SpringBoot 工程是否可以直接获取 jar 包中定义的 Bean?答案是不可以的,需要做一些操作才可以。

3.1 现象演示

        接下来我们演示下不可以直接获取第三方 jar 包中定义的 Bean 的现象,首先创建两个模块,springboot-enable 和 springboot-enable-other。

        其中 springboot-enable-other 模块只是为了提供一个测试用的 Bean,在这个模块下创建一个 User 类和一个配置类 UserConfig,其他的不用配置,如下

package com.domain;public class User {
}
package com.config;import com.domain.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class UserConfig {@Beanpublic User user(){return new User();}
}

        在 springboot-enable 模块下引入 springboot-enable-other 的依赖,如下:

<dependency><groupId>com.xhf</groupId><artifactId>springboot-enable-other</artifactId><version>0.0.1-SNAPSHOT</version>
</dependency>

        测试下是否可以在 springboot-enable 模块下获取 springboot-enable-other 模块里面的 Bean,如下:

@SpringBootApplication
public class SpringbootEnableApplication {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);// 获取 BeanObject user = context.getBean("user");System.out.println(user);}
}

        可以看到是无法获取的,如下图:

3.2 原因分析

        为什么不能直接获取其他工程的 Bean 呢?我们分析下原因,在我们的启动类上面有个 @SpringBootApplication 注解,点击去,可以看到它具体的定义,如下图:

        可以看到它上面有个 @ComponentScan 的注解,这个注解规定了扫描的范围为当前引导类所在包及其子包,如下图:

        而我们的 User 类的 Bean 的所在包为 com.config,他们之间没有一个包含的关系,所以无法加载 User 类的 Bean。

3.3 问题解决

        解决方式一:使用 @ComponentScan 扫描 User 类所在的路径,如下:

@SpringBootApplication
@ComponentScan("com.config")
public class SpringbootEnableApplication {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);// 获取 BeanObject user = context.getBean("user");System.out.println(user);}
}

        解决方式二:可以使用 @Import 注解来加载类,这些类都会被 Spring 创建并放入 IOC 容器,如下:

@SpringBootApplication
@Import(UserConfig.class)
public class SpringbootEnableApplication {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);// 获取 BeanObject user = context.getBean("user");System.out.println(user);}
}

        解决方式三:可以对 @Import 注解进行封装,在 springboot-enable-other 模块里面创建一个自定义注解 @EnableUser,内容如下:

package com.config;import org.springframework.context.annotation.Import;import java.lang.annotation.*;@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 导出自己的 UserConfig
@Import(UserConfig.class)
public @interface EnableUser {
}

        将来在使用的时候只需要使用 @EnableUser 注解即可,如下:

@SpringBootApplication
@EnableUser
public class SpringbootEnableApplication {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);// 获取 BeanObject user = context.getBean("user");System.out.println(user);}
}

四、@Import注解

        @Enable* 底层依赖于 @Import 注解导入一些类,使用 @Import 导入的类会被 Spring 加载到 IOC 容器中。而 @Import 提供四种用法,下面分别介绍下。

4.1 导入Bean

        直接通过 @Import 注解,将我们所需要的 Bean 直接导入进去,代码如下:

@SpringBootApplication
@Import(User.class)
public class SpringbootEnableApplication {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);// 获取 BeanObject user = context.getBean("user");System.out.println(user);}
}

        代码执行报错了,是因为 User 类 Bean 的名字不一定叫 user。 

        我们换一种方式来获取,通过类型来获取,如下,这下就没啥问题了

@SpringBootApplication
@Import(User.class)
public class SpringbootEnableApplication {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);// 获取 BeanUser bean = context.getBean(User.class);System.out.println(bean);}
}

4.2 导入配置类

        这个我们上面演示过,只需要导入 UserConfig 即可。且 UserConfig 上面的 @Configuration 注解可以不写,如下:

@SpringBootApplication
@Import(UserConfig.class)
public class SpringbootEnableApplication {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);// 获取 BeanObject user = context.getBean(User.class);System.out.println(user);Object role = context.getBean(Role.class);System.out.println(role);}
}
public class UserConfig {@Beanpublic User user(){return new User();}@Beanpublic Role role(){return new Role();}
}

4.3 导入 ImportSelector 实现类

        在 springboot-enable-other 模块下新建一个 MyImportSelector 类并实现 ImportSelector 接口,如下:

package com.config;import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;public class MyImportSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {return new String[]{"com.domain.User","com.domain.Role"};}
}

        然后在启动类上面使用 @Import 注解导入 MyImportSelector 类即可,如下:

@SpringBootApplication
@Import(MyImportSelector.class)
public class SpringbootEnableApplication {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);// 获取 BeanObject user = context.getBean(User.class);System.out.println(user);Object role = context.getBean(Role.class);System.out.println(role);}
}

4.4 导入 ImportBeanDefinitionRegistrar 实现类

         在 springboot-enable-other 模块下新建一个 MyImportBeanDefinitionRegistrar 类并实现 ImportBeanDefinitionRegistrar 接口,如下:

package com.config;import com.domain.User;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();registry.registerBeanDefinition("user",beanDefinition);}
}

        然后在启动类上面使用 @Import 注解导入 MyImportBeanDefinitionRegistrar 类即可,如下:

@SpringBootApplication
@Import(MyImportBeanDefinitionRegistrar.class)
public class SpringbootEnableApplication {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);// 获取 BeanObject user = context.getBean(User.class);System.out.println(user);Object role = context.getBean(Role.class);System.out.println(role);}
}

五、@EnableAutoConfiguration 注解

        1、@EnableAutoConfiguration 注解内部使用 @Import(AutoConfigurationImportSelector.class) 来加载配置类。

        2、配置文件位置:META-INF/spring.factories,该配置文件中定义了大量的配置类,当 SpringBoot 应用启动时,会自动加载这些配置类,初始化 Bean

        3、并不是所有的 Bean 都会被初始化,在配置类中使用 Condition 来加载满足条件的 Bean

六、实现自定义 redis-starter

6.1 需求描述

        自定义实现一个 redis-starter,要求当导入 redis 坐标时,SpringBoot 自动创建 Jedis 的 Bean。

6.2 需求分析

        SpringBoot 提供了很多的 starter,但是有一些起步依赖 SpringBoot 并没有提供,而是由那些技术本身自己实现的,它期望和 SpringBoot 整合,所以它自己实现了起步依赖,比如说 mybatis 就是自己实现的,如下图:

        springBoot 官方所提供的起步依赖它的功能名字都在最后面,比如说上面的 -test,而 mybatis 提供的起步依赖功能名字在最前面,比如说上面的 mybatis-。

        总结起来说就是一般第三方自己实现的起步依赖功能名字都在前面, springBoot 官方所提供的起步依赖的功能名字都在最后面,以示区分。

        此时我们可以点击 mybatis 的坐标,看看它涉及到的依赖,最重要的就是 mybatis-spring-boot-autoconfigure 这个依赖,从名字上就可以看出来这个是 mybatis 自动配置的坐标。只有引入了这个坐标,我们才可以加载 mybatis 的相关类。

        同样的,我们也可以打开对应的 jar 包看下,我们可以看到,其实 mybatis-spring-boot-starter 它里面没有代码,只有一个 META-INF 文件夹,它只是将上图的那些坐标整合到一起对外提供一个依赖坐标,方标导入,仅此而已。

        我们再来看下 mybatis-spring-boot-autoconfigure 的实现,如下,可以看到,他里面就有很多代码了,如下图。

        比如说,他里面有个 MybatisAutoConfiguration 的类,他就是一个 mybatis 的自动配置类,这个自动配置类将来如果想要被 Spring 所识别,从而加载配置类里面的 Bean,它的具体实现是定义一个 META-INF 文件夹,里面会有一个 spring.factories 文件,在这个文件里面配置一个 EnableAutoConfiguration 指向上面的 MybatisAutoConfiguration,内容如下:

        SpringBoot 在启动的时候就加载 META-INF 文件夹里面的 spring.factories 文件,从而识别到这个自动配置类。

6.3 需求实现

        1、创建 redis-spring-boot-autoconfigure 模块,如下图,把没用的文件都删除掉。

        2、创建 redis-spring-boot-starter 模块,依赖 redis-spring-boot-autoconfigure 的模块,如下图,也是把没用的文件都删除掉,如下图

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><!--引入自己的 autoconfigure 模块--><dependency><groupId>com</groupId><artifactId>redis-spring-boot-autoconfigure</artifactId><version>0.0.1-SNAPSHOT</version></dependency>
</dependencies>

        3、在 redis-spring-boot-autoconfigure 模块中初始化 Jedis 的 Bean。

<!-- 引入 jedis 依赖-->
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId>
</dependency>

        接下来在 redis-spring-boot-autoconfigure 模块中编写一个核心的 redis 的自动配置类 RedisAutoConfiguration ,内容如下:

package com.redis.config;import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.Jedis;@Configuration
// 启用 RedisProperties 类,让其受 Spring IOC 容器管理
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfiguration {/*** 提供 Jedis 的bean*/@Beanpublic Jedis jedis(RedisProperties redisProperties) {// 一般使用两个参数的构造方法return new Jedis(redisProperties.getHost(), redisProperties.getPort());}
}

        创建一个 ConfigurationProperties 类让实体类和配置文件相绑定,动态的指定 redis 的 ip 地址和端口号,如下所示:

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;@Component
// 以后以 redis 开头的配置文件都会和 RedisProperties 实体类的属性相绑定
@ConfigurationProperties(prefix="redis")
public class RedisProperties {private String host = "localhost";private int port = 6379;public String getHost() {return host;}public void setHost(String host) {this.host = host;}public int getPort() {return port;}public void setPort(int port) {this.port = port;}
}

        创建 META-INF/spring.factories 文件,结构如下所示:

        并在 spring.factories 里面输入以下的内容:

org.springframework.boot.autoconfigure.EnableAutoConfiguration = com.redis.config.RedisAutoConfiguration

        4、在测试模块中引入自定义的 redis-starter 依赖,随便找一个模块,配置依赖,如下:

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><!--引入自定义的 starter--><dependency><groupId>com</groupId><artifactId>redis-spring-boot-starter</artifactId><version>0.0.1-SNAPSHOT</version></dependency>
</dependencies>

        在测试类获取 Jedis 的 Bean ,如下所示:

@SpringBootApplication
public class RedisSpringBootTestApplication {public static void main(String[] args) {// 启动 springboot 应用,返回 Spring 的 IOC 容器ConfigurableApplicationContext context = SpringApplication.run(RedisSpringBootTestApplication.class, args);// 获取 beanJedis jedis = context.getBean(Jedis.class);System.out.println(jedis);jedis.set("name","zhangsan");String name = jedis.get("name");System.out.println(name);}
}

        可以看到,获取了 Jedis 的 Bean 了。 

6.4 需求优化

6.4.1 验证配置文件

        刚才我们创建一个 ConfigurationProperties 类让实体类和配置文件相绑定,接下来我们测试下,在 application.properties 里面配置 redis 的端口号,如下:

redis.port=6666

        启动工程,报错了,可以看到,我们的配置文件是生效了的。

        当我们程序启动的时候 RedisAutoConfiguration 这个 Bean 就会被加载,我们可以给它加一些条件,当 Jedis 在的时候才会去加载这个 Bean,如下:

@Configuration
// 启用 RedisProperties 类,让其受 Spring IOC 容器管理
@EnableConfigurationProperties(RedisProperties.class)
@ConditionalOnClass(Jedis.class)
public class RedisAutoConfiguration {/*** 提供 Jedis 的bean*/@Bean// 如果没有一个名字为 jedis 的Bean 才加载这个@ConditionalOnMissingBean(name="jedis")public Jedis jedis(RedisProperties redisProperties) {System.out.println("RedisAutoConfiguration......");// 一般使用两个参数的构造方法return new Jedis(redisProperties.getHost(), redisProperties.getPort());}
}

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

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

相关文章

openGauss:准备知识1【IP地址/SSH协议/PuTTY安装和使用】

最近研究在openEuler 22.03 LTS上使用openGauss数据库。如果想要远端访问服务器&#xff0c;那么就先要了解IP地址、SSH协议等内容。 IP代表“Internet Protocol”&#xff0c;是一种网络协议&#xff0c;它定义了计算机在网络上的地址和数据传输方式。简言之&#xff0c;可以…

JAVA正则表达式第二个作用:爬取

目录 本地数据爬取&#xff1a; 本地爬取练习&#xff1a; 网络爬取&#xff1a; ----- 以下为均本地数据爬取&#xff1a; 带条件爬取 贪婪爬取和非贪婪爬取&#xff1a; 例题 1&#xff1a;使获取 1 为不贪婪 *例题 2&#xff1a;使获取 0、1 都为不贪婪 之前介绍了正…

Linux 系统中忘记了用户密码,可以通过以下步骤来重置密码

如果你在 Linux 系统中忘记了用户密码&#xff0c;可以通过以下步骤来重置密码&#xff1a; 通过 root 用户重置密码&#xff1a; 进入恢复模式&#xff1a; 重新启动计算机。在 GRUB 启动画面选择引导项时&#xff0c;选择 "恢复模式" 或 "恢复模式 with netw…

【数据结构】- 单链表

先创建好SList.c、Test.c两个源文件和SList.h一个头文件。 SList.c和Test.c一样 SList.h 文件 在SList.h加上 1.单链表打印 SList.h SList.c 思路&#xff1a; 逻辑图&#xff08;之后经常用到&#xff09;&#xff1a; 物理图&#xff1a; 为了方便运行Test.c中的运行还要在…

洛谷-[NOIP1996 提高组]-挖地雷

[NOIP1996 提高组] 挖地雷 题目描述 在一个地图上有 N ( N ≤ 20 ) N\ (N \le 20) N (N≤20) 个地窖&#xff0c;每个地窖中埋有一定数量的地雷。同时&#xff0c;给出地窖之间的连接路径。当地窖及其连接的数据给出之后&#xff0c;某人可以从任一处开始挖地雷&#xff0c;…

链表中倒数最后k个结点

链表中倒数最后k个结点 链表中倒数最后k个结点_牛客题霸_牛客网输入一个长度为 n 的链表&#xff0c;设链表中的元素的值为 ai &#xff0c;返回该链表中倒数第k个节点。。题目来自【牛客题霸】https://www.nowcoder.com/practice/886370fe658f41b498d40fb34ae76ff9 描述 输…

RNN:Recurrent Neural Network(上)

目录 1 为什么提出 RNN 1.1 什么是 Slot Filling 1.2 为什么 FFN 做不好 Slot Filling 1.3 为什么 RNN 能做好 Slot Filling 2 RNN 的整体结构 3 更高级的 RNN 结构 3.1 Deep RNN 3.2 Elman Network & Jordan Network 3.3 Bidirectional RNN 原视频&…

怎么样的布局是符合可制造性的PCB布局?

满足可制造性、可装配性、可维修性要求&#xff0c;方便调试的时候于检测和返修&#xff0c;能够方便的拆卸器件&#xff1a; 1&#xff09;极性器件的方向不要超过2种&#xff0c;最好都进行统一方向等要求&#xff0c;如图1-1所示&#xff1b; 图1-1 极性器件方向统一摆放 2…

k8s的坑,从这里开始

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 以前刚接触k8s时踩了不少坑&#xff0c;比如这些&#xff1a; 问题1 1、在master节点使用kubectl命令时&#xff0c;报错&…

阿里云云原生弹性方案:用弹性解决集群资源利用率难题

作者&#xff1a;赫曦 随着上云的认知更加普遍&#xff0c;我们发现除了以往占大部分的互联网类型的客户&#xff0c;一些传统的企业&#xff0c;一些制造类的和工业型企业客户也都开始使用云原生的方式去做 IT 架构的转型&#xff0c;提高集群资源使用率也成为企业上云的一致…

20. 从零用Rust编写正反向代理,四层反向代理stream(tcp与udp)实现

wmproxy wmproxy已用Rust实现http/https代理, socks5代理, 反向代理, 静态文件服务器&#xff0c;四层TCP/UDP转发&#xff0c;内网穿透&#xff0c;后续将实现websocket代理等&#xff0c;会将实现过程分享出来&#xff0c;感兴趣的可以一起造个轮子 项目地址 gite: https:…

【算法】串联所有单词的子串【滑动窗口】

题目 给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。例如&#xff0c;如果 words ["ab","cd","ef"]&#xff0c; 那么 "abcd…

2024 前端高频面试题之 JS 篇

JS 篇&#xff08;持续更新中&#xff09; 1、什么是原型、原型链&#xff1f;2、什么是继承&#xff1f;说一说有哪些&#xff1f;继承组合的原理及优点&#xff1f;3、new 操作符具体干了什么&#xff1f;4、js 有哪些方法改变 this 指向&#xff1f;5、bind 有哪些实现的注意…

通过manifest清单导入项目到gitlab中

文章目录 说明使用manifest得要求Manifest 格式演示示例 说明 从gitlab 11.2引入此功能。 GitLab 允许根据manifest清单文件&#xff08;如 Android 存储库使用的清单文件&#xff09;导入所需的 Git 存储库。 使用manifest得要求 GitLab 必须对其数据库使用 PostgreSQL。至少…

最终Docker6:nacos集群部署

目录 mysql容器构建 1.进入soft 文件夹&#xff0c;创建mysql文件夹 2.进入conf文件夹 放入my.conf 配置文件 3.运行mysql容器 4.进入script文件夹 导入 sql文件 5.进入mysql 容器 并登录 6.创建nacos 数据库并使用&#xff0c;运行nacos.sql文件 7.授予用户所有权限 部…

解析Linux中的VFS文件系统机制(根文件系统的由来)以及 ARM Linux启动流程-根文件系统的加载

《解析Linux中的VFS文件系统机制&#xff08;根文件系统的由来&#xff09;》 以及 《ARM Linux启动流程-根文件系统的加载》两篇文章都是好文章&#xff0c;但是第一篇缺少了rootfs的由来&#xff0c;让人觉得不能融会贯通&#xff0c;缺点火候。 第一篇文章&#xff1a;试图…

朗伯体光源阶数优化

参考&#xff1a; https://wenku.baidu.com/view/2512c78f162ded630b1c59eef8c75fbfc67d946b.html?wkts1705651310970&bdQuery%E6%9C%97%E4%BC%AF%E4%BD%93%E9%98%B6%E6%95%B0 https://www.xjishu.com/zhuanli/62/202211181966.html https://answer.baidu.com/answer/land…

深入理解MyBatis中的#{ }和${ }占位符及参数传递过程

深入理解MyBatis中的#{ }和${ }占位符及参数传递过程 MyBatis是一个广泛使用的持久层框架&#xff0c;它以其强大的数据库访问能力和灵活的SQL映射配置而著称。在MyBatis中&#xff0c;#{ } 和 ${ } 是两种常用的占位符&#xff0c;用于构建动态的SQL语句。本文将深入研究这两种…

扭蛋机小程序搭建:互联网时代下的“潮玩经济”

近几年&#xff0c;潮玩文化逐渐兴起&#xff0c;盲盒经济闯入了各个消费者的视野中。扭蛋机不同于盲盒&#xff0c;包含的产品多数来自于各类热门动漫IP等&#xff0c;这些产品被装进一个扭蛋中&#xff0c;大众根据概率获得不一样的产品。扭蛋机具有产品多样、好玩不贵的特点…

【Git】实习使用记录

Git 高频命令、版本回退、分支操作、文件修改删除、撤销、标签、远程仓库推送、拉取 https://blog.csdn.net/wohu1104/article/details/105601657 浏览器可以访问github仓库&#xff0c;但是使用git就用不了 https://blog.csdn.net/m0_63230155/article/details/132070860 可…