spring boot 自动配置原理
springboot的自动配置原理
在我们使用springboot的时候,能带来的方便性和便利性,不需要配置便可以实现相关的使用,开发效率极大的提升,那么实际上,springboot本身的基础依赖中封装了许许多多的配置帮我们自动完成了配置了。那么它是如何实现的呢?
Condition接口及相关注解
那么它到底是如何实现的呢?我们现在给一个需求:
(1)需求:需求目的:验证以及了解只要添加起步依赖,spring boot就能将相应的类自动放入spring容器中的步骤。
在spring容器中有一个user的bean对象,如果导入了redisclient的坐标则加载该bean,如果没有导入则不加载该bean.
(2)实现步骤:步骤解析:只要添加jedis的依赖,就将自己创建的user对象放入spring容器中
1.定义一个接口condition的实现类
public class ConditionUser implements Condition { @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { try { //实例一个类,如果没有异常,则返回true,让@Conditional修饰的方法执行,这也说明jedis的依赖已经生效 Class.forName("redis.clients.jedis.Jedis"); return true; } catch (ClassNotFoundException e) { e.printStackTrace(); //如果实例化jedis类出现异常,说明没有jedis的依赖,不让@Conditional修饰的方法执行,spring容器中也不会出现User的对象 return false; } } }
2.实现方法 判断是否有字节码对象,有则返回true 没有则返回false
3.定义一个User的pojo
4.定义一个配置类用于创建user对象交给spring容器管理
5.修改加入注解@conditional(value=Condition)
//@Configuration:指定一个类为配置类,在这个配置类中可以将user对象放入核心容器 @Configuration public class ConfigUser { //@Bean(name = "user"):加上这个注解的方法,返回的对象就会被放入核心容器,name指定放入核心容器的对象名 @Bean(name = "user") //@Conditional(value = ConditionUser.class):加上这个注解的方法会根据指定的condition的实现类返回的结果决定是否执行这个方法 @Conditional(value = ConditionUser.class) public User configUser(){ return new User(); } }
condition 用于自定义某一些条件类,用于当达到某一个条件时使用。关联的注解为@conditional结合起来使用。当然我们springboot本身已经提供了一系列的注解供我们使用:
ConditionalOnBean 当spring容器中有某一个bean时使用
ConditionalOnClass 当判断当前类路径下有某一个类时使用
ConditionalOnMissingBean 当spring容器中没有某一个bean时才使用
ConditionalOnMissingClass 当当前类路径下没有某一个类的时候才使用
ConditionalOnProperty 当配置文件中有某一个key value的时候才使用
中间步骤省略……
无需手动将类实例化并放入核心容器的总结:
spring boot框架实现只要加入初步依赖,相关类全部都会通过condition接口判断,如果有初步依赖,则自动放入spring容器中,这样就可以在使用时无需再去配置文件中用<bean>标签ioc了,直接注入就可以使用。
我们知道在springboot启动的时候如果我们使用web起步依赖,那么我们默认就加载了tomcat的类嵌入了tomcat了,不需要额外再找tomcat。
web起步依赖依赖于spring-boot-starter-tomcat,这个为嵌入式的tomcat的包,而spring boot默认使用的也就是tomcat。
自动配置方式:
我们也可以尝试修改web容器:只要在依赖中排除tomcat 加入想要的容器:
<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> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency>
自动配置tomcat原理总结:原理大致上同,会用condition接口实现的条件判断类,判断是否有tomcat的依赖,如果有,则会执行相关配置类,通过@bean注解将tomcat初始化并放入核心容器
@SpringBootApplication 由以下三个注解来组成:
+ @SpringBootConfiguration 就是配置类的注解 被该注解修饰的类就是一个配置类。
+ @ComponentScan 是一个组件扫描的注解 相当于spring.xml中的<context:componet-scan >
@ComponentScan 默认的情况下会自动扫描 该注解修饰的类 所在的包以及子包。可以自定义指定扫描的包路径
+ @EnableAutoConfiguration 启用自动的配置
@ComponentScan("com")注解和@impor(ConfigUser.class)的使用方式,以及引出自定义注解@EnableUser的使用方式
1.定义两个工程demo2 demo3 demo3中有bean
2.demo2依赖了demo3
3.我们希望demo2直接获取加载demo3中的bean
验证结论:1、如果配置类不在被@SpringBootApplication注解修饰的类的两级包目录,那么配置类也就不会被扫描,更不会把user到并放入spring容器
2、如果想要解决配置类不在被@SpringBootApplication注解修饰的类的两级包目录,也要将user放入spring容器中,两种简单解决方式:
.第一种使用组件扫描 扫描包路径放大 在启动类上加@ComponentScan("com")注解
.第二种使用import注解进行导入配置类的方式即可 在启动类上加@impor(ConfigUser.class)注解,直接指定配置类让springboot执行
3、但是以上两种方式麻烦,所以引出自定义注解@EnableUser的使用方式:
在com.config下创建一个自定义注解@EnableUser: 这个自定义注解重点在于import的注解
package com.config; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(UserConfig.class) public @interface EnableUser { }
以上自定义注解使用结论:使用@import注解可以加载指定配置类,只要传给要加载的配置类的字节码对象即可
import注解用于导入其他的配置,让spring容器进行加载和初始化。import的注解有以下的几种方式使用:
-
直接导入Bean 例如:()
-
导入配置类 上述需求实现就用到了
-
导入ImportSelector的实现类,通常用于加载配置文件中的Bean 例如:@improt(Configs.class)会加载指定的ImprotSelector接口的实现类
/** * 这个类需要实现ImportSelector接口,并重写其方法 * 只要这个实现类被加载,就会把selectImports方法返回的字符串数组中写的相应类放入spring容器中 * */ public class Configs implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { //只要这个字符串数组中指定的类,都会随着这个实现类加载而放入spring容器中 /* 有一个地方要注意,这种方式放入spring容器中,它们的名字就是这个全限定名,类名首字母改成小写 所以取的时候不能再用getBean("user") 可以用getBean(User.class)方式,也可以如下方式: getBean("com.xxx.pojo.user"); getBean("com.xxx.pojo.role"); * */ String[] aaa = {"com.xxx.pojo.User","com.xxx.pojo.Role"}; return aaa; } }
-
导入ImportBeanDefinitionRegistrar实现类
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //创建BeanDefiniation AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition(); //注册bean 目前只注册User registry.registerBeanDefinition("user",beanDefinition); } }
如果感觉上面的操作陌生不好理解,没关系,无需理解,上面的操作就是把一个User类注册到spring容器中了,然后只要这个实现类被加载执行,那么它指定的User就会被放到spring容器中
理解以上几种import注解导入其他的配置,让spring容器进行加载和初始化的方式,为了什么? -----这是为了理解自动配置的前置要求,源码中使用了这些注解
现在,开始图片旅途。之前理到了@SpringBootApplication 由三个注解来组成:
+ @SpringBootConfiguration 就是配置类的注解 被该注解修饰的类就是一个配置类。
+ @ComponentScan 是一个组件扫描的注解 相当于spring.xml中的<context:componet-scan >
+ @EnableAutoConfiguration 启用自动的配置
那么,这三个注解中前两个已经讲过,只有第三个,启用自动配置,还没有讲,接下来的图片就是从加在启动类上的@SpringBootApplication开始:
两张图片进行对比,确认它们是一个东西
如果觉得还不确定,那么往前退一步,再次进行对比
接下来的东西我也懵,所以到此为止,但目的还没达到,所以现在开始意淫。
理解到这里,我就发现,其实理解了一开始的那个需求就差不多了,一个意思,只不过它底层原理要比那个复杂很多。总结一下:
1、在启动类上加@SpringBootApplication,这个注解被@EnableAutoConfiguration修饰了
2、@EnableAutoConfiguration注解被@Import({AutoConfigurationImportSelector.class})修饰了
3、@Import({AutoConfigurationImportSelector.class})注解导入了一个importSelector接口实现类,这个实现类中获取了很多的(跟@EnableAutoConfiguration注解关连)配置类的全路径名,放入list<String>中返回
4、这样,被@Import({AutoConfigurationImportSelector.class})注解修饰的类最终就会获得这个List<String>,并将这个list中的配置类全部交给spring容器,如果达成条件,这个配置类就会执行,然后注入就可以使用
如果再针对性(针对spring boot实现自动配置的过程)的忽悠就是这样:
首先,启动类只要启动,就会加载@SpringBootApplication注解,这个注解被三个关键注解修饰,共同完成自动配置。分别是@SpringBootConfiguration、@ComponentScan和@EnableAutoconfiguration
其中:@SpringBootConfiguration表示这是一个配置类,交给spring容器会被加载
@CompoentScan注解会被启动类所在的包以及其子包中扫描所有被@controller、@Service注解修饰的类,并将其放入spring容器中,这里就不需要我们手动去配置文件扫描包了。
最关键的@EnableAutoConfiguration注解,它被一个@Import注解导入了一个ImportSelector接口的实现类,这个实现类会获取SpringBoot框架准备好的配置类的全路径名,并把这些全路径名放到list集合中返回,所以,当@EnabaleAutoConfiguration注解加载的时候,只要这些配置类达成相应条件,就会被加载到spring容器中,这些配置类中的bean也就存在与spring容器中了,写代码的时候直接注入就可以了。
这里面又涉及到达成相应条件,关系到Condition接口,当Condition接口的实现类返回true,那么,被@Conditional注解的方法才会被执行,所以它的操作空间是什么条件返回true。
自定义starter
了解自定义starter流程: 重点理解部分:1、如何完成自动配置,2、如何完成让使用者自己配置的部分。 一下主要以两个部分图片
自定义starter需求:只要依赖了jedis,就将jedis自动配置好,让用户可以直接使用jedis,如果不额外配置,则采用自动默认配置
自定义starter部分:
这个部分由自动配置类、pojo、spring.factories三个东西组成,三张图片
pojo:用于自动配置的参数存储,或去配置文件加载数据,或用默认值
自动配置类:
spring.factories:
测试部分:两个部分组成,一个启动类,一个配置文件:
配置文件:配置文件怎么写在上面的图片已经写过
spring boot 的监控
Actuator是springboot自带的组件可以用来进行监控,Bean加载情况、环境变量、日志信息、线程信息等等,使用简单
需要添加依赖如下:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
application.properties:
# 配置健康端点开启所有详情信息 management.endpoint.health.show-details=always # 设置开放所有web相关的端点信息 management.endpoints.web.exposure.include=* # 设置info前缀的信息设置 info.name=zhangsan info.age=18
但是使用actuator使用起来比较费劲,没有数据直观感受。我们可以通过插件来展示。
-
-
Spring Boot Admin 有两个角色,客户端(Client)和服务端(Server)。
-
应用程序作为Spring Boot Admin Client向为Spring Boot Admin Server注册
-
spring boot admin的架构角色
-
admin server 用于收集统计所有相关client的注册过来的信息进行汇总展示
-
admin client 每一个springboot工程都是一个client 相关的功能展示需要汇总到注册汇总到server
创建admin server 工程 itheima-admin-server
配置依赖:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.13.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.itheima</groupId> <artifactId>ithima-admin-server</artifactId> <version>0.0.1-SNAPSHOT</version> <name>ithima-admin-server</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-boot-admin.version>2.1.6</spring-boot-admin.version> </properties> <dependencies> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-dependencies</artifactId> <version>${spring-boot-admin.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
创建启动类:@EnableAdminServer 该注解用于启用Server功能。
@SpringBootApplication @EnableAdminServer public class IthimaAdminServerApplication { public static void main(String[] args) { SpringApplication.run(IthimaAdminServerApplication.class, args); } }
修改application.properties文件
server.port=9000
创建admin client 工程 itheima-admin-client
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.13.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.itheima</groupId> <artifactId>ithima-admin-client</artifactId> <version>0.0.1-SNAPSHOT</version> <name>ithima-admin-client</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-boot-admin.version>2.1.6</spring-boot-admin.version> </properties> <dependencies> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-dependencies</artifactId> <version>${spring-boot-admin.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
创建启动类:
@SpringBootApplication public class IthimaAdminClientApplication { public static void main(String[] args) { SpringApplication.run(IthimaAdminClientApplication.class, args); } @RestController @RequestMapping("/user") class TestController { @RequestMapping("/findAll") public String a() { return "aaaa"; } } }
配置application.properties:
# 配置注册到的admin server的地址 spring.boot.admin.client.url=http://localhost:9000 # 启用健康检查 默认就是true management.endpoint.health.enabled=true # 配置显示所有的监控详情 management.endpoint.health.show-details=always # 开放所有端点 management.endpoints.web.exposure.include=* # 设置系统的名称 spring.application.name=abc
点击相关界面链接就能看到相关的图形化展示了。
SpringBoot项目部署:
将项目打包,然后输入命令:java -jar 项目名-1.0-SNAPSHOT.jar 就可以了。