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

目录

一、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.mfbz.cn/a/871690.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

git 学习--GitHub Gitee码云 GitLab

1 集中式和分布式的区别 1.1 集中式 集中式VCS必须有一台电脑作为服务器&#xff0c;每台电脑都把代码提交到服务器上&#xff0c;再从服务器下载代码。如果网络出现问题或服务器宕机&#xff0c;系统就不能使用了。 1.2 分布式 分布式VCS没有中央服务器&#xff0c;每台电脑…

Python编码系列—Python SQL与NoSQL数据库交互:深入探索与实战应用

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

预警先行,弯道哨兵让行车更安全

预警先行&#xff0c;弯道哨兵让行车更安全”这句话深刻体现了现代交通安全理念中预防为主、科技赋能的重要性。在道路交通中&#xff0c;尤其是复杂多变的弯道区域&#xff0c;交通事故的发生率往往较高&#xff0c;因此&#xff0c;采取有效的预警措施和引入先进的交通辅助设…

怎么管控终端电脑上的移动端口

管控终端电脑上的移动端口&#xff0c;尤其是USB等移动端口&#xff0c;是确保企业数据安全和提升网络管理效率的重要手段。 一、使用注册表编辑器禁用USB端口&#xff08;适用于Windows系统&#xff09; 打开注册表编辑器&#xff1a; 同时按下“WinR”组合键&#xff0c;打…

SEO优化:如何优化自己的文章,解决搜索引擎不收录的问题

可以使用bing的URL检查&#xff0c;来检查自己的文章是不是负荷收录准测&#xff0c;如果页面有严重的错误&#xff0c;搜索引擎是不会进行收录的&#xff0c;而且还会判定文章为低质量文章&#xff01; 检查是否有问题。下面的页面就是有问题&#xff0c;当然如果是误报你也可…

Java并发类API——CompletionService

CompletionService 是 Java 中 java.util.concurrent 包的一部分&#xff0c;用于管理并发任务的执行&#xff0c;并以完成的顺序提供结果。它结合了线程池和阻塞队列的功能&#xff0c;用于提交任务并按照任务完成的顺序来检索结果&#xff0c;而不是按照任务提交的顺序。 接…

NC拼接所有的字符串产生字典序最小的字符串

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 描述 给定一个长度…

单例模式(singleton)- python实现

通俗示例 想象一下&#xff0c;一个国家只有一个国王。不管你在哪里&#xff0c;提到这个国家的国王&#xff0c;大家都能知道是指同一个人。在程序设计中&#xff0c;单例模式就像是这样的国王&#xff0c;一个类只有一个实例&#xff0c;无论你多少次请求这个类的实例&#…

基于Hadoop的汽车大数据分析系统设计与实现【爬虫、数据预处理、MapReduce、echarts、Flask】

文章目录 有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主项目介绍爬虫数据概览HIve表设计Cars Database Tables1. cars_data2. annual_sales_volume3. brand_sales_volume4. city_sales_volume5. sales_volume_by_year_and_brand6. sales_distribu…

Midjourney进阶-反推与优化提示词(案例实操)

​ Midjourney中提示词是关键&#xff0c;掌握提示词的技巧直接决定了生成作品的质量。 当你看到一张不错的图片&#xff0c;想要让Midjourney生成类似的图片&#xff0c;却不知道如何描述画面撰写提示词&#xff0c;这时候Midjourney的/describe指令&#xff0c;正是帮助你推…

嵌入式AI快速入门课程-K510篇 (第四篇 AI概念及理论知识)

第四篇 AI概念及理论知识 文章目录 第四篇 AI概念及理论知识1.人工智能与机器学习1.1 机器学习1.2 模型和拟合1.3 线性回归模型1.3.1 实现简单线性回归1.3.2 简单线性回归代码解析1.3.3 Sklearn实现房价预测模型1.3.4 Sklearn房价预测代码解析 2.深度学习及神经网络2.1 深度学习…

Java | Leetcode Java题解之第355题设计推特

题目&#xff1a; 题解&#xff1a; class Twitter {private class Node {// 哈希表存储关注人的 IdSet<Integer> followee;// 用链表存储 tweetIdLinkedList<Integer> tweet;Node() {followee new HashSet<Integer>();tweet new LinkedList<Integer&g…

多线程并发服务器

多线程并发服务器 服务端 #include <stdio.h> #include <string.h> #include <sys/errno.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <stdlib.h> #include <ctype.h> #include <p…

Nofollow不好吗?Follow和Nofollow的区别

Follow和Nofollow的区别 “follow”和“nofollow”是HTML中的两种属性&#xff0c;它们通常用于<a>标签&#xff0c;即超链接。这两种属性对搜索引擎优化&#xff08;SEO&#xff09;有重要的影响。 1.Follow链接&#xff08;Dofollow&#xff09;: 这是默认的链接属性…

带你玩转小程序推广,实现短链接一键跳转

不知道各位有没有想过&#xff0c;短链接直接跳转到微信小程序到底该怎么操作呢&#xff1f;掌握这个小技能&#xff0c;能让你的推广效率大幅提升哦。今天就给大家分享一个全新方法&#xff0c;教你如何从短链接直接跳转到微信小程序&#xff0c;实现高效的一键式跨越。 一、…

如何开发出一款优秀的软件

一段时间以来&#xff0c;笔者都想写一篇关于如何开发一款优秀软件的文章&#xff0c;关于软件的质量&#xff0c;笔者一直很有想法&#xff0c;自2014年从一家很优秀的软件公司出来后&#xff0c;笔者发现很多软件都存在这样&#xff0c;那样的问题&#xff0c;最终相关企业也…

docker连接宿主机redis,提示Connection refused

目录 一、测试环境 二、问题现象 三、问题总结 一、测试环境 centos 7 redis-5.0.14 docker-26.0.1 二、问题现象 服务器重启后docker连接宿主机redis&#xff0c;提示Connection refused Reconnecting, last destination was /172.25.xxx.x:6379 …

[CTF]-Reverse:纯逻辑分析题型综合解析

C语言&#xff1a; 字符串爆破&#xff1a; 例题&#xff08;BUUCTF SimpleRev&#xff09;&#xff1a; 查壳 看ida 这里的中心就是两个字符串和一个计算式子&#xff0c;textkillshadow和str2adsfkndcls&#xff0c;计算式子str2[v2] (v1 - 39 - key[v3 % v5] 97) % 26 …

汽车的UDS诊断02

UDS的不同服务: 1)物理寻址和功能寻址 can总线上往往有多个ECU,诊断设备可以和某个ECU通信,也可以和多个ECU通信,通过物理寻址和功能寻址来解决这个问题,只针对请求报文: 物理寻址:就是诊断仪与ECU之间点对点通信 功能寻址:就是诊断仪与多个ECU之间一对多信 我们的…

Github 2024-08-22 Go开源项目日报 Top10

根据Github Trendings的统计,今日(2024-08-22统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Go项目10TypeScript项目1精选Go框架、库和软件列表 创建周期:3700 天开发语言:Go协议类型:MIT LicenseStar数量:127377 个Fork数量:11751 …