Springboot的理解和疑惑

前言:大家都知道springboot的好处是,简化配置,一键启动,面试也总是问自动装配的原理,我们只有深刻理解装配原理才能以不变应万变。

目录

问题1:springboot 自动配置是如何 知道依赖的类存在不存在?

问题2: springboot-start-XXXX下META-INF的文件作用


问题1:springboot 自动配置是如何 知道依赖的类存在不存在?

在Spring Boot中,自动配置是通过类路径上的META-INF/spring.factories文件实现的。这个文件中列出了所有自动配置类的全限定类名。Spring Boot在启动时会扫描这些自动配置类,然后根据需要将它们应用到应用程序上下文中。

当您在项目中添加一个新的依赖时,例如在pom.xml文件中添加一个新的库,Spring Boot会扫描该库的类路径以查找META-INF/spring.factories文件。如果该文件存在,它将被读取并将其中列出的自动配置类加载到应用程序上下文中。

如果自动配置类依赖于其他类或库,但这些类或库不存在,或者没有正确配置,Spring Boot会在应用程序启动时抛出异常。因此,要确保自动配置类正常工作,必须正确配置依赖项并将它们添加到类路径中。

总的来说,Spring Boot的自动配置机制是基于类路径上的META-INF/spring.factories文件的。这个文件中列出了所有自动配置类的全限定类名。当您添加新的依赖时,Spring Boot会自动扫描该依赖的类路径以查找该文件,并将其中列出的自动配置类应用到应用程序上下文中。

扩展:所以,我们也了解到:如果发现jar包中的类是报错的,如:

那这个自动装配是肯定不会 自动装配的。 

问题2: springboot-start-XXXX下META-INF的文件作用

spring-autoconfigure-metadata.properties文件是Spring Boot自动配置元数据文件,用于描述Spring Boot自动配置类的元数据信息。这个文件包含一组键值对,每个键值对表示一个自动配置项的元数据信息,例如属性名称、属性类型、默认值、描述等等。

Spring Boot使用这个文件来帮助IDE(例如Eclipse、IntelliJ IDEA等)和其他工具(例如Spring Boot Actuator)了解自动配置类的信息

如:

org.springframework.boot.autoconfigure.condition.ConditionalOnProperty=\
  name,spring.datasource.url,\
  matchIfMissing=false
  prefix=spring.datasource

spring-configuration-metadata.json文件是Spring Boot配置元数据文件,用于描述应用程序配置属性的元数据信息。这个文件包含了一组JSON格式的键值对,每个键值对表示一个配置属性的元数据信息,例如属性名称、属性类型、默认值、描述等等。

Spring Boot使用这个文件来帮助IDE(例如Eclipse、IntelliJ IDEA等)和其他工具(例如Spring Boot Actuator)了解自动配置类的信息

如:

{
  "name": "spring.datasource.url",
  "type": "java.lang.String",
  "description": "URL of the database.",
  "defaultValue": "jdbc:h2:mem:testdb"
}

spring.factories文件是Spring框架的一个标准配置文件,它允许第三方开发者向Spring框架注册自己的实现类或者工厂类,以扩展或替换Spring框架的默认行为。这个文件位于类路径的META-INF目录下,它是一个标准的Java属性文件,其中每行包含了一个键值对,键表示要注册的类型或接口,值表示该类型或接口的实现类或工厂类。

如:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\
  org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,\
  org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration

 SpringBoot的启动原理:

基本流程:①加载配置文件②创建是spring容器③将Bean放入容器④自动配置

手写SpringBoot源码: 

启动类:

@ZhouyuSpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        ZhouyuSpringApplication.run(MyApplication.class);
    }
}


------------以下是启动类注解--------
/*

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Configuration
@ComponentScan //只能扫描当前注解标注类的 兄弟及下属目录 扫描到的且标注注解的 才放入容器
@Import(ZhouyuImportSelect.class) // @import的作用是将ZhouyuImportSelect.class作为一个bean放入容器
public @interface ZhouyuSpringBootApplication {
}

*/

1、创建容器(创建springBoot容器、开启Tomcat服务器)

public class ZhouyuSpringApplication {

    public static void run(Class clazz){
        //创建一个spring容器  创建的方式有很多种,如果是加载xml的话,就是ClassPathXmlApplicationContext,如果是加载注解的话,就是AnnotationConfigApplicationContext
        AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
        applicationContext.register(clazz);//向容器中注册一个启动类
        applicationContext.refresh();
        //从sspring容器中开启TomcatService容器
        WebServer webServer = getWebServer(applicationContext);
        webServer.start();

    }

    public static WebServer getWebServer(ApplicationContext applicationContext){
        Map<String, WebServer> beansOfType = applicationContext.getBeansOfType(WebServer.class);
        if (beansOfType.isEmpty()) {
            throw new NullPointerException();
        }
        if (beansOfType.size() > 1) {//如果拿到多个同类型的Bean就抛异常
            throw new IllegalStateException();
        }
        return beansOfType.values().stream().findFirst().get();
    }
}

2、向容器放入需要Bean

//这个类需要注意 有没有被 SpringBoot扫描到?这时候就需要在 启动类头上只扫描了其兄弟目录
@Configuration
public class WebServerAutoConfiguration implements AutoConfiguration {

    @Bean
    @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")  
//如果maven中排除Tomcat依赖 这个就为false 此刻该bean就注入不成功了,其实现是通过类加载器获取,获取不到表示系统中没有这个类
    public TomcatWebServer tomcatWebServer(){
        return new TomcatWebServer();
    }

    @Bean
    @ConditionalOnClass(name = "org.eclipse.jetty.server.Server")
    public JettyWebServer jettyWebServer(){
        return new JettyWebServer();

    }
}

 通过配置类向容器中注入 TomcatService和JettyService,但上面代码写的,发现两个webServers时则抛异常,所以我们对Bean的注入加入@Conditional(XX.class)(其内部)通过类加载器判断有没有加载到类。下图是实现判定是否存在该class的原理:

 Maven知识:

<option>true<option>标签:

Spring的Bean的生命周期

前言:了解Spring Bean的生命周期 可以更好的帮我们管理与优化java项目

Bean的生命周期可以分为4个阶段:

  1. 实例化 Bean

  2. Bean属性赋值 (Bean的属性值或依赖注入到Bean的实例中)

  3. 初始化Bean

    1. 是否实现Aware相关接口

      1. (作用:让Bean拿到容器中的资源,如BeanNameAware可以拿BeanName)

    2. BeanPostProcessor前置后置处理

      1. 作用:对bean进行一些自定义操作,如 代理,数据校验

    3. 是否实现InitailizinBean接口

      1. 作用:编写Bean的初始化逻辑,如初始化数据库连接

    4. 是否配置自定义initMethod

    5. BeanPostProcessor后置处理

  4. 使用Bean

  5. 销毁 Destruction

注:BeanPostProcessor是对所有实例都其作用,即容器内的bean均会调用。应用场景:对某些类标记了某注解,那我们在某个bean实现该接口,结合AnnotationUtils.findAnnotation去查询,凡是标记的都放入map进行管理。

例如:

项目启动前后的加载顺序

前言:对于第三方、或者某些配置 经常需要 在xx之后或者在xx之前加载,因此需要了解加载顺序。

①项目完全启动后加载

方法1:ApplicationRunnerCommandLineRunner接口来实现。这两个接口都是在Spring Boot应用程序启动完成后执行的。

ApplicationRunner接口中的run()方法会在Spring Boot应用程序启动完成后被调用,而CommandLineRunner接口中的run()方法则会在ApplicationRunner接口中的run()方法之后被调用。这两个接口都需要实现一个run()方法,如果项目中存在多个继承这接口又想指定顺序时,可以使用注解 @Order来指定加载顺序

如:

2023-05-17 10:23:17.027  INFO 19704 --- [           main] c.patpat.product.ProductCoreApplication  : Started ProductCoreApplication in 68.43 seconds (JVM running for 69.844)

实现方法:实现接口 ApplicationRunner,如下

@Slf4j
@Configuration
public class RabbitConfig implements ApplicationRunner {

  @Resource
    private Environment environment;

 @Override
    public void run(ApplicationArguments args) {
//以下实现了 项目启动后 以及 环境变量start_mq 的值 来判断是否启动mq
        Collection<MessageListenerContainer> containers = registry.getListenerContainers();
        String environmentProperty = environment.getProperty("start_mq");
        if (Objects.equals(environmentProperty,"true")) {
            for (MessageListenerContainer container : containers) {
                container.start();
                log.info("-----启动监听mq,监听队列:"+container+" -----");
            }
        }
    }
}

方法②:使用@PostConstruct

 @PostConstruct注解可以用于标记一个方法,在Bean初始化后立即执行,会早于ApplicationRunner

@Configuration
public class TestConfig {
    @PostConstruct
    public void init() {
        System.out.println("TestConfig init");
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值