spring boot 自动配置原理


springboot的自动配置原理

在我们使用springboot的时候,能带来的方便性和便利性,不需要配置便可以实现相关的使用,开发效率极大的提升,那么实际上,springboot本身的基础依赖中封装了许许多多的配置帮我们自动完成了配置了。那么它是如何实现的呢?

Condition接口及相关注解

讲Springboot自动配置,逃不开ConditionalOnxxx等等注解,也逃不开condition接口所定义的功能。

condition接口是spring4之后提供给的接口,增加条件判断功能,用于选择性的创建Bean对象到spring容器中。

  我们之前用过springboot整合redis 实现的步骤:就是添加redis起步依赖之后,直接就可以使用从spring容器中获取注入RedisTemplate对象了,而不需要创建该对象放到spring容器中了.意味着Spring boot redis的起步依赖已经能自动的创建该redisTemplate对象加入到spring容器中了。这里应用的重要的一个点就是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了,直接注入就可以使用。


 

切换内置的web容器

我们知道在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初始化并放入核心容器


 

 

@Enable*类型的注解说明

@SpringBootApplication 由以下三个注解来组成:
+ @SpringBootConfiguration 就是配置类的注解 被该注解修饰的类就是一个配置类
+ @ComponentScan 是一个组件扫描的注解 相当于spring.xml中的<context:componet-scan >
@ComponentScan 默认的情况下会自动扫描 该注解修饰的类 所在的包以及子包。可以自定义指定扫描的包路径
+ @EnableAutoConfiguration 启用自动的配置

再通过一个需求实现,来理解:

实现加载第三方的Bean

需求:需求目的:验证spring boot 框架会自动扫描被@SpringBootApplication注解修饰的类的两级包目录,以及验证@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注解

import注解用于导入其他的配置,让spring容器进行加载和初始化。import的注解有以下的几种方式使用:

  • 直接导入Bean       例如:(@import(User.class)

  • 导入配置类     上述需求实现就用到了

  • 导入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

 

 

在浏览器输入 地址:http://localhost:8080/actuator

 

但是使用actuator使用起来比较费劲,没有数据直观感受。我们可以通过插件来展示。

  • Spring Boot Admin是一个开源社区项目,用于管理和监控SpringBoot应用程序。

  • Spring Boot Admin 有两个角色,客户端(Client)和服务端(Server)。

  • 应用程序作为Spring Boot Admin Client向为Spring Boot Admin Server注册

  • Spring Boot Admin Server 通过图形化界面方式展示Spring Boot Admin Client的监控信息。

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

启动两个系统。访问路径<http://localhost:9000/>点击相关界面链接就能看到相关的图形化展示了。

 

 

 

SpringBoot项目部署:

将项目打包,然后输入命令:java -jar 项目名-1.0-SNAPSHOT.jar  就可以了。

posted @ 2020-10-30 14:59  名难  阅读(2195)  评论(0编辑  收藏  举报