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

目录

什么是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模板类。

@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类

public class User {

}

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

        2.创建UserConfig配置类

@Configuration  //声明配置类
public class UserConfig {

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

       3.创建ClassCondition实现类

        必须实现Condition接口。

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。

@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方法。

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

测试成功

  ④ 导入 ImportBeanDefinitionRegistrar 实现类。

 创建实现ImportBeanDefinitionRegistrar 的实现类

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注解。

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

进入 @SpringBootApplication注解内部

@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的配置类; 

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

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

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

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

        1.@AutoConfigurationPackage :自动配置包

//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实体类

@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;
    }
}

@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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值