未央花博客

SpringBoot高级原理分析(未完)

SpringBoot高级原理分析

一、SpringBoot自动配置--注解说明

1.1 Condition条件判断

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

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

  1. 导入Jedis坐标后,加载该Bean,没导入,则不加载。
  2. 将类的判断定义为动态的。判断哪个字节码文件存在可以动态指定。
1.1.2 Condition案例2

​ 问题:现在的 Class.forName("redis.clients.jedis.Jedis") 这个是写死的,是否可以动态的加载呢?

1.1.3 @Condition*注解小结

​ springboot提供的,作为条件加载Bean

1.2 SpringBoot切换内置服务器

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <groupId>org.springframework.boot</groupId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <artifactId>spring-boot-starter-jetty</artifactId>
    <groupId>org.springframework.boot</groupId>
</dependency>

1.3 @Enable*注解

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

问题:SpringBoot 工程是否可以直接获取jar包中定义的Bean?

1.3.1、创建两个模块

一个是springboot-enable,一个是springboot-enable-other。然后在springboot-enable中导入springboot-enable-other坐标,实现导入其模块的Bean

1.3.2、创建实体类与配置类
// User.java
public class User {
}
// UserConfig.java
@Configuration
public class UserConfig {

    @Bean
    public User user(){
        return new User();
    }
}
1.3.3 导入jar包中Bean的三种方式

springboot-enable的启动类

/**
 * ComponentScan:扫描包,当前包及其子包
 * com.itheima.enable
 * com.itheima.enableother.config
 *
 * 方式1.使用ComponentScan,扫描com.itheima.enableother.config
 * 方式2.使用import注解,导入配置类
 * 方式3.使用Enable注解
 */

@SpringBootApplication
//@ComponentScan("com.itheima.enableother.config")
//@Import(UserConfig.class)
@EnableUser
public class SpringbootEnableApplication {

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

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

}

springboot导入第三方Bean

@SpringBootApplication ===>
@EnableAutoConfiguration ===>
@Import(AutoConfigurationImportSelector.class) ===>
AutoConfigurationImportSelector.java类 ===>
里面的getCandidateConfigurations()方法中的 META-INF/spring.factories 配置文件

1.4 @Import注解

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

1.4.1 导入Bean

SpringbootEnableApplication.java

@SpringBootApplication
@Import(User.class)
public class SpringbootEnableApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);
// 这样导入不了的原因在于,我们是通过Bean的名称"user"来获取对象,而导入的这个User.class不一定叫"user"这个名字,所以需要修改启动类,通过类的类型来获取。
        Object user = context.getBean("user");
        System.out.println(user); // 这样导入不了
        
        User user = context.getBean(User.class);
        System.out.println(user);
    }

}

如果想要获取Bean的名称,那么可以使用context.getBeansOfType

//这是得到Import导入Bean的名称
Map<String, User> map = context.getBeansOfType(User.class);
// {com.itheima.enableother.domain.User=com.itheima.enableother.domain.User@c7045b9}
System.out.println(map);
//Import导入Bean的名称 = "com.itheima.enableother.domain.User"
Object bean = context.getBean("com.itheima.enableother.domain.User");
System.out.println(bean);
1.4.2 导入配置类

​ 配置类就是前面的UserConfig.java类

1.4.3 实现ImportSelector接口

MyImportSelector类

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        //这里也可以通过配置文件,然后转换为String[]数组返回
        return new String[]{"com.itheima.enableother.domain.User",
                "com.itheima.enableother.domain.Role"};
    }
}

SpringbootEnableApplication.java

@SpringBootApplication
@Import(MyImportSelector.class)
public class SpringbootEnableApplication {
    ......
}
1.4.4 实现ImportBeanDefinitionRegistrar接口

MyImportBeanDefinitionRegistrar类

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {


    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
        registry.registerBeanDefinition("user", beanDefinition);
    }
}
@SpringBootApplication
@Import(MyImportBeanDefinitionRegistrar.class)
public class SpringbootEnableApplication {
    ......
}

1.5 @EnableAutoConfiguration

@SpringBootApplication中的@EnableAutoConfiguration注解,也是通过@Import({AutoConfigurationImportSelector.class})来实现类的加载,那么说明Springboot中也是使用第三种方法导入类。

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

二、SpringBoot自动配置--自定义Starter

2.1 MyBatis加载过程

2.2、自定义Starter需求

自定义redis-starter。要求当导入redis坐标时,SpringBoot自动创建Jedis的Bean。
步骤:
    创建 redis-spring-boot-autoconfigure 模块
    创建 redis-spring-boot-starter 模块,依赖 redis-spring-bootautoconfigure的模块
    在 redis-spring-boot-autoconfigure 模块中初始化 Jedis 的 Bean。并定义META-INF/spring.factories 文件
    在测试模块中引入自定义的 redis-starter 依赖,测试获取 Jedis 的Bean,操作 redis。

2.3 创建两个模块

​ redis-spring-boot-autoconfigure
​ redis-spring-boot-starter

2.4 创建相关配置类

2.5 创建META-INF/spring.factories

​ 在autoconfigure模块的resources目录下创建

spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.itheima.autoconfigure.RedisAutoConfigure

2.6 利用前面的enable模块

在enable模块导入starter模块坐标,然后在SpringbootEnableApplication类中测试

@SpringBootApplication
public class SpringbootEnableApplication {

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

        jedis.set("mykey", "itheima");
        String value = jedis.get("mykey");
        System.out.println(value);

    }
//    这个是验证@ConditionalOnMissBean注解
    /*@Bean
    public Jedis jedis(){
        return new Jedis("localhost", 6379);
    }*/

}

然后测试成功

2.7 autoconfigure模块的内容

2.8 总结自定义的流程

​ 1. enable模块导入自定义stater模块坐标,在SpringbootEnableApplication类中测试 ===> 在自中定义stater模块导入自定义autoconfigure模块坐标 ===> 然后在自定义autoconfigure模块中创建RedisConfigure类和RedisProperties类,最后在resources目录下建立META-INF/spring.factories配置文件

​ 2. enable模块的@SpringBootApplication注解 ==> @EnableAutoConfiguration注解 ==> @Import(AutoConfigurationImportSelector.class)注解 ==> AutoConfigurationImportSelector.java类,这里面的selectImports()方法,能够得到autoconfigure模块中的spring.factories中的

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.itheima.autoconfigure.RedisAutoConfigure

值,然后RedisAutoConfigure,java就被加入spring容器管理,对应的Jedis也被加入spring容器

三、SpringBoot监听机制(有时间就补充)

如果有时间后面就继续来补充,可能好久都没时间::quyin:cry::

3.1 JAVA的监听机制

SpringBoot 的监听机制,其实是对Java提供的事件监听机制的封装。
Java中的事件监听机制定义了以下几个角色:
①事件:Event,继承 java.util.EventObject 类的对象
②事件源:Source ,任意对象Object
③监听器:Listener,实现 java.util.EventListener 接口的对象

3.2 Springboot监听器

SpringBoot 在项目启动时,会对几个监听器进行回调,我们可以实现这些监听器接口,在项目启动时完成一些操作。

一共有四种实现方法:

spring只加载下面两个

四、SpringBoot监控(有时间就补充)

五、SpringBoot运行流程分析

当前页面是本站的「Google AMP」版。查看和发表评论请点击:完整版 »