手写SpringBoot(四)之bean动态加载

简介: 可以看到只有userApplication tomcatWebServer init打印,没有mySpringboot tomcatWebServer init打印,证明spring-boot只加载了用户定义的那个tomcatWebServer

手写SpringBoot(四)之bean动态加载

本节巩固一下@ConditionalOnClass的相关逻辑,主要新增@ConditionalOnMissingBean注解


SpringBoot配置了很多类,如果用户不希望使用springboot自动配置的那个类怎么办?


如果用户强行配置了一个相同的类,SpringBoot会选择使用哪一个呢?


这就需要按需加载了,如果用户配置了相同的类,则SpringBoot就不加载自己配置的那个。这就是ConditionalOnMissingBean的由来,如果这个bean缺失了,则SpringBoot将这个Bean配置上,如果这个Bean由用户配置了,则自己配置的bean作弃。


前置工作,将MyConditionalOnClass,MyClassCondition 迁移到my-spring-boot-configuration模块下面


ConditionalOnMissingBean 逻辑实现


定义@MyConditionalOnMissingBean注解


package cn.axj.springboot.my.annotation;

import cn.axj.springboot.my.condition.MyMissingBeanCondition;
import org.springframework.context.annotation.Conditional;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
@Conditional(MyMissingBeanCondition.class)
public @interface MyConditionalOnMissingBean {
    String value();
}

  1. 实现MissBean的判断逻辑,新增MyMissingBeanCondition
package cn.axj.springboot.my.condition;

import cn.axj.springboot.my.annotation.MyConditionalOnMissingBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

import java.util.Map;
import java.util.Objects;

public class MyMissingBeanCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(MyConditionalOnMissingBean.class.getName());
       /**
         * 获取{@link MyConditionalOnMissingBean}注解中的属性值
         * 例如:@MyConditionalOnMissingBean(value = "com.example.MyBean")
         * 则可以通过annotationAttributes.get("value")获取到"com.example.MyBean"
         */
        String beanName = (String) annotationAttributes.get("value");
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        Object bean = null;
        try {
            //这里简单点,根据beanName获取bean,实际中需要更复杂的逻辑来判断是否缺失
            bean = beanFactory.getBean(beanName);
            return false;
        } catch (BeansException e) {
            BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory();
            try {
                if(Objects.isNull(parentBeanFactory)){
                    return true;
                }
                bean = parentBeanFactory.getBean(beanName);
                return false;
            } catch (BeansException ex) {
                return true;
            }
        }
    }
}

至此@ConditionalOnMissingBean逻辑开发完成


测试


在UserApplication中配置tomcatWebServer

  @Bean
    public TomcatWebServer tomcatWebServer(){
        System.out.println("userApplication tomcatWebServer init");
        return new TomcatWebServer();
    }

在my-spring-boot模块WebServerAutoConfiguration中,配置tomcatWebServer中加上注解


@ConditionalOnMissingBean

@Bean
    @MyConditionalOnClass("org.apache.catalina.startup.Tomcat")
    @MyConditionalOnMissingBean("tomcatWebServer")
    public TomcatWebServer tomcatWebServer() {
        System.out.println("mySpringboot tomcatWebServer init");
        return new TomcatWebServer();
    }

启动user-application,

userApplication tomcatWebServer init
三月 29, 2024 5:20:31 下午 org.springframework.context.annotation.ConfigurationClassPostProcessor enhanceConfigurationClasses
信息: Cannot enhance @Configuration bean definition 'userApplication' since its singleton instance has been created too early. The typical cause is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor return type: Consider declaring such methods as 'static'.
启动TomcatWeb容器
三月 29, 2024 5:20:31 下午 org.apache.coyote.AbstractProtocol init
信息: Initializing ProtocolHandler ["http-nio-8080"]
三月 29, 2024 5:20:31 下午 org.apache.catalina.core.StandardService startInternal
信息: Starting service [Tomcat]
三月 29, 2024 5:20:31 下午 org.apache.catalina.core.StandardEngine startInternal
信息: Starting Servlet engine: [Apache Tomcat/9.0.75]
三月 29, 2024 5:20:32 下午 org.apache.catalina.util.SessionIdGeneratorBase createSecureRandom
警告: Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [320] milliseconds.
三月 29, 2024 5:20:32 下午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["http-nio-8080"]


可以看到只有userApplication tomcatWebServer init打印,没有mySpringboot tomcatWebServer init打印,证明spring-boot只加载了用户定义的那个tomcatWebServer

目录
相关文章
|
10天前
|
Java
SpringBoot构建Bean(RedisConfig + RestTemplateConfig)
SpringBoot构建Bean(RedisConfig + RestTemplateConfig)
31 2
|
1月前
|
架构师 Java 开发者
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?
在40岁老架构师尼恩的读者交流群中,近期多位读者成功获得了知名互联网企业的面试机会,如得物、阿里、滴滴等。然而,面对“Spring Boot自动装配机制”等核心面试题,部分读者因准备不足而未能顺利通过。为此,尼恩团队将系统化梳理和总结这一主题,帮助大家全面提升技术水平,让面试官“爱到不能自已”。
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?
|
1月前
|
Java Spring 容器
Springboot3.2.1搞定了类Service和bean注解同名同类型问题修复
这篇文章讨论了在Spring Boot 3.2.1版本中,同名同类型的bean和@Service注解类之间冲突的问题得到了解决,之前版本中同名bean会相互覆盖,但不会在启动时报错,而在配置文件中设置`spring.main.allow-bean-definition-overriding=true`可以解决这个问题。
85 0
Springboot3.2.1搞定了类Service和bean注解同名同类型问题修复
|
2月前
|
Java Spring
springboot 集成 swagger 2.x 和 3.0 以及 Failed to start bean ‘documentationPluginsBootstrapper‘问题的解决
本文介绍了如何在Spring Boot项目中集成Swagger 2.x和3.0版本,并提供了解决Swagger在Spring Boot中启动失败问题“Failed to start bean ‘documentationPluginsBootstrapper’; nested exception is java.lang.NullPointerEx”的方法,包括配置yml文件和Spring Boot版本的降级。
springboot 集成 swagger 2.x 和 3.0 以及 Failed to start bean ‘documentationPluginsBootstrapper‘问题的解决
|
1月前
|
Java Shell C++
Springboot加载注入bean的方式
本文详细介绍了Spring Boot中Bean的装配方法。首先讲解了使用@Component、@Service、@Controller、@Repository等注解声明Bean的方式,并解释了这些注解之间的关系及各自适用的层次。接着介绍了通过@Configuration和@Bean注解定义Bean的方法,展示了其灵活性和定制能力。最后讨论了@Component与@Bean的区别,并提供了在Spring Boot应用中装配依赖包中Bean的三种方法:使用@ComponentScan注解扫描指定包、使用@Import注解导入特定Bean以及在spring.factories文件中配置Bean。
|
4月前
|
Java Spring 容器
Spring Boot 启动源码解析结合Spring Bean生命周期分析
Spring Boot 启动源码解析结合Spring Bean生命周期分析
103 11
|
4月前
|
消息中间件 Java Kafka
Spring boot 自定义kafkaTemplate的bean实例进行生产消息和发送消息
Spring boot 自定义kafkaTemplate的bean实例进行生产消息和发送消息
187 5
|
3月前
|
Java Spring 容器
Java SpringBoot 中,动态执行 bean 对象中的方法
Java SpringBoot 中,动态执行 bean 对象中的方法
40 0
|
3月前
|
Java Spring
Java SpringBoot Bean InitializingBean 项目初始化
Java SpringBoot Bean InitializingBean 项目初始化
54 0
|
4月前
|
文字识别 Java
文本,文字识别07,SpringBoot服务开发-入参和返回值,编写接口的时候,要注意识别的文字返回的是多行,因此必须是List集合,Bean层,及实体类的搭建
文本,文字识别07,SpringBoot服务开发-入参和返回值,编写接口的时候,要注意识别的文字返回的是多行,因此必须是List集合,Bean层,及实体类的搭建