SpringBoot自动配置原理

SpringBoot的自动配置就是当Spring容器启动后,一些自动配置类(只是自动配置类,并不是当前的组件配置到IOC容器中,自动配置类通过@Conditional注解来按需配置)就自动装配的IOC容器中,不需要我们手动注入,从而简化了开发,省去繁琐的配置。

一、Condition

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

具体实例:

在Spring的IOC容器中有一个User的Bean

步骤:

1.导入Jedis坐标后,加载该Bean,没有导入,则不加载

     自定义类ClassCondition实现Condition接口:

package com.ly.springboot_conditional.condition;

import com.ly.springboot_conditional.domain.User;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

import java.util.Map;
/**
 *
 * @param //context 上下文对象。用于获取环境,IOC容器,ClassLoader对象
 * @param //metadata 注解元对象。 可以用于获取注解定义的属性值
 * @return
 */
public class ClassCondation implements Condition {
    @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;*/
        //需求:导入通过注解属性value指定坐标后创建Bean
        //获取注解属性值 value
        Map<String,Object> map=metadata.getAnnotationAttributes(ConditionOnClass.class.getName());
        System.out.println(map);
        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;
    }

}

注入User类型的bean:

package com.ly.springboot_conditional.config;

import com.ly.springboot_conditional.condition.ClassCondation;
import com.ly.springboot_conditional.condition.ConditionOnClass;
import com.ly.springboot_conditional.domain.User;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;


@Configuration
public class UserConfig {
    //@Conditional中的ClassCondition.class的matches方法,返回true执行以下代码,否则反之
    @Bean
    //    @Conditional(ClassCondition.class)
//    @ConditionOnClass(value="redis.clients.jedis.Jedis")
    @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();
    }
}


pom文件中引入Jedis坐标测试类

package com.ly.springboot_conditional;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class SpringbootConditionalApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context=SpringApplication.run(SpringbootConditionalApplication.class, args);

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

}

如果没引入Jedis成功会报异常;

导入Jedies坐标之后

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

实例2:

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

1.将实现类的判断定义为动态的。判断哪个字节码文件存在可以动态指定

步骤:

  • 不使用@Conditional(ClassCondition.class)注解
  • 自定义注解@ConditionOnClass,因为它和@Conditional注解功能一致,
  • 编写ClassCondition中的matches方法

自定义注解ConditionOnClass:

package com.ly.springboot_conditional.condition;

import org.springframework.context.annotation.Conditional;

import java.lang.annotation.*;

@Target({ElementType.TYPE,ElementType.METHOD})//可修饰在类与方法上
@Retention(RetentionPolicy.RUNTIME)//注解生效节点runtime
@Documented//生成文档
@Conditional(value = ClassCondation.class)
public @interface ConditionOnClass {
    String[] value();//设置此注解的属性redis.clients.jedis.Jedis
}

 自定义类ClassCondition的matches方法判断逻辑,根据自定义注解指定的value值来动态判断是否可以加载某个bean:

package com.ly.springboot_conditional.condition;

import com.ly.springboot_conditional.domain.User;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

import java.util.Map;



/**
 *
 * @param //context 上下文对象。用于获取环境,IOC容器,ClassLoader对象
 * @param //metadata 注解元对象。 可以用于获取注解定义的属性值
 * @return
 */
public class ClassCondation implements Condition {
    @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;*/
        //需求:导入通过注解属性value指定坐标后创建Bean
        //获取注解属性值 value
        Map<String,Object> map=metadata.getAnnotationAttributes(ConditionOnClass.class.getName());
        System.out.println(map);
        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;
    }

}

测试是否引入fast.json的坐标来加载user的bean:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fast.json</artifactId>
    <version>1.2.62</version>
</dependency>

总结:

自定义条件: 

  1. 定义条件类:自定义类实现Condition接口,重写matches方法中进行逻辑及判断,返回boolean值。                                                                                                                      matches的两个参数:                                                                                                        ·context:上下文对象,可以获取属性值,获取类加载器,获取BeanFactory等。          ·metadata:元数据对象,用于获取注解属性。
  2. 判断条件:在初始化Bean时,实验@Conditional(条件类.class)注解                                 SpringBoot提供的常用注解:                                                                                             一下注解在springboot-autoconfiguration的condition包下                                                 · ConditionalOnProperty:判断配置文件中是否有对应属性和值才初始化Bean                 · ConditionalOnClass:判断环境中是否有对应字节码文件才初始化Bean                       · ConditionOnMissingBean:判断环境中有没有对应的bean才初始化Bean                       · ConditionOnBean:判断环境中有对应Bean才初始化Bean 

 二、@Enable注解

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

@Import 注解
@Enable底层依赖于 @Import 注解导入一些类,使用 @Import 导入的类会被 Spring 加载到 IOC 容器中。
而 @Import 提供 4 中用法:
导入Bean
导入配置类
导入 ImportSelector 实现类。一般用于加载配置文件中的类
导入 ImportBeanDefinitionRegistrar 实现类。

三、.@EnableAutoConfiguration 注解

1、当SpringBoot应用启动的时候,就从主方法里面进行启动的它主要加载了@SpringBootApplication注解主配置类。

@SpringBootApplication
public class SpringbootCondition01Application {
 
    public static void main(String[] args) {
        SpringApplication.run(SpringbootCondition01Application.class, args);
    }

2.@SpringBootApplication注解的内部当中,可以发现它是一个复合注解,它由@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan这三个注解组成

(1)@ComponentScan

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

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

(2)@SpringBootConfiguration

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

(3)@EnableAutoConfiguration

它主要利用了一个AutoConfigurationImportSelector选择器给Spring容器中来导入一些组件。以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置 ;@EnableAutoConfiguration 告诉SpringBoot开启自动配置功能,这样自动配置才能生效;

@AutoConfigurationPackage :自动配置包
作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器
@Import({AutoConfigurationImportSelector.class}) :给容器导入组件 ;
AutoConfigurationImportSelector :自动配置导入选择器,给容器中导入一些组件
进入 AutoConfigurationImportSelector类的内部。

扫描所有jar包类路径下META-INF/spring.factories,把扫描到的这些文件的内容包装成properties对象,从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后将他们添加到容器中。从META-INF/spring.factories 位置来加载一个文件默认扫描我们当前系统里面所有 META-INF/spring.factories位置的文件

总结原理:

  • @EnableAutoConfiguration 注解内部使用 @Import(AutoConfigurationImportSelector.class) 来加载配置类。
  • 配置文件位置:META-INF/spring.factories,该配置文件中定义了大量的配置类,当 SpringBoot应用启动时,会自动加载这些配置类,初始化Bean并不是所有的Bean都会被初始化,在配置类中使用Condition来加载满足条件的Bean

 四.自定义启动器

自定义redis-starter ,要求当导入 redis 坐标时, SpringBoot 自动创建 Jedis 的 Bean
实现步骤:
1、创建redis_spring_boot_autoconfigure模块

2、创建自动加载和属性配置类

package com.ly;

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
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
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoconfiguration {
    //注入jedis
    @Bean
    @ConditionalOnMissingBean(Jedis.class)
    public Jedis jedis(RedisProperties redisProperties){
        return new Jedis(redisProperties.getHost(),redisProperties.getPort());
    }
}

 

package com.ly;

import org.springframework.boot.context.properties.ConfigurationProperties;


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

3、定义META INF/spring.factories文件,引入jedis坐标

4、创建springboot_starter模块,依赖springboot_redis_autoconfigure的模块,在pom.xml中引入坐标

5、在springboot_starter启动类测试 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值