1.1.6、SpringBoot高级原理分析
一、SpringBoot自动配置
Condition是在SpringBoot4.0增加的条件判断功能,通过这个功能可以实现选择性的创建Bean操作
案例:在Spring的IOC容器中有一个User的Bean,现要求:
导入Jedis坐标后,加载该Bean,没倒入,则不加载。
将类的判断定义为动态的。判断那个字节码文件是否存在可以动态指定
代码如下:
// pom.xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- <dependency>--> <!-- <groupId>redis.clients</groupId>--> <!-- <artifactId>jedis</artifactId>--> <!-- </dependency>--> </dependencies>备注:注释坐标为测试坐标
// 启动类 @SpringBootApplication public class LearnApplication { public static void main(String[] args) { // 启动SpringBoot应用,返回Spring的IOC容器 ConfigurableApplicationContext context = SpringApplication.run(LearnApplication.class, args); // // 捕获Bean,redisTemplate,输出地址 // Object redisTemplate = context.getBean("redisTemplate"); // System.out.println(redisTemplate); // 捕获UserBean,输出地址 Object userBean = context.getBean("user"); System.out.println(userBean); } }备注:获取IOC容器,查看时候有对应的Bean
// User类 public class User { }备注:用来被Bean注入的,表示某些功能
// Bean注入类 @Configuration public class UserConfig { @Bean // @Condition(ClassCondition.class) @ConditionOnClass("redis.clients.jedis.Jedis") public User user() { return new User(); } }备注:注释的内容是原测试内容,下面的是动态内容
// Condition实现类 public class ClassCondition implements Condition { /** * @param context 上下文对象,用于获取环境,IOC容器,ClassLoader对象 * @param metadata 注解元对象,可以用于获取注解定义的属性值 */ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // // 获取环境 // Environment environment = context.getEnvironment(); // environment.getProperty() // 获取配置文件的属性 // // 导入Jedis坐标后,返回true,创建Bean // try { // // 获取某个类的字节码文件,获取到就有,获取不到就没有 // Class<?> aClass = Class.forName("redis.clients.jedis.Jedis"); // } catch (ClassNotFoundException e) { // return false; // } // return true; // 导入指定坐标后,返回true,创建Bean // 获取注解属性值 Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionOnClass.class.getName()); String[] value = (String[]) attributes.get("value"); try { for (String className : value) { Class<?> aClass = Class.forName(className); } } catch (ClassNotFoundException e) { return false; } return true; } }备注:相关参数的描述,以及想关功能操作
// 自定义注解 @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(ClassCondition.class) public @interface ConditionOnClass { String[] value(); }备注:自定义注解,实现动态功能
SpringBoot内部配置了很多Condition,包地址:org.springframework.boot:spring-boot-autoconfigure,其中有一个文件夹condition,专门编写Condition
自定义的条件中,可以直接使用autoconfigure包中分装好的Condition去操作
总结
自定义条件:
定义条件类:自定义类实现Condition接口,重写matches方法,在matches方法中进行逻辑判断,返回boolean值
备注:mathces的两个参数:
contrxt:上下文对象,可以获取属性值,获取类加载器(IOC容器),获取BeanFactory等
metadata:元数据对象,用于获取注解属性
判断条件:在初始化Bean时,使用@Condition(条件类.class)注解
SpringBoot提供的常用条件注解
ConditionOnProperty:判断配置文件中是否有对应属性和值才初始化Bean
ConditionOnClass:判断环境中是否有对应的字节码文件才初始化Bean
ConditionOnMissingBean:判断环境中有没有对应的Bean才初始化Bean
等等,还有好多SpringBoot配置好的
二、切换内置Web服务器
SpringBoot的web环境中默认使用tomcat作为内置服务器,其实SpringBoot提供了4种内置服务器供我们选择,我们可以很方便的进行切换
查看内置服务器的提供种类,包地址:org.springframework.boot:spring-boot-autoconfigure,其中有一个包名叫做web/embedded的包中,下面四个就是内置的服务
查看包中的源代码,就发现如何修切换嵌服务器——导入不同的Web服务器坐标即可
方法:
备注:引入web坐标,排除tomcat依赖,引入jetty等想要的服务器依赖即可,实现方法:参考上面的Condition注解
三、Enable*注解
SpringBoot中提供了很多Enable开头的注解,这些注解都是用于动态启用某些功能的。而期底层原理是使用@import注解导入一些配置类,实现Bean的动态加载
@SpringBootApplication包扫描的范围是当前引导类所在包及其子包
如何扫描其他位置的包
使用@ComponentScan("地址")可以指定扫描地址
使用@Import(文件.class)注解指定引入某个文件,加载类。这些类会被Spring创建,并放入IOC容器中
可以对@Import注解进行封装,封装成一个注解,直接引用(这就是@Enable*注解的由来)
四、Import注解
@Enabel*底层依赖于@Import注解导入一些类,使用@Import注解导入的类会被Spring容器加载到IOC容器中。而@Import提供4种用法:
导入Bean——直接导入类
导入配置类——导入带有@Configuration注解的文件
导入ImportSelector实现类。一般用于加载配置文件中的类——导入实现类文件即可
导入ImportBeanDefinitionRegistrar实现类——导入实现类文件即可
备注:4的实现有点复杂,在这里附加代码
五、@EnableAutoConfiguration注解
注解内部使用@Import(AutoConfigurationImportSelector.class)来加载配置类
配置文件位置:META-INF/spring.factories,该配置文件中定义了大量的配置类,当SpringBoot启动时,会自动加载这些配置类,初始化Bean
并不是所有的Bean都会被初始化,在配置类中使用@Condition来加载满足条件的Bean
六、自定义starter步骤(强化上面内容)
需求:自定义redis-starter。要求当导入redis坐标时,SpringBoot自动创建Jedis的Bean
实现步骤:
创建redis-spring-boot-autoconfigure模块
创建redis-spring-boot-starter模块,依赖redis-spring-boot-autoconfigure的模块
在redis-spring-boot-autoconfigure模块中初始化Jedis的Bean。并定义META_INF/spring.factories文件
在测试模块中引入自定义的redis-starter依赖测试获取Jedis的Bean,操作redis
实现代码:
redis-spring-boot-autoconfigure模块
redis-spring-boot-starter模块
测试包:
引入redis-spring-boot-starter模块坐标即可,余下操作正常即可
七、SpringBoot事件监听机制
SpringBoot的监听机制,其实是对Java提供的事件监听的封装
Java中的事件监听机制定义了以下几个角色
事件:Event,继承java.util.EventObject类的借口
事件源:Source,任意对象Object
监听器:Listener,实现java.util.EventListener接口的对象
SpringBoot在项目启动时,会对几个监听器进行回调,我们可以实现这些监听器接口,在项目启动时完成一些操作
ApplicationContextInitializer:需要在META-INF/spring.factories中配置,检测资源是否存在
备注:接口文件路径=实现类文件路径
SpringApplicationRunListener:需要在META-INF/spring.factories中配置
备注:接口文件路径=实现类文件路径
CommandLineRunner:实现后可以直接执行,当项目启动后执行run方法,参数时启动时候传递的参数
ApplicationRunner:实现后可以直接执行,当项目启动后执行run方法,参数是启动时候传递的参数
上面都加上运行结果:
八、流程分析
初始化操作:
配置source
配置是否为web环境
创建初始化构造器——只创建,未操作
创建应用监听器——只创建,未操作
配置应用的主方法所在类
run
应用启动计时器开始计时,应用启动监听器开始监听
应用启动监听模块:SpringApplicationRunListener
启动
监听
配置环境
应用上下文
......
结束
配置环境模块
创建配置环境
加载属性文件资源
配置监听
图标
应用上下文模块
创建应用上下文对象
基础属性配置
加载配置环境
ResourceLoader资源加载器
配置监听
加载启动参数
更新应用上下文
配置环境所需bean工厂
通过工厂生产所需的bean
应用启动时结束计时,应用启动监听器结束监听
SpringApplication启动结束
Last updated
Was this helpful?