手写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

目录
相关文章
|
2月前
|
存储 NoSQL Java
Spring Boot统计一个Bean中方法的调用次数
Spring Boot统计一个Bean中方法的调用次数
60 1
|
2月前
|
XML Java 开发者
Spring Boot中的bean注入方式和原理
Spring Boot中的bean注入方式和原理
250 0
|
23天前
|
Java Spring
解决Springboot集成ElasticSearch 报错:A bean with that name has already been defined in null and overriding
解决Springboot集成ElasticSearch 报错:A bean with that name has already been defined in null and overriding
|
3天前
|
Java Linux 程序员
技术笔记:Spring生态研习【五】:Springboot中bean的条件注入
技术笔记:Spring生态研习【五】:Springboot中bean的条件注入
|
9天前
|
Java
springboot Test 测试类中如何排除一个bean类
springboot Test 测试类中如何排除一个bean类
8 0
|
2月前
|
Java 开发者 Spring
Springboot中的@Bean用法以及常见问题
【5月更文挑战第27天】@Bean 注解是Spring框架中用于声明Spring应用上下文中的bean的一种方式。在Spring Boot中,@Bean注解通常与@Configuration注解一起使用,在配置类(Configuration class)中定义bean。
38 2
|
2月前
|
XML Java 数据库
【SpringBoot:详解Bean装配】
【SpringBoot:详解Bean装配】
15 3
|
2月前
|
Java Spring 容器
SpringBoot中bean的生命周期
Spring Boot的Bean生命周期涉及实例化、属性注入、初始化和销毁。在实例化后,Spring通过构造函数或Setter注入属性,然后调用初始化方法(@PostConstruct、InitializingBean接口)。Bean在应用中使用后,当容器关闭时,会调用销毁方法(@PreDestroy、DisposableBean接口)。依赖注入、配置管理、组件扩展和切面编程是其常见应用场景。示例代码展示了如何通过实现BeanNameAware、BeanFactoryAware等接口以及使用@PostConstruct注解来控制Bean的初始化。
50 2
SpringBoot中bean的生命周期
|
2月前
|
Java Spring 容器
SpringBoot 使用Quartz执行定时任务对象时无法注入Bean问题
SpringBoot 使用Quartz执行定时任务对象时无法注入Bean问题
50 1
|
2月前
|
Java 容器 Spring
SpringBoot:Bean生命周期自定义初始化和销毁
SpringBoot:Bean生命周期自定义初始化和销毁