【Spring Boot-SpringBoot怎么实现自动配置】

目录

[什么是Spring Boot自动配置](#什么是Spring Boot自动配置)

自动配置中需要的重要注解

一.@Condition

二.@Enable

三.@EnableAutoConfiguration

实现一个自定义starter


什么是Spring Boot自动配置

SpringBoot的自动配置简单来说就是当spring容器启动后,一些配置类、bean对象就自动存入到了IOC容器中,不需要我们去手动装配,从而简化开发,省去了大部分繁琐的配置操作。

自动配置中需要的重要注解

我们接下来通过一步一步了解Spring Boot自动配置中的关键注解来明白SpringBoot怎么实现自动配置。

一.@Condition

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

增加条件判断注解的目的是为了使我们的程序动态的开启自动配置。

举例说明:

我们使用Spring Boot进行程序开发时,会需要在pom.xml配置文件中进行坐标导入,当你导入某个坐标后,你在程序中就能使用需要的类和方法。

这里以导入Redis坐标为例:

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-redis</artifactId>

</dependency>

当你导入坐标之后,你的程序就装配了该依赖库封装的两个模板类,帮助我们实现操作redis。这时你在启动类中getBean("redisTemplate"),就能获取到其中redisTemplate模板类。

java 复制代码
@SpringBootApplication
public class SpringbootCondition01Application {

    public static void main(String[] args) {

        //启动SpringBoot的应用,返回Spring的IOC容器
        ConfigurableApplicationContext context =  SpringApplication.run(SpringbootCondition01Application.class, args);
        //获取Bean,redisTemplate
         Object redisTemplate = context.getBean("redisTemplate");
         System.out.println(redisTemplate);
    }
}

启动后获取到

如果你没有添加starter-data-redis坐标,那么从容器中获取模板类则报空

那么在以上例子中其实就实现了通过@Conditon进行选择配置,如果存在该坐标就帮你在容器中注入模板类。

为了更好的理解,我们可以自己使用@Condition来选择注入一下。

假设我们现在需求在 Spring 的 IOC 容器中有一个 User 的 Bean,要求:导入Jedis坐标后,加载该Bean,没导入,则不加载。

1.创建一个User类

java 复制代码
public class User {

}

因为我们不能直接在User类上直接写注入注解,这样就不能选择,直接注入了User类。所以创建一个间接创建User类的配置类。

2.创建UserConfig配置类

java 复制代码
@Configuration  //声明配置类
public class UserConfig {

    @Bean //向容器中注入一个User类型的user对象
    @Conditional(value= ClassCondition.class) //判断,通过ClassCondition的实现类
    public User user(){
        return new User();
    }
}

3.创建ClassCondition实现类

必须实现Condition接口。

java 复制代码
public class ClassCondition implements Condition {
    /**
     *
     * @param context 上下文对象。用于获取环境,IOC容器,ClassLoader对象
     * @param metadata 注解元对象。 可以用于获取注解定义的属性值
     * @return
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //1.需求: 导入Jedis坐标后创建Bean
        //思路:判断redis.clients.jedis.Jedis.class文件是否存在

        boolean flag = true;
        try {
            Class<?> cls = Class.forName("redis.clients.jedis.Jedis");
        } catch (ClassNotFoundException e) {
            flag = false;
        }
        return flag;

    }
}

4.测试

在启动类,从容器中获取getBean("user")。

因为我们没有导入jedis坐标,所以报错。

二.@Enable

SpringBoot中提供了很多Enable开头的注解,这些注解都是用于动态启用某些功能的。

例如@EnableAsync注解可以使Bean在spring应用中支持异步功能,@EnableWebMvc注解引入了MVC框架在Spring应用中需要用到的所有bean。

java 复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync { 
    ...
}


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

可以看到其底层原理是使用@Import注 解导入一些配置类,实现Bean的动态加载。

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

@Import提供4种用法:

① 导入Bean

② 导入配置类

③ 导入 ImportSelector 实现类。一般用于加载配置文件中的类

④ 导入 ImportBeanDefinitionRegistrar 实现类。

举例说明:

① 导入Bean

简单创建一个User类并且创建User的配置类,使用@Import将User类导入当前容器。这里还是选择在启动类中导入并且getBean()测试。

② 导入配置类

其实就是①的基础上,可以导入User的配置类。也成功getBean

③ 导入 ImportSelector 实现类。一般用于加载配置文件中的类

创建一个实现ImportSelector接口的实现类,并且需要重写selectImports方法。

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

测试成功

④ 导入 ImportBeanDefinitionRegistrar 实现类。

创建实现ImportBeanDefinitionRegistrar 的实现类

java 复制代码
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        
        //1.获取user的definition对象
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();

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

    }
}

测试成功

三.@EnableAutoConfiguration

我们观察启动类,可以发现启动类上都有@SpringBootApplication注解。

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

进入 @SpringBootApplication注解内部

java 复制代码
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
// ......
}

其中有

1.@ComponentScan

这个注解在Spring中很重要 ,它对应XML配置中的元素。

作用:自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中

2.@SpringBootConfiguration

作用:SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类;

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

3.@EnableAutoConfiguration开启自动配置功能

作用:@EnableAutoConfiguration 告诉SpringBoot开启自动配置功能,这样自动配置才能生效;

在@EnableAutoConfiguration注解内部又包含一些重要注解:

1.@AutoConfigurationPackage :自动配置包

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

2.@Import({AutoConfigurationImportSelector.class})

作用:自动配置导入选择器,给容器中导入一些组件

看到这里我们就能大概知道,SpringBoot自动配置重要的一些地方

在启动类时通过@SpringBootApplication注解就开启了Spring Boot自动配置了一些库进入你的程序。而在该配置类内部,

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

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

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

实现一个自定义starter

本文一直以整合Redsi为例,这里也实现一个与Redis相关的自定义启动器。

需求: 自定义redis-starter,要求当导入redis坐标时,SpringBoot自动创建Jedis的Bean。

这里参考考导入mybatis坐标时,我们导入了一个mybatis-spring-boot-starter的启动器。这样在我们的

就能找到mybatis相关的jar包,不仅仅导入了mybatis-spring-boot-starter,还有mybatis-spring-boot-autoconfigure。

实现步骤:

1.创建一个基本的SpringBoot项目,在项目springboot_starter中先完成基本配置。

当你在项目的pom.xml文件中导入坐标时,就开启了关于该坐标的相关自动配置。那么我们在实现自定义启动器时,也可以使用坐标。导入的坐标就是我们自定义的有关内容,我们观察pom.xml文件就能发现其实每一个项目都在其中有自己的坐标。那么我们就可以把自定义的redis-spring-boot-starter模块的坐标写入redis_starter中。

2.创建redis-spring-boot-starter模块,依赖redis-spring-boot-autoconfigure的模块

3.在redis-spring-boot-autoconfigure模块中初始化Jedis的Bean,并定义META-INF/spring.factories文件

在该模块中创建RedisAutoconfiguration配置类,注入jedis对象

为了动态的获取,创建一个application.yml在springboot_starter获取端口号、ip

在redis-spring-boot-autoconfigure中创建一个Redis身体

java 复制代码
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
    private String host="localhost";
    private int port=6379;

    public String getHost() {
        return host;
    }

    public int getPort() {
        return port;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public void setPort(int port) {
        this.port = port;
    }
}
java 复制代码
@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoconfiguration {
    //注入jedis
    @Bean
    public Jedis jedis(RedisProperties redisProperties){
        return new Jedis(redisProperties.getHost(),redisProperties.getPort());
    }
}

怎样加载到这个类呢,就需要MEAT-INF文件

4.在测试模块中引入自定义的redis-starter依赖,测试获取Jedis的Bean,操作redis。

获取到jedis对象证明成功实现了自定义启动时自动配置jedis.

相关推荐
kinlon.liu6 分钟前
零信任安全架构--持续验证
java·安全·安全架构·mfa·持续验证
王哲晓27 分钟前
Linux通过yum安装Docker
java·linux·docker
java66666888831 分钟前
如何在Java中实现高效的对象映射:Dozer与MapStruct的比较与优化
java·开发语言
Violet永存32 分钟前
源码分析:LinkedList
java·开发语言
执键行天涯32 分钟前
【经验帖】JAVA中同方法,两次调用Mybatis,一次更新,一次查询,同一事务,第一次修改对第二次的可见性如何
java·数据库·mybatis
Adolf_19931 小时前
Flask-JWT-Extended登录验证, 不用自定义
后端·python·flask
Jarlen1 小时前
将本地离线Jar包上传到Maven远程私库上,供项目编译使用
java·maven·jar
蓑 羽1 小时前
力扣438 找到字符串中所有字母异位词 Java版本
java·算法·leetcode
叫我:松哥1 小时前
基于Python flask的医院管理学院,医生能够增加/删除/修改/删除病人的数据信息,有可视化分析
javascript·后端·python·mysql·信息可视化·flask·bootstrap
Reese_Cool1 小时前
【C语言二级考试】循环结构设计
android·java·c语言·开发语言