【Spring】——7、@Import注解给容器中快速导入一个组件

news2024/7/4 5:14:56

在这里插入图片描述

📫作者简介:zhz小白
公众号:小白的Java进阶之路
专业技能:
1、Java基础,并精通多线程的开发,熟悉JVM原理
2、熟悉Java基础,并精通多线程的开发,熟悉JVM原理,具备⼀定的线上调优经验
3、熟悉MySQL数据库调优,索引原理等,⽇志原理等,并且有出过⼀篇专栏
4、了解计算机⽹络,对TCP协议,滑动窗⼝原理等有⼀定了解
5、熟悉Spring,Spring MVC,Mybatis,阅读过部分Spring源码
6、熟悉SpringCloud Alibaba体系,阅读过Nacos,Sentinel,Seata,Dubbo,Feign,Gateway核⼼源码与设计,⼆次开发能⼒
7、熟悉消息队列(Kafka,RocketMQ)的原理与设计
8、熟悉分库分表ShardingSphere,具有真实⽣产的数据迁移经验
9、熟悉分布式缓存中间件Redis,对其的核⼼数据结构,部署架构,⾼并发问题解决⽅案有⼀定的积累
10、熟悉常⽤设计模式,并运⽤于实践⼯作中
11、了解ElasticSearch,对其核⼼的原理有⼀定的了解
12、了解K8s,Jekins,GitLab
13、了解VUE,GO
14、⽬前有正在利⽤闲暇时间做互游游戏,开发、运维、运营、推销等

本人著作git项目:https://gitee.com/zhouzhz/star-jersey-platform,有兴趣的可以私聊博主一起编写,或者给颗star
领域:对支付(FMS,FUND,PAY),订单(OMS),出行行业等有相关的开发领域
🔥如果此文还不错的话,还请👍关注、点赞、收藏三连支持👍一下博主~

文章目录

  • 1、自己写的类:
  • 2、三方类
  • 3、注册Bean的方式(注解方式)
  • 4、@Import注解概述
  • 5、@Import使用方式
    • 5.1、验证@Import效果
      • 5.1.1、正常情况下没有@Import注解时的效果
    • 5.2、使用方式详解
      • 5.2.1、直接填写class数组的方式
        • 5.2.1.1、Import注解类单导入
        • 5.2.1.2、Import注解类批量导入
      • 5.2.2、ImportSelector,即批量导入(重点)
        • 5.2.2.1、上实践用例
          • 5.2.2.1.1、首先我们创建一个类它实现了ImportSelector接口,代码如下:
          • 5.2.2.1.2、我们在MainConfig中通过@Import引入该类,代码如下:
          • 5.2.2.1.3、我们在TestImportSelector类上的selectImports()方法增加一个断点,然后我们dubug下面的测试方法
          • 5.2.2.1.4、我们dubug之后发现,打到这个断点之前,是没有任何输出的,说明是在bean实例化前就做了,如下
          • 5.2.2.1.5、然后我们再回归selectImports()方法,看一下他的参数的值,可以发现他获取到了当前标注了@Import注解的类的所有注解信息。
          • 5.2.2.1.6、我们来研究一下为什么会空指针?
          • 5.2.2.1.7、防止以上空指针,代码改造
          • 5.2.2.1.8、改造White.class,blue.class的导入方式
      • 5.2.3、ImportBeanDefinitionRegistrar接口方式,即手工注册bean到容器中
        • 5.2.3.1、ImportBeanDefinitionRegistrar介绍
        • 5.2.3.2、使用方法
        • 5.2.3.3、ImportBeanDefinitionRegistrar接口实例

1、自己写的类:

日常工作中,我们可以把一些bean组件通过包扫描+注解(@Controller,@Service,@Respository,@Component)的形式将入注入到IOC容器中,可以定制为单例Bean或者多例Bean,当然这种方式,一般局限于我们自己写的类。

2、三方类

如果是三方的类,我们一般怎么注入到容器中呢,有两种方方式:@Bean,@Import可以将其注入Spring容器中。

3、注册Bean的方式(注解方式)

包扫描+注解(@Controller、@Servcie、@Repository、@Component)
@Bean注解,通常用于导入第三方包中的组件
@Import注解,快速向Spring容器中导入一个组件

4、@Import注解概述

  • Spring3.0之前我们可以通过XML方式去指定扫描特定的包将类注入到Spring容器中,Spring3.0之后通过JavaConfig的方式将Bean的元信息以代码的方式进行描述,我们可以通过@Configuration与@Bean配合使用将原来配置在XML方文件里面的Bean通过Java代码描述。
  • @Import注解提供了@Bean注解的功能,同时还有XML配置文件里面标签组织多个分散的XML文件的功能,当然在这里是组织多个分散的@Configuration,因为一个配置类就约等于一个XML配置文件。

接下来让我们开始走进@Import的世界,先看其源码:

/*
 * Copyright 2002-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.context.annotation;

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

/**
 * Indicates one or more <em>component classes</em> to import &mdash; typically
 * {@link Configuration @Configuration} classes.
 *
 * <p>Provides functionality equivalent to the {@code <import/>} element in Spring XML.
 * Allows for importing {@code @Configuration} classes, {@link ImportSelector} and
 * {@link ImportBeanDefinitionRegistrar} implementations, as well as regular component
 * classes (as of 4.2; analogous to {@link AnnotationConfigApplicationContext#register}).
 *
 * <p>{@code @Bean} definitions declared in imported {@code @Configuration} classes should be
 * accessed by using {@link org.springframework.beans.factory.annotation.Autowired @Autowired}
 * injection. Either the bean itself can be autowired, or the configuration class instance
 * declaring the bean can be autowired. The latter approach allows for explicit, IDE-friendly
 * navigation between {@code @Configuration} class methods.
 *
 * <p>May be declared at the class level or as a meta-annotation.
 *
 * <p>If XML or other non-{@code @Configuration} bean definition resources need to be
 * imported, use the {@link ImportResource @ImportResource} annotation instead.
 *
 * @author Chris Beams
 * @author Juergen Hoeller
 * @since 3.0
 * @see Configuration
 * @see ImportSelector
 * @see ImportBeanDefinitionRegistrar
 * @see ImportResource
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

	/**
	 * {@link Configuration @Configuration}, {@link ImportSelector},
	 * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
	 */
	Class<?>[] value();

}

我们从其中源码可以看出@Import是可以与@Configuration、ImportSelector、ImportBeanDefinitionRegistrar配合使用,并且可以看出其只能放到类上,不能放到方法或者属性上。

5、@Import使用方式

使用方式:

  • 直接填写class数组的方式
  • ImportSelector,即批量导入(重点)
  • ImportBeanDefinitionRegistrar接口方式,即手工注册bean到容器中

5.1、验证@Import效果

5.1.1、正常情况下没有@Import注解时的效果

我们新建一个类叫White(空类),我们并未通过MainConfig进行注入,代码如下:

package com.zhz.bean;

/**
 * @author zhouhengzhe
 * @date 2022/11/15
 */
public class White {

}

然后我们运行下test方法,看看效果:
在这里插入图片描述

我们可以发现一个现状,他没有把White类注入到Spring容器中,也就是说没有这个Bean实例。

5.2、使用方式详解

5.2.1、直接填写class数组的方式

5.2.1.1、Import注解类单导入

然后我们简单使用下@Import,看一下演示效果,我们在MainConfig中添加一个@Import(White.class)去看一下效果,代码如下

package com.zhz.config;

import com.zhz.bean.Person;
import com.zhz.bean.White;
import com.zhz.condition.AppleCondition;
import com.zhz.condition.BananaCondition;
import com.zhz.condition.WatermelonCondition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
 * @author zhouhengzhe
 * @description: todo
 * @date 2022/11/4 10:27
 * @since v1
 */
@Configuration
@Import(White.class)
public class MainConfig {

    @Bean(name = "person")
    public Person person() {
        return new Person("apple", 20);
    }

    /**
     * @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id
     */
    @Bean(name = "apple")
    @Conditional(AppleCondition.class)
    public Person apple() {
        return new Person("apple", 20);
    }

    @Bean(name = "banana")
    @Conditional(BananaCondition.class)
    public Person banana() {
        return new Person("banana", 20);
    }

    @Bean(name = "watermelon")
    @Conditional(WatermelonCondition.class)
    public Person watermelon() {
        return new Person("watermelon", 20);
    }
}

然后我们运行一下test类,代码如下

  @Test
    public void test3(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }

运行效果如下:
在这里插入图片描述

  • 我们可以发现通过@Import注解快速地导入组件时,容器中就会自动注册这个组件,并且id默认是组件的全类名。

5.2.1.2、Import注解类批量导入

我们在创建一个Bean类,来验证批量导入,代码如下:

package com.zhz.bean;

/**
 * @author zhouhengzhe
 * @date 2022/11/15
 */
public class Blue {
}

在MainConfig中通过**@Import**注入,如下:

package com.zhz.test;

import com.zhz.bean.Person;
import com.zhz.config.MainConfig;
import com.zhz.scope.ThreadScope;
import org.junit.Test;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.util.concurrent.TimeUnit;

/**
 * @author zhouhengzhe
 * @description: todo
 * @date 2022/11/4 10:58
 * @since v1
 */
public class IOCTest {

    @SuppressWarnings("resource")
    @Test
    public void test() {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        Person person = applicationContext.getBean(Person.class);
        System.out.println(person);
//        Person person1 = applicationContext.getBean(Person.class);
//        System.out.println(person == person1);
    }

    @Test
    public void test1() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
        // 向容器中注册自定义的Scope
        beanFactory.registerScope(ThreadScope.THREAD_SCOPE, new ThreadScope());
        for (int i = 0; i < 2; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName() + ","+applicationContext.getBean("person"));
                System.out.println(Thread.currentThread().getName() + ","+applicationContext.getBean("person"));
            }).start();
        }
        try {
            TimeUnit.SECONDS.sleep(1);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    @Test
    public void test2(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        // 我们现在就来看一下IOC容器中Person这种类型的bean都有哪些
        String[] namesForType = applicationContext.getBeanNamesForType(Person.class);

        for (String name : namesForType) {
            System.out.println(name);
        }
    }

    @Test
    public void test3(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }
}

我们写一个test方法,来看一下Spring容器中的类,代码如下:

  @Test
    public void test3(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }

最后我们看一下运行结果:
在这里插入图片描述

  • 我们可以发现通过@Import注解,是可以同时导入多个Bean到Spring容器中。

5.2.2、ImportSelector,即批量导入(重点)

首先我们先看一下ImportSelector接口的源码,我们可以发现其是导入外部配置的核心接口,在SpringBoot的自动装配和@Enable(XXX)都会有用到这个接口。代码如下:

/*
 * Copyright 2002-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.context.annotation;

import java.util.function.Predicate;

import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.Nullable;

/**
 * Interface to be implemented by types that determine which @{@link Configuration}
 * class(es) should be imported based on a given selection criteria, usually one or
 * more annotation attributes.
 *
 * <p>An {@link ImportSelector} may implement any of the following
 * {@link org.springframework.beans.factory.Aware Aware} interfaces,
 * and their respective methods will be called prior to {@link #selectImports}:
 * <ul>
 * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
 * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}</li>
 * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}</li>
 * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}</li>
 * </ul>
 *
 * <p>Alternatively, the class may provide a single constructor with one or more of
 * the following supported parameter types:
 * <ul>
 * <li>{@link org.springframework.core.env.Environment Environment}</li>
 * <li>{@link org.springframework.beans.factory.BeanFactory BeanFactory}</li>
 * <li>{@link java.lang.ClassLoader ClassLoader}</li>
 * <li>{@link org.springframework.core.io.ResourceLoader ResourceLoader}</li>
 * </ul>
 *
 * <p>{@code ImportSelector} implementations are usually processed in the same way
 * as regular {@code @Import} annotations, however, it is also possible to defer
 * selection of imports until all {@code @Configuration} classes have been processed
 * (see {@link DeferredImportSelector} for details).
 *
 * @author Chris Beams
 * @author Juergen Hoeller
 * @since 3.1
 * @see DeferredImportSelector
 * @see Import
 * @see ImportBeanDefinitionRegistrar
 * @see Configuration
 */
public interface ImportSelector {

	/**
	 * Select and return the names of which class(es) should be imported based on
	 * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
	 * @return the class names, or an empty array if none
	 */
	String[] selectImports(AnnotationMetadata importingClassMetadata);

	/**
	 * Return a predicate for excluding classes from the import candidates, to be
	 * transitively applied to all classes found through this selector's imports.
	 * <p>If this predicate returns {@code true} for a given fully-qualified
	 * class name, said class will not be considered as an imported configuration
	 * class, bypassing class file loading as well as metadata introspection.
	 * @return the filter predicate for fully-qualified candidate class names
	 * of transitively imported configuration classes, or {@code null} if none
	 * @since 5.2.4
	 * 返回用于从导入候选对象中排除类的谓词,为。
	 *可传递地应用于通过此选择器的导入找到的所有类。
	 *<p>如果此谓词返回给定完全限定的。
	 *类名,所述类不会被视为导入的配置类,绕过类文件加载以及元数据自省。
	 *@返回 完全限定候选类名的筛选器谓词,在传递导入的配置类中,
	 */
	@Nullable
	default Predicate<String> getExclusionFilter() {
		return null;
	}

}

  • 主要作用是收集需要导入的配置类,selectImports()方法的返回值就是我们向Spring容器中导入的类的全类名。如果该接口的实现类同时实现EnvironmentAware,BeanFactoryAware,BeanClassLoaderAware或者ResourceLoaderAware,那么在调用其selectImports()方法之前先调用上述接口中对应的方法,如果需要在所有的@Configuration处理完再导入时,那么可以实现DeferredImportSelector接口。
  • 在ImportSelector接口的selectImports()方法中,存在一个AnnotationMetadata类型的参数,这个参数能够获取到当前标注@Import注解的类的所有注解信息,也就是说不仅能获取到@Import注解里面的信息,还能获取到其他注解的信息

5.2.2.1、上实践用例

5.2.2.1.1、首先我们创建一个类它实现了ImportSelector接口,代码如下:
package com.zhz.selecter;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

/**
 * 自定义逻辑,返回需要导入的组件
 * @author zhouhengzhe
 * @date 2022/11/16
 */
public class TestImportSelector implements ImportSelector {

    /**
     * Select and return the names of which class(es) should be imported based on
     * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
     *
     * @param importingClassMetadata 当前标注@Import注解的类的所有注解信息,也就是说不仅能获取到@Import注解里面的信息,还能获取到其他注解的信息
     * @return 导入到容器中的组件的全类名
     */
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return null;
    }
}

5.2.2.1.2、我们在MainConfig中通过@Import引入该类,代码如下:
package com.zhz.config;

import com.zhz.bean.Blue;
import com.zhz.bean.Person;
import com.zhz.bean.White;
import com.zhz.condition.AppleCondition;
import com.zhz.condition.BananaCondition;
import com.zhz.condition.WatermelonCondition;
import com.zhz.selecter.TestImportSelector;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
 * @author zhouhengzhe
 * @description: todo
 * @date 2022/11/4 10:27
 * @since v1
 */

@Configuration
@Import(value = {White.class, Blue.class, TestImportSelector.class})
public class MainConfig {

    @Bean(name = "person")
    public Person person() {
        return new Person("apple", 20);
    }

    /**
     * @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id
     */
    @Bean(name = "apple")
    @Conditional(AppleCondition.class)
    public Person apple() {
        return new Person("apple", 20);
    }

    @Bean(name = "banana")
    @Conditional(BananaCondition.class)
    public Person banana() {
        return new Person("banana", 20);
    }

    @Bean(name = "watermelon")
    @Conditional(WatermelonCondition.class)
    public Person watermelon() {
        return new Person("watermelon", 20);
    }
}
5.2.2.1.3、我们在TestImportSelector类上的selectImports()方法增加一个断点,然后我们dubug下面的测试方法

在这里插入图片描述

 @Test
    public void test3(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }
5.2.2.1.4、我们dubug之后发现,打到这个断点之前,是没有任何输出的,说明是在bean实例化前就做了,如下

在这里插入图片描述

5.2.2.1.5、然后我们再回归selectImports()方法,看一下他的参数的值,可以发现他获取到了当前标注了@Import注解的类的所有注解信息。

在这里插入图片描述

我们发现执行到下一步的时候有个空指针异常,如下
在这里插入图片描述

控制台输出也是空指针异常。
在这里插入图片描述

5.2.2.1.6、我们来研究一下为什么会空指针?

继续打开dubug,我们可以发现它后面执行到了,如下:
在这里插入图片描述

然后我们进入asSourceClasses()方法,然后它里面有一行拿了当前上一行的返回值,而上一行的返回值是null,那么null.length()自然而然就会空指针了。
在这里插入图片描述

5.2.2.1.7、防止以上空指针,代码改造

我们把继承了ImportSelector的TestImportSelector类的selectImports()改造一下返回值,返回一个空数组,代码如下:
在这里插入图片描述

然后我们再运行下面的test方法,代码如下

  @Test
    public void test3(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }

运行结果为:
在这里插入图片描述

  • 因为我们的selectImports()方法返回为一个空数组,所以自然而然就没有通过ImportSelector接口的方式注册的Bean的全类名集合输出了。
5.2.2.1.8、改造White.class,blue.class的导入方式

修改MainConfig,代码如下:

package com.zhz.config;

import com.zhz.bean.Blue;
import com.zhz.bean.Person;
import com.zhz.bean.White;
import com.zhz.condition.AppleCondition;
import com.zhz.condition.BananaCondition;
import com.zhz.condition.WatermelonCondition;
import com.zhz.selecter.TestImportSelector;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
 * @author zhouhengzhe
 * @description: todo
 * @date 2022/11/4 10:27
 * @since v1
 */

@Configuration
@Import(value = {TestImportSelector.class})
public class MainConfig {

    @Bean(name = "person")
    public Person person() {
        return new Person("apple", 20);
    }
    /**
     * @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id
     */
    @Bean(name = "apple")
    @Conditional(AppleCondition.class)
    public Person apple() {
        return new Person("apple", 20);
    }

    @Bean(name = "banana")
    @Conditional(BananaCondition.class)
    public Person banana() {
        return new Person("banana", 20);
    }

    @Bean(name = "watermelon")
    @Conditional(WatermelonCondition.class)
    public Person watermelon() {
        return new Person("watermelon", 20);
    }
}

然后我们改造一下TestImportSelector的selectImports()方法,代码如下:

package com.zhz.selecter;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

/**
 * 自定义逻辑,返回需要导入的组件
 * @author zhouhengzhe
 * @date 2022/11/16
 */
public class TestImportSelector implements ImportSelector {

    /**
     * Select and return the names of which class(es) should be imported based on
     * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
     *
     * @param importingClassMetadata 当前标注@Import注解的类的所有注解信息,也就是说不仅能获取到@Import注解里面的信息,还能获取到其他注解的信息
     * @return 导入到容器中的组件的全类名
     */
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        /**
         * 1、方法不要返回null值,否则会报空指针异常
         * 2、可以返回一个空数组
         */

        return new String[]{"com.zhz.bean.Blue","com.zhz.bean.White"};
    }
}

然后运行test方法,代码如下:


    @Test
    public void test3(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }

运行结果如下:
在这里插入图片描述

  • 因此,我们是不是可以发现Blue,White两个类也注入进来了

5.2.3、ImportBeanDefinitionRegistrar接口方式,即手工注册bean到容器中

5.2.3.1、ImportBeanDefinitionRegistrar介绍

首先我们先看其源码,代码如下

/*
 * Copyright 2002-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.context.annotation;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.type.AnnotationMetadata;

/**
 * Interface to be implemented by types that register additional bean definitions when
 * processing @{@link Configuration} classes. Useful when operating at the bean definition
 * level (as opposed to {@code @Bean} method/instance level) is desired or necessary.
 *
 * <p>Along with {@code @Configuration} and {@link ImportSelector}, classes of this type
 * may be provided to the @{@link Import} annotation (or may also be returned from an
 * {@code ImportSelector}).
 *
 * <p>An {@link ImportBeanDefinitionRegistrar} may implement any of the following
 * {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
 * methods will be called prior to {@link #registerBeanDefinitions}:
 * <ul>
 * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
 * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
 * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
 * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}
 * </ul>
 *
 * <p>Alternatively, the class may provide a single constructor with one or more of
 * the following supported parameter types:
 * <ul>
 * <li>{@link org.springframework.core.env.Environment Environment}</li>
 * <li>{@link org.springframework.beans.factory.BeanFactory BeanFactory}</li>
 * <li>{@link java.lang.ClassLoader ClassLoader}</li>
 * <li>{@link org.springframework.core.io.ResourceLoader ResourceLoader}</li>
 * </ul>
 *
 * <p>See implementations and associated unit tests for usage examples.
 *
 * @author Chris Beams
 * @author Juergen Hoeller
 * @since 3.1
 * @see Import
 * @see ImportSelector
 * @see Configuration
 */
public interface ImportBeanDefinitionRegistrar {

	/**
	 * Register bean definitions as necessary based on the given annotation metadata of
	 * the importing {@code @Configuration} class.
	 * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
	 * registered here, due to lifecycle constraints related to {@code @Configuration}
	 * class processing.
	 * <p>The default implementation delegates to
	 * {@link #registerBeanDefinitions(AnnotationMetadata, BeanDefinitionRegistry)}.
	 * @param importingClassMetadata annotation metadata of the importing class
	 * @param registry current bean definition registry
	 * @param importBeanNameGenerator the bean name generator strategy for imported beans:
	 * {@link ConfigurationClassPostProcessor#IMPORT_BEAN_NAME_GENERATOR} by default, or a
	 * user-provided one if {@link ConfigurationClassPostProcessor#setBeanNameGenerator}
	 * has been set. In the latter case, the passed-in strategy will be the same used for
	 * component scanning in the containing application context (otherwise, the default
	 * component-scan naming strategy is {@link AnnotationBeanNameGenerator#INSTANCE}).
	 * @since 5.2
	 * @see ConfigurationClassPostProcessor#IMPORT_BEAN_NAME_GENERATOR
	 * @see ConfigurationClassPostProcessor#setBeanNameGenerator
	 */
	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
			BeanNameGenerator importBeanNameGenerator) {

		registerBeanDefinitions(importingClassMetadata, registry);
	}

	/**
	 * Register bean definitions as necessary based on the given annotation metadata of
	 * the importing {@code @Configuration} class.
	 * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
	 * registered here, due to lifecycle constraints related to {@code @Configuration}
	 * class processing.
	 * <p>The default implementation is empty.
	 * @param importingClassMetadata annotation metadata of the importing class
	 * @param registry current bean definition registry
	 * 
	 * 向Spring容器中注册bean实例
	 */
	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
	}

}

  • 由上可知:ImportBeanDefinitionRegistrar本质上是一个接口。在ImportBeanDefinitionRegistrar接口中,有一个registerBeanDefinitions()方法,通过该方法,我们可以向Spring容器中注册bean实例。
  • Spring在动态注册Bean的时候,都是采用这个ImportBeanDefinitionRegistrar接口,
  • 所有实现了该接口的类都会被ConfigurationClassPostProcessor处理,ConfigurationClassPostProcessor实现了BeanFactoryPostProcessor接口,所以ImportBeanDefinitionRegistrar中动态注册的bean是优先于依赖其的bean初始化的,也能被aop、validator等机制处理。

5.2.3.2、使用方法

ImportBeanDefinitionRegistrar需要配合@Configuration和@Import这俩注解,其中,@Configuration注解定义Java格式的Spring配置文件,@Import注解导入实现了ImportBeanDefinitionRegistrar接口的类。

5.2.3.3、ImportBeanDefinitionRegistrar接口实例

1、创建一个ImportBeanDefinitionRegistrar的实现类,空实现方法registerBeanDefinitions()

package com.zhz.registrar;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

/**
 * @author zhouhengzhe
 * @date 2022/11/16
 */
public class TestImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     * AnnotationMetadata:当前类的注解信息
     * BeanDefinitionRegistry:BeanDefinition注册类
     * <p>
     * 我们可以通过调用BeanDefinitionRegistry接口中的registerBeanDefinition方法,手动注册所有需要添加到容器中的bean
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    
    }
}

2、接着我们在MainConfig中用@Import把他添加进去

package com.zhz.config;

import com.zhz.bean.Person;
import com.zhz.condition.AppleCondition;
import com.zhz.condition.BananaCondition;
import com.zhz.condition.WatermelonCondition;
import com.zhz.registrar.TestImportBeanDefinitionRegistrar;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
 * @author zhouhengzhe
 * @description: todo
 * @date 2022/11/4 10:27
 * @since v1
 */

@Configuration
@Import(value = {TestImportBeanDefinitionRegistrar.class})
public class MainConfig {

    @Bean(name = "person")
    public Person person() {
        return new Person("apple", 20);
    }

    /**
     * @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id
     */
    @Bean(name = "apple")
    @Conditional(AppleCondition.class)
    public Person apple() {
        return new Person("apple", 20);
    }

    @Bean(name = "banana")
    @Conditional(BananaCondition.class)
    public Person banana() {
        return new Person("banana", 20);
    }

    @Bean(name = "watermelon")
    @Conditional(WatermelonCondition.class)
    public Person watermelon() {
        return new Person("watermelon", 20);
    }
}

3、然后我们运行下test方法,看一下效果
在这里插入图片描述

4、接着我们把registerBeanDefinitions()方法定制一下自己的逻辑

package com.zhz.registrar;

import com.zhz.bean.Blue;
import com.zhz.bean.White;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

/**
 * @author zhouhengzhe
 * @date 2022/11/16
 */
public class TestImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     * AnnotationMetadata:当前类的注解信息
     * BeanDefinitionRegistry:BeanDefinition注册类
     * <p>
     * 我们可以通过调用BeanDefinitionRegistry接口中的registerBeanDefinition方法,手动注册所有需要添加到容器中的bean
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        RootBeanDefinition rootBeanDefinitionBlue = new RootBeanDefinition(Blue.class);
        RootBeanDefinition rootBeanDefinitionWhite = new RootBeanDefinition(White.class);

        registry.registerBeanDefinition("blue",rootBeanDefinitionBlue);
        registry.registerBeanDefinition("white",rootBeanDefinitionWhite);

    }
}

然后我们运行一下test方法,看一下效果
在这里插入图片描述

  • 是不是发现了Blue,White被注入到Spring容器中了,并且命名还是我们自己命的,当然我们也可以让Spring自己生成,换一个父类方法就可以嘞,他注册的Bean名字是全类名
package com.zhz.registrar;

import com.zhz.bean.Blue;
import com.zhz.bean.White;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

/**
 * @author zhouhengzhe
 * @date 2022/11/16
 */
public class TestImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     * AnnotationMetadata:当前类的注解信息
     * BeanDefinitionRegistry:BeanDefinition注册类
     * <p>
     * 我们可以通过调用BeanDefinitionRegistry接口中的registerBeanDefinition方法,手动注册所有需要添加到容器中的bean
     */
//    @Override
//    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//        RootBeanDefinition rootBeanDefinitionBlue = new RootBeanDefinition(Blue.class);
//        RootBeanDefinition rootBeanDefinitionWhite = new RootBeanDefinition(White.class);
//
//        registry.registerBeanDefinition("blue",rootBeanDefinitionBlue);
//        registry.registerBeanDefinition("white",rootBeanDefinitionWhite);
//
//    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
        RootBeanDefinition rootBeanDefinitionBlue = new RootBeanDefinition(Blue.class);
        RootBeanDefinition rootBeanDefinitionWhite = new RootBeanDefinition(White.class);

        registry.registerBeanDefinition(importBeanNameGenerator.generateBeanName(rootBeanDefinitionBlue,registry),rootBeanDefinitionBlue);
        registry.registerBeanDefinition(importBeanNameGenerator.generateBeanName(rootBeanDefinitionWhite,registry),rootBeanDefinitionWhite);    }
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/12103.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Ubuntu下关于cuda和cudnn 报错 现象及解决方案

详细流程&#xff1a;1、现象2、探索&#xff08;可跳过&#xff09;3、完美解决&#xff08;真实原因&#xff09;3.1 查看当前cuda版本3.2电脑有多个cuda版本&#xff0c;通过改变软链接改变指向3.3 写入系统环境&#xff1a;1、现象 啊这,重启丢失了个啥触摸的驱动&#xff…

Java并发 - (并发基础)

Java并发 - (并发基础) 1、什么是共享资源 堆是被所有线程共享的一块内存区域。在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例 Java中几乎所有的对象实例都在这里分配内存。方法区与堆一样&#xff0c;也是各个线程共享的一块内存区域&#xff0c;它用于存储已被…

Linux实用操作-----快捷键的使用(收藏系列)

推荐教程&#xff1a;Linux零基础快速入门到精通 ctrl c 强制停止 •Linux某些程序的运行&#xff0c;如果想要强制停止它&#xff0c;可以使用快捷键ctrl c •命令输入错误&#xff0c;也可以通过快捷键ctrl c&#xff0c;退出当前输入&#xff0c;重新输入 ctrl d 退出…

注意论文投稿风险,现投期刊会不会成为预警期刊呢?

中科院发布《国际预警期刊名单》 2021年12月31日&#xff0c;中国科学院文献情报中心期刊分区表团队发布《国际期刊预警名单&#xff08;试行&#xff09;》&#xff0c;用以提醒科研人员审慎选择成果发表平台&#xff0c;提示出版机构强化期刊质量管理。截至目前&#xff0c;…

Vue 实现拖拽模块(二)自定义拖拽组件位置

上文介绍了 拖拽添加组件 的简单实现&#xff0c;本文将继续给大家分享如何自定义拖拽组件位置的简单实现&#xff0c;文中通过示例代码介绍&#xff0c;感兴趣的小伙伴们可以了解一下 本文主要介绍了 Vue自定义拖拽组件位置的简单实现&#xff0c;具体如下&#xff1a; 效果图…

高空抛物检测方案设计(使用SOM进行轨迹分类)

文章目录前言一、技术方案介绍1.方案设计图2.流程介绍3.说明二、实际检测1.摄像头的安装2.实际检测三、误报解决误报事件1&#xff1a;飞鸟误报事件2&#xff1a;树叶误报事件3&#xff1a;被子解决方案轨迹展示原始轨迹SOM分类结果总结前言 高空抛物检测的关键是方案的设计&a…

MATLAB 绘图合集:分类散点图gscatter

本文主要介绍如何依据数据类别同时呈现数据散点图。 目录 说明 例子 使用默认设置绘图 使用数据类别来画图 使用多组数据类别来画图 创建并调整散点图 说明 gscatter(x,y,g) 创建 和 的散点图x&#xff0c;y按 分组g。输入x和 y是相同大小的向量。 例子 gscatter(x,y,g,c…

Jekyll 语句语法、功能的实现方法和结构介绍小手册

本文很长&#xff0c;建议使用侧边栏进行跳转。 本文虽然按照由浅入深的顺序介绍了 Jekyll 的语句语法和功能结构&#xff0c;但是主要用于快速查询一些 Jekyll 语句、功能的实现方法和结构介绍。 如果你想&#xff1a; 了解查看一些常用的 Jekyll 的相关命令和选项还请查看&…

第六章第一节:图的基本概念和存储及基本操作

文章目录1. 图的基本概念1.1 图逻辑结构的应用1.2 无向图&#xff0c;有向图1.3 简单图&#xff0c;多重图1.4 顶点的度&#xff0c;入度和出度1.5 顶点和顶点的关系描述1.6 连通图&#xff0c;强连通图1.7 研究图的局部——子图1.8 生成树1.9 生成森林1.10 边的权&#xff0c;…

手机怎么实现图片转文字操作?学会这三招就够了

随着互联网不断发展&#xff0c;我们手机的功能越来越丰富&#xff0c;现在基本上只需要一部手机&#xff0c;就能满足我们的多样化需求。比如想要提取书本中的文字&#xff0c;我们不需要再手动输入&#xff0c;只要将文字内容拍照下来&#xff0c;利用一些软件来提取图片中的…

【深度学习】argparse模块一些学习心得体会(2)| parser.parse_known_args() |位置参数 可选参数

文章目录前言一、位置参数和可选参数二、parser.parse_known_args()前言 之前我们写了一期命令行模块的使用介绍&#xff0c;但是依然有很多语法是没有介绍到的&#xff1a;比如parser.parse_known_args()这样的命令。这样的命令大量出现在各大开源项目中&#xff0c;今天我就…

外汇天眼:官网虚假宣传受到多重监管!FCA率先发出警告!

11月16日&#xff0c;英国金融行为监管局( FCA )发出最新警告&#xff0c;提示投资者警惕与 Trade Top FX 这家未经授权的公司打交道。 FCA警告称&#xff1a;Trade Top FX 在未经我们授权的情况下在英国提供金融服务或产品&#xff0c;如果一旦遭受资金损失&#xff0c;您将无…

Word处理控件Aspose.Words功能演示:在 Python 中比较两个 Word 文档

在各种情况下执行 Word 文档的比较以确定差异。各种在线工具允许您比较 Word 文档&#xff0c;但是&#xff0c;您可能需要在应用程序中实现比较功能。为实现它&#xff0c;本文展示了如何在 Python 中比较两个 Word 文档。 Aspose.Words for . Python 最新下载&#xff08;qu…

子容器在启动期间失败

​ 遇错&#xff1a; 今天遇到一个Bug&#xff1a;A child container failed during start 中文翻译为&#xff1a;子容器在启动期间失败 事情是这样的&#xff0c;一开始我在做案例的时候使用的tomcat是本地集成的&#xff0c;项目能正常运行&#xff1b; 后来我将tomcat的…

Dopamine-PEG-N3,DOPA-PEG-azide,水溶性PEG试剂供应

1、名称 英文&#xff1a;Dopamine-PEG-N3&#xff0c;DOPA-PEG-azide 中文&#xff1a;多巴胺-聚乙二醇-叠氮 2、CAS编号&#xff1a;N/A 3、所属分类&#xff1a;Azide PEG Dopamine PEG 4、分子量&#xff1a;可定制&#xff0c;有2k、5k、3.4k、10k、20k、1k 5、质量…

【salesforce】Lightning Web Component Study Log —— Part 2

持续学习中… 文章目录版本管理V1.0.01. The Lightning Web Components Model1.1 Web演变1.2 LWC1.3Aura和LWC的互用性2. Creating Lightning Web Components2.1 在VSCode定义一个LWC组件2.2 文件构成2.3 客户端-服务端体系结构2.4 基础UI组件-JS2.5 基础UI组件-HTML2.6 基础UI…

艾美捷PEG-2000 DMG解决方案

艾美捷PEG-2000 DMG英文全名1,2-dimyristoyl-rac-glycero-3-methoxypolyethylene glycol-2000&#xff0c;中文名可对应翻译为二肉豆蔻酰甘油-聚乙二醇2000。它的分子式为C122H242O50&#xff0c;分子量2509.2&#xff08;平均值&#xff09;&#xff0c;代表结构如下&#xff…

数字IC手撕代码-XX公司笔试真题(串并转换控制)

前言&#xff1a; 本专栏旨在记录高频笔面试手撕代码题&#xff0c;以备数字前端秋招&#xff0c;本专栏所有文章提供原理分析、代码及波形&#xff0c;所有代码均经过本人验证。 目录如下&#xff1a; 1.数字IC手撕代码-分频器&#xff08;任意偶数分频&#xff09; 2.数字…

JetsonNano部署yolo5 c++ onnx

编译OpenCV最新4.5.x版本 Jetson Nano自带的OpenCV版本比较低&#xff0c;Jetpack4.6对应的OpenCV版本为4.1的 而OpenCV当前最新版本已经到了4.5跟4.6了&#xff0c;4.5.x中OpenCV DNN支持了很多新的模型推理跟新的特性都无法在OpenCV4.1上演示&#xff0c;所以我决定从源码编…

Go语言 02

2.1 下载安装 Go Golang 中文网 Go 的安装也是十分的简洁、简单。就是 Next。哈哈 ~ 输入 go version 来查看是否安装完成吧 ~ 2.2 配置环境变量和工作目录 GOROOT&#xff1a;C:\Environment\Go 意味着 Go 在哪里。 在配置 GOPATH 之前&#xff0c;先把用户的 GOPATH 删掉。…