springboot的自动配置和怎么做自动配置

news2024/11/15 17:48:54

目录

一、Condition

1、Condition的具体实现

2、Condition小结

(1)自定义条件

(2)SpringBoot 提供的常用条件注解

二、@Enable注解

三、@EnableAutoConfiguration 注解和自动配置

1、@EnableAutoConfiguration的三个注解属性

2、自动配置过程       

四、自定义启动器


        开发过程中我们要配置很多依赖项和类,减少开发者的配置工作,在程序启动时自动配置功能通过检测类路径中的库和依赖项,并基于这些信息自动配置相应的 Spring Bean,从而减少了手动配置的需求。

一、Condition

        Condition 是在Spring 4.0 增加的条件判断功能,通过这个可以功能可以实现选择性的创建 Bean操作。

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

1、Condition的具体实现

        首先我们要先导入依赖坐标:

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

        首先是我们要创建一个条件类,也就是在如果有这个条件类的时候我再去创建对象。

public class ClassCondition implements Condition {

    /**
     *
     * @param context 上下文对象。用于获取环境,IOC容器,ClassLoader对象
     * @param metadata 注解元对象。 可以用于获取注解定义的属性值
     * @return
     */

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //2.需求: 导入通过注解属性值value指定坐标后创建Bean
        //获取注解属性值  value
        Map<String,Object> map = metadata.getAnnotationAttributes(ConditionOnClass.class.getName());
        System.out.println(map);
        // 得到{"com.alibaba.fastjson.JSON", "redis.clients.jedis.Jedis"}
        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;

    }
}

        这里首先实现了Condition接口,Condition 接口用于定义条件,Spring 在决定是否加载某些配置或 Bean 时会使用它。这也就是上面的问题的解决,通过条件来判断是否创建bean。

        matches 方法是 Condition 接口中的唯一方法。它的返回值决定了某些配置或 Bean 是否应该被加载。ConditionContext context:提供了上下文对象,可以用来获取 Spring 环境、IOC 容器、ClassLoader 等。AnnotatedTypeMetadata metadata:提供了注解的元数据,可以用来获取注解的属性值。

        首先通过getAnnotationAttributes方法获取ConditionOnClass注解的属性值,这里的ConditionOnClass是我们自定义的注解,等会我们来讲。

        通过map集合通过get方法获取到了里面的"com.alibaba.fastjson.JSON", "redis.clients.jedis.Jedis"两个依赖。

        通过遍历里面的类名,并尝试去加载这些类,创建这些类的对象。如果能加载成功,则保持返回true,如果不能成功加载,则抛出异常并将flag设置为false返回给ConditionOnClass自定义注解。

        然后我们要去创建一个自定义注解,因为你不能每次去判断某个类是否依赖都去在条件类里卖弄重新写入,所以通过一个自定义注解来接收测试类中传进来的依赖类来完成判断,下面是自定义注解ConditionOnClass的代码:

// 指定了这个注解可以应用的目标。ElementType.TYPE 表示注解可以用在类、接口或枚举上,ElementType.METHOD 表示注解可以用在方法上。
@Target({ElementType.TYPE,ElementType.METHOD}) // 可以修饰在类与方法上
// 指定了注解的保留策略。RUNTIME 表示注解会在运行时保留,可以通过反射机制读取。
@Retention(RetentionPolicy.RUNTIME) // 注解生效节点runtime
@Documented //生成工具文档化
// 表示这个注解的作用是条件性的,只有当 ClassCondition 条件满足时,注解修饰的类或方法才会生效。ClassCondition 是一个实现了 Condition 接口的类,用来定义条件逻辑。
@Conditional(value = ClassCondition.class)
public @interface ConditionOnClass {
    String[] value(); // 设置此注解的属性redis.clients.jedis.Jedis
}

        这段代码里面@Target定义了这个自定义注解可以用在谁身上,比如ElementType.TYPE是可以用在类、接口或枚举上,ElementType.METHOD是可以用在方法上。

        然后是@Retention是指定了保留策略,RUNTIME是代表注解会在运行时保留,并且可以通过反射机制读取,就比如测试类中传来的参数我们就可以进行保留,传递给ClassCondition进行相关操作判断。

        @Documented是指定该注解在生成 Javadoc 时会包含在文档中,便于开发者查阅。

        @Conditional()代表的是指定了自己的条件逻辑由 ClassCondition 类来实现,由它的返回值来判断自己的注解是否生效。

        @interface是用来定义自定义注解的关键字。

        value 属性是一个字符串数组,用于指定需要检查的类名(通常是全限定类名)。

        然后是我们的配置类,它通过刚才的ClassCondition条件类和ConditionOnClass自定义配置来帮助它判断是否要注册加载当前的bean。下面是配置类UserConfig类的代码:

@Configuration
public class UserConfig {

    // 情况1
    @Bean
    // 该注解的作用是通过条件类 ClassCondition 来决定 user() 方法是否应该被注册为一个 Bean。
    @ConditionOnClass(value={"com.alibaba.fastjson.JSON","redis.clients.jedis.Jedis"})
    public User user(){
        return new User();
    }

    // 情况2
    @Bean
    // 当容器中有一个key=k1且value=v1的时候user2才会注入
    // 在application.properties文件中条件k1=v1
    @ConditionalOnProperty(name = "k1",havingValue = "v1")
    public User user2(){
        return new User();
    }

}

        里面调用了@ConditionOnClass我们已经配置好的自定义注解,传入两个类路径,只有在类路径中存在 com.alibaba.fastjson.JSON 和 redis.clients.jedis.Jedis 这两个类时,ConditionOnClass调用ClassCondition判断返回true的情况 user() 方法才会被执行,返回的 User 对象才会被注册为 Bean。

        第二种user2的方式是通过@ConditionalOnProperty的注解根据配置属性的值来决定是否创建 Bean。这里我们已经准备好了配置文件(存在于application.properties中):

k1=v1

        其中的两个参数为name:指定要检查的属性名称,这里是 k1。havingValue:指定属性的期望值,这里是 v1。当 Spring 环境中的属性 k1 的值等于 v1 时user2() 方法会被执行,返回的 User 对象会被注册为 Bean;否则,user2() 方法不会被执行,也不会注册该 Bean,也就是说在这里我们user2()方法是可以被执行的。

        最后我们在测试类主入口中去通过bean的id创建一个bean对象,看是否被注册加载:

@SpringBootApplication
public class SpringbootCondition02Application {

    public static void main(String[] args) {

        //启动SpringBoot的应用,返回Spring的IOC容器
        ConfigurableApplicationContext context =  SpringApplication.run(SpringbootCondition02Application.class, args);


        /********************获取容器中user********************/
//        Object user1 = context.getBean("user");
//        System.out.println(user1);

        Object user2 = context.getBean("user");
        System.out.println(user2);

    }
}

2、Condition小结
(1)自定义条件

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

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

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

(2)SpringBoot 提供的常用条件注解

        以下注解在springBoot-autoconfigure的condition包下:

  • ConditionalOnProperty:判断配置文件中是否有对应属性和值才初始化Bean
  • ConditionalOnClass:判断环境中是否有对应字节码文件才初始化Bean
  • ConditionalOnMissingBean:判断环境中没有对应Bean才初始化Bean
  • ConditionalOnBean:判断环境中有对应Bean才初始化Bean

二、@Enable注解

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

        思考 SpringBoot 工程是否可以直接获取jar包中定义的Bean?

        这里我们就要@Import注解来解决这个问题了:

        @Enable底层依赖于@Import注解导入一些类,使用@Import导入的类会被Spring加载到IOC容器中。而@Import提供4中用法:

  • ① 导入Bean。
  • ② 导入配置类。
  • ③ 导入 ImportSelector 实现类。一般用于加载配置文件中的类。
  • ④ 导入 ImportBeanDefinitionRegistrar 实现类。

        演示示例(1)

        就比如说我们要在一个项目子模块中去配置一些依赖坐标,并去对其进行自定义注解的创建,再通过主项目模块来调用,呢该怎么实现呢?

        首先是子项目模块:

        子项目模块配置类config中的EnableUser(自定义配置类)

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(UserConfig.class)
public @interface EnableUser {
}

        里面@Target已经讲过是ElementType.TYPE用来用在类、接口或枚举上,Retention是用来运行时保留,并且可以通过反射机制读取,@Documented是生成工具文档化。最重要的是@Import,他是用来将指定的配置类导入到当前应用上下文中,就是把UserConfig 类导入到 Spring 容器中,使 UserConfig 中定义的 Bean 被注册到 Spring 容器里

        模块配置类config中的UserConfig配置类:

@Configuration
public class UserConfig {

    @Bean
    public User user() {
        return new User();
    }
    
}

        这里定义了user()方法用来封装名为user的bean。

        这里子模块项目实现了相关代码,呢么主模块就进行导入子模块的坐标,来获取他的方法和配置类等:

        <dependency>
            <groupId>com.apesource</groupId>
            <artifactId>springboot-enable_other-04</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

        再通过测试主入口进行调用:

@SpringBootApplication
//@ComponentScan("com.apesource.config")
//@Import(User.class)//导入javaBean
//@Import(UserConfig.class)
@EnableUser
//@EnableScheduling
//@EnableCaching
public class SpringbootEnable03Application {

    public static void main(String[] args) {


        ConfigurableApplicationContext context =  SpringApplication.run(SpringbootEnable03Application.class, args);

        /**
         * @SpringBootApplication中有@ComponentScan注解, 扫描范围:当前引导类所在包及其子包
         *  当前引导类所在包com.apesource.springbootenable03
         *  注入user类所在包com.apesource.springbootenable_other04.config
         *  因此扫描不到,所以容器中没有user
         *  解决方案:
         *          1.使用@ComponentScan扫描com.apesource.springbootenable_other04.config包
         *          2.可以使用@Import注解,加载类。这些类都会被Spring创建,并放入IOC容器
         *          3.可以对Import注解进行封装。
         *
         */

        //获取Bean
        User user = context.getBean(User.class);
        System.out.println(user);
    }

}

        如果注解的是@ComponentScan("com.apesource.config"),那么就是通过扫描指定包名com.apesource.config来注册bean。

        如果是@Import(User.class),那么直接将 User 类导入到 Spring 容器中,使得 User 成为一个 Bean。

        如果是@Import(UserConfig.class),那么就是导入导入 UserConfig 配置类,也会自动注册bean。

        如果是@EnableUser,那么就是用咱们刚才自定义的注解来完成注册bean。

        @EnableScheduling,启用 Spring 的任务调度功能,如果需要使用定时任务,可以启用此注解。

        @EnableCaching,启用 Spring 的缓存功能,允许使用注解来缓存方法的返回结果。

        这几种方式都可以去注册bean。

        演示示例(2)

        刚才只是演示了最基础的调用Config配置类,再提供两种可以调用的方法:

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //AnnotationMetadata注解
        //BeanDefinitionRegistry向spring容器中注入

        //1.获取user的definition对象
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();

        //2.通过beanDefinition属性信息,向spring容器中注册id为user的对象
        registry.registerBeanDefinition("user", beanDefinition);

    }
}

        这里的ImportBeanDefinitionRegistrar是Spring 提供的一个接口,允许你以编程方式在配置类被处理时向 Spring 容器中注册额外的 Bean 定义。

        registerBeanDefinitions是用于向 Spring 容器注册新的 Bean 定义。参数里的importingClassMetadata提供关于使用 @Import 导入当前类的注解元数据(注解信息),BeanDefinitionRegistry是一个 Bean 定义注册中心,允许通过这个接口向 Spring 容器注册新的 Bean 定义。

        然后是通过BeanDefinitionBuilder调用rootBeanDefinition(User.class).getBeanDefinition()获取到 User 类的 BeanDefinition 对象,描述了如何创建这个 User Bean。BeanDefinition 是 Spring 用来描述一个 Bean 的配置元数据对象,包括 Bean 的类型、作用范围、依赖关系等。

        最后通过通过beanDefinition属性信息将 User Bean定义注册到Spring容器中。

        还有一种可以解决写死配置类和组件的情况,用于动态选择要导入的配置类或组件:

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        // 目前字符串数组的内容是写死的,未来可以设置在配置文件中动态加载
        return new String[]{"com.apesource.domain.User","com.apesource.domain.Student"};
    }
}

        这里面实现了ImportSelector接口,用于动态选择要导入的配置类或组件,重写selectImports方法来实现返回一个字符串数组,返回值就为已经需要导入的类的名称,代表将"字符串数组"中的的类,全部导入spring容器。

        然后同样的道理在主项目里导子模块项目的包,进行调用测试:

@SpringBootApplication
//@Import(User.class)
//@Import(UserConfig.class)
//@Import(MyImportSelector.class)
@Import({MyImportBeanDefinitionRegistrar.class})
//@EnableCaching
//@EnableAsync

public class SpringbootEnableMain05Application {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableMain05Application.class, args);
        /**
         * Import4中用法:
         *  1. 导入Bean
         *  2. 导入配置类
         *  3. 导入ImportSelector的实现类
         *      查看ImportSelector接口源码
         *          String[] selectImports(AnnotationMetadata importingClassMetadata);
         *          代表将“字符串数组”中的的类,全部导入spring容器
         *  4. 导入ImportBeanDefinitionRegistrar实现类
         *
         */
//        User user = context.getBean(User.class);
//        System.out.println(user);
//
//        Student student = context.getBean(Student.class);
//        System.out.println(student);

        User user = (User) context.getBean("user");
        System.out.println(user);
    }

}

        这里多个一个注释,@EnableAsync是启用 Spring 的异步方法执行功能。

三、@EnableAutoConfiguration 注解和自动配置

        这个注释主要是用来帮助我们配置,首先它的自动配置是从主启动类中启动时通过注解@SpringBootApplication里的@EnableAutoConfiguration来实现:

//@SpringBootApplication 来标注一个主程序类
//说明这是一个Spring Boot应用
@SpringBootApplication
public class SpringbootApplication {
   public static void main(String[] args) {
     //以为是启动了一个方法,没想到启动了一个服务
      SpringApplication.run(SpringbootApplication.class, args);
   }
}

        当我们进去到 @SpringBootApplication 注解的源码当中,可以发现它是一个复合注解,它是由 @SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan 这三个注解组成。

1、@EnableAutoConfiguration的三个注解属性

        @SpringBootConfiguration:在@SpringBootConfiguration 源码中可以发现有 @Configuration,代表是一个配置类,说明主程序类也是一个配置类。

//@SpringBootConfiguration注解内部
//这里的 @Configuration,说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件;
@Configuration
public @interface SpringBootConfiguration {}
//里面的 @Component 这就说明,启动类本身也是Spring中的一个组件而已,负责启动应用
@Component
public @interface Configuration {}

        @ComponentScan:这个注解在Spring中很重要 ,它对应XML配置中的元素,刚才也讲到过,是自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中。

        @EnableAutoConfiguration:以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置 ;@EnableAutoConfiguration 告诉SpringBoot开启自动配置功能,这样自动配置才能生效;

@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
}
2、自动配置过程       

        @AutoConfigurationPackage:自动配置包,将指定的一个包下的所有组件导入到容器当中,在@AutoConfigurationPackage 注解中存在一个 @Import({Registrar.class}) 注解,自动配置包就是通过这个 Registrar 类的方法来完成的。

//AutoConfigurationPackage的子注解
//Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器 
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}

        由于@AutoConfigurationPackage 是在 @EnableAutoConfiguration 中,所以@AutoConfigurationPackage 是标识在主程序类上,所以 metadata为主程序类。

        因为在AutoConfigurationPackage注解中用import导入了Registrar类:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};
}

       所以在可以调用Registrar的方法,在Registrar中的 registerBeanDefinitions 方法,首先先获取到注解所标识的类,然后将这个类所在的包以及子包的名称放入到一个String数组当中,再将该String数组中的包的所有组件导入到容器当中。

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

        其中PackageImports(metadata)).getPackageNames()中的PackageImports的大致意思是,将包下所有的组件导入,获取包名,然后将包名存放在一个String数组中,Register的作用就是将包下的所有组件导入,这也是为什么Spring容器启动后会自动导入 主程序所在的包及其子包中的所有组件。

        @Import({AutoConfigurationImportSelector.class}):这个注解是@EnableAutoConfiguration下的注解,该注解的大概流程就是:将 spring-boot-autoconfigure-x.x.x.jar包中 META-INF/spring.factories文件中的。

    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }
getAutoConfigurationEntry(annotationMetadata); //给容器批量导入一些组件

        selectImports()是AutoConfigurationImportSelector的核心函数,其核心功能就是获取spring.factories中EnableAutoConfiguration所对应的Configuration类列表,由@EnableAutoConfiguration注解中的exclude/excludeName参数筛选一遍,再由AutoConfigurationImportFilter类所有实例筛选一遍,得到最终的用于Import的configuration和exclusion。

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

        其中this.getAutoConfigurationEntry(annotationMetadata),该方法就是批量导入一些组件,让我们进方法看一看:

        List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes)就是获取到所有需要导入到容器当中的组件,利用工厂加载。

        Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) 是得到所有组件从META-INF/spring.factories 位置来加载一个文件,默认扫描我们当前系统里面所有 META-INF/spring.factories位置的文件spring-boot-autoconfigure-2.6.7.jar包里边也有  META-INF/spring.factories。

        configurations中一共有133个组件:

        可以发现,spring-boot-autoconfigure-2.6.7.jar包里边也有 META-INF/spring.factories, 而 spring.factories 的 EnableAutoConfiguration,正好有133条,而configurations中正好也有 133 条,所以configurations中读取到的内容,正好是 spring.factories 的 EnableAutoConfiguration 中的内容。

        当 Spring Boot 应用启动时,它会扫描所有的 spring.factories 文件,查找 org.springframework.boot.autoconfigure.EnableAutoConfiguration 属性对应的类,并将这些类作为自动配置类进行加载。这样,配置类中的配置逻辑(如 Jedis Bean 的创建)会被自动执行,而无需显式在应用代码中引用或调用。

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=

        以上就是自动配置的全部过程。

四、自定义启动器

        通过刚才的自动配置我们可以发现我们可以自己配置一个spring.factories来完成自定义的启动器。

        首先我们要创建一个子模块项目用来定义自动配置的信息:

        RedisAutoconfiguration配置类:

@Configuration
// RedisProperties 是一个包含 Redis 配置参数的类
// 通常使用 @ConfigurationProperties 注解标注,定义了 Redis 的主机、端口等属性。
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoconfiguration {
    // 注入jedis
    @Bean
    // 由于上面使用了 @EnableConfigurationProperties 注解,Spring 会自动将 RedisProperties 实例注入到这个方法中。
    public Jedis jedis(RedisProperties redisProperties){
        // 主机/端口
        // 这个 Jedis 实例就是 Redis 的客户端,用于连接和操作 Redis 数据库。
        return new Jedis(redisProperties.getHost(),redisProperties.getPort());
    }
}

        加了@EnableConfigurationProperties(RedisProperties.class)注解用于对对 RedisProperties 配置属性类的支持。RedisProperties 是一个包含了 Redis 配置参数的类,通常包括主机名、端口等。通过这个注解,Spring Boot 可以将外部配置文件(如 application.properties 或 application.yml)中的相关配置映射到 RedisProperties 类的属性中,并自动实例化一个 RedisProperties 对象。

        其中RedisProperties的代码为:

@ConfigurationProperties(prefix = "spring.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;
    }
}

        这里面@ConfigurationProperties(prefix = "spring.redis")用于将外部配置文件,就比如 application.properties 或 application.yml中的配置参数绑定到这个类的字段上。

        剩下的host是Redis服务器的主机名,port是于存储 Redis 服务器的端口号。

        然后是创建了一个Jedis实例的方法, 由于使用了 @EnableConfigurationProperties(RedisProperties.class),Spring 会自动将 RedisProperties 实例作为参数注入到这个方法中。后面的getHost()和getPort()是刚才在RedisProperties中的主机名和端口号,获取到Jedis实例中并创建,从而连接到 Redis 服务器。

        其中根据springboot自动配置的过程,我们也手动的配置了一个spring.factories用来设置自动加载类是谁:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.apesource.RedisAutoconfiguration

        这里我们设置了RedisAutoconfiguration为自动加载类,当Spring Boot 会在启动时自动加载并应用这个配置类。

       

        然后我们在redis-spring-boot-starter子模块项目中导入刚才的子模块项目的坐标

<!--        引入自定义的redis-spring-boot-autoconfigure-->
        <dependency>
            <groupId>com.apesource</groupId>
            <artifactId>redis-spring-boot-autoconfigure</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

        最后我们在主项目中先导入redis-spring-boot-starter子模块项目的坐标

        <!--导入坐标-->
        <dependency>
            <groupId>com.apesource</groupId>
            <artifactId>redis-spring-boot-starter</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

        然后我们配置一下application.yml中的Redis 的连接参数的信息,用于刚才RedisProperties中的@ConfigurationProperties的获取:

spring:
  redis:
    port: 6060
    host: 127.0.0.1

        最后的最后我们去测试类主入口进行测试:

@SpringBootApplication
public class SpringbootStarter04Application {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootStarter04Application.class, args);
        Jedis bean1 = context.getBean(Jedis.class);
        System.out.println(bean1);
    }

}

        这就是自定义启动器的全部过程了。

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

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

相关文章

LLama 3 跨各种 GPU 类型的基准测试

2024 年 4 月 18 日&#xff0c;AI 社区对 Llama 3 70B 的发布表示欢迎&#xff0c;这是一款最先进的大型语言模型 &#xff08;LLM&#xff09;。该型号是 Llama 系列的下一代产品&#xff0c;支持广泛的用例。该模型 istelf 在广泛的行业平台上表现良好&#xff0c;并提供了新…

Eclipse的使用配置教程:必要设置、创建工程及可能遇到的问题(很详细,很全面,能解决90%的问题)

Eclipse的使用配置&#xff1a; Ⅰ、Eclipse 的必要配置&#xff1a;1、Eclipse 的安装&#xff1a;其一、将 Eclipse 解压或安装到没有中文且没有空格的路径下。其二、拿到 eclipse.exe 文件&#xff0c;傻瓜式安装即可; 2、设置工作空间(workspace)&#xff1a;其一、首次启动…

源头分析: 网络安全的分布式方法

部署网络可见性和网络检测和响应 (NDR) 解决方案来收集、查看和分析网络活动&#xff0c;以检测网络上的可疑和恶意活动。 这些解决方案中的大多数都是为了将数据从收集点 (或传感器) 移动到中央存储库进行分析而构建的。 这种方法有几个缺点&#xff0c;包括规模、性能、成本…

乌龟对对碰在线版

爆肝两天使用vue开发了一个在线版的乌龟对对碰小游戏之幸运对对碰。没有找到合适的乌龟素材&#xff0c;现在使用小兔子代替。 体验地址&#xff1a;幸运对对碰 | 乌龟对对碰小游戏 之前的python版本的乌龟对对碰&#xff1a;写文章-CSDN博客 乌龟对对碰-幸运对对碰

无需测试环境!如何利用测试脚手架隔离微服务,实现功能自动化

以下为作者观点&#xff1a; 想在不建立完整测试环境的情况下测试微服务&#xff1f; 想在将变更推送到主线分支之前完成测试&#xff1f; 这是我们在进行项目交付时经常遇到的难题。最近&#xff0c;当我们开始一个新的项目&#xff0c;为客户构建一个新的聚合平台时&#…

【springboot】springboot接口参数全局解密,解决request内容修改后如何重新设置回去的问题

文章目录 核心思路spring&servelt基础核心接口类body解密核心原理讲解get解密核心原理讲解 核心思路 拦截每次请求 所以要么在拦截器 要么在过滤器中做 (正常来说 其实只能在过滤器做)修改request中的参数把修改后的参数设置回去(难点&#xff09; spring&servelt基础…

RegFormer:用于大规模点云配准的高效投影感知Transformer网络

目录 一、导言 二、相关工作 1、点云配准工作 2、大规模点云配准 3、Transformer引入配准工作 三、RegFormer 1、柱面投影 2、特征提取Transformer 3、双射关联Transformer(BAT) 4、刚性变换估计 5、损失函数 四、实验 一、导言 该论文来自于ICCV2023&#xff08;…

如何搞定聊天记录找回?三款数据恢复工具分享

聊天记录丢了怎么办&#xff1f;别急&#xff0c;我这就带大家看看市面上比较火的三款数据恢复软件在恢复聊天记录方面的表现如何。首先&#xff0c;我们得知道&#xff0c;聊天记录这东西&#xff0c;一旦误删&#xff0c;那可是心急如焚啊。所以&#xff0c;选择一款靠谱的数…

场外个股期权可以分批建仓吗?

场外个股期权的优势是可以进行风险的精细化管理&#xff0c;但由于期权价格变化的非线性特性&#xff0c;盈利与标的行情走势的相关性不断变化&#xff0c;场外个股期权最便宜的可以用2-5万买到100万市值的股票持仓一个月&#xff0c;下面是整理得出的场外个股期权可以分批建仓…

SwiftUI 6.0(iOS 18)监听滚动视图视口中子视图可见性的极简方法

概览 在 SwiftUI 的应用开发中,我们有时需要监听滚动视图中子视图当前的显示状态:它们现在是被滚动到可见视口(Viewport)?或仍然是隐藏在“未知的黑暗”中呢? 在 SwiftUI 早期版本中为了得偿所愿,我们需要借助一些“取巧”的手段。不过,从 SwiftUI 6.0(iOS 18)开始情…

echarts柱状图使用自定义图片填充柱体

这是我的柱状图图片纹理的实现过程的流水账式记录 方法一&#xff1a; 在option.series里面&#xff0c;给每一项配置上图片路径 let Image2 new Image() Image2.src src/assets/image/BarChart-line.pngcolor: {image: Image,repeat: repeat } 出来的效果比较适合整体纹样…

[Linux]如何在Ubuntu中安装Docker,并且学习基本操作?

一、我们为什么需要Docker&#xff1f; 相信大家都遇到过部署一个应用时缺少依赖的情况&#xff0c;往往我们需要手动解决依赖问题&#xff0c;在解决了依赖问题以后&#xff0c;好不容易安装了这个应用&#xff0c;但是我们更换了计算机以后又需要重复上面步骤将这个软件再安装…

Unity(2022.3.38LTS) - Project Settings详细介绍不看你就亏大了(一)

目录 一. 简介 二. 详细介绍 1. Adaptive Performance(自适应性能) 2.音频 3. 编辑器 4. 图形 5. 输入管理器 6.包管理器 7.物理 8.2D物理 9.玩家 10.预设管理器 一. 简介 在 Unity 中&#xff0c;Project Settings&#xff08;项目设置&#xff09;页面是一个非常…

企业组网中MPLS和SD-WAN方案各有什么特点?

MPLS&#xff08;多协议标签交换&#xff09;和SD-WAN&#xff08;软件定义广域网&#xff09;是企业组网的两大关键技术方案&#xff0c;各自具备独特的特点和优势。 MPLS作为一种传统的专线技术&#xff0c;通过给数据包附加标签&#xff0c;实现了网络流量的高效转发。这种方…

教你如何安装并使用小熊猫c++

目录 前言 一、获取安装包 二、安装 1.打开安装包 2.选择语言 3.接受协议 4.安装场景 5.选择组件 6.选择位置 7.完成安装 三、如何使用 1.打开软件并选择主题 2.创建项目 3.新建空项目 4.创建源文件 5.测试C语言代码 6.编译 7.运行 总结 前言 已有的C/…

地震采集的观测系统

这张图表明&#xff0c;为什么在速度分析论文中&#xff0c;与CMP有关的数据都有CDP序号的影子——因为CDP序号是对一条测线上布置的观测系统对地下反射点的信号记录。换句话说&#xff0c;不同的CDP序号&#xff08;类似测井位置&#xff09;意味着不同的CMP道集。 几种论文中…

【Python快速入门和实践013】Python常用脚本-目标检测之按照类别数量划分数据集

一、功能介绍 这段代码实现了从给定的图像和标签文件夹中分割数据集为训练集、验证集和测试集的功能。以下是代码功能的总结&#xff1a; 创建目标文件夹结构&#xff1a; 在指定的根目录&#xff08;dataset_root&#xff09;下创建images和labels两个文件夹。在这两个文件夹下…

scoket通信 -- 网络字节序

include <arpa/inet.h> 考虑到不同语言不同库函数的参数可能不同&#xff0c;我这里以c语言的arpa/inet.h库中的函数为例. 网络字节序是什么 网络字节序&#xff08;Network Byte Order&#xff09;是指在网络通信中用于数据交换时所采用的字节序&#xff0c;它是大端…

log4j日志配置%X{TransId}

log4j日志配置文件中的%X{TransId}是怎么动态获取值的 在Log4j中&#xff0c;%X{TransId} 是用来从MDC&#xff08;Mapped Diagnostic Context&#xff09;中获取值的占位符。MDC 是 Log4j 提供的一种机制&#xff0c;用于在同一个线程的不同日志记录中传递上下文信息。通过 M…

centos ssh免密登录配置

ssh免密登录 centos 系统中&#xff0c;配置免密需要确保ssh配置文件免密登录权限打开了 sudo vim /etc/ssh/sshd_config 查看PubkeyAuthentication值为yes 修改之后&#xff0c;重启sshd sudo systemctl restart sshd免密配置 &#xff08;1&#xff09;生成秘钥文件 ssh-…