将Bean导入Spring容器的方法

关于Spring的编译可以看我之前写的文章,该文展示了在Mac下编译Spring 5的流程

本文内容主要基于Spring的注解开发

如何将Bean导入容器

首先准备一个POJO:

public class User {
    private String name;

    public User(String name) {
        this.name = name;
    }
}

接着,一般使用Spring我们需要准备一份配置文件,但是基于注解开发就不需要了。只要准备一个注解配置类即可:

@Configuration
public class BeanConfig {
    @Bean
    public User user(){
        return new User("Q");
    }
}

将配置类传入AnnotationConfigApplicationContext类:

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig.class);

这样,我们就获得了Spring的容器抽象接口ApplicationContext

想要从容器中获取被Spring接管的Bean,我们可以调用ApplicationContext#getBean

Object user = applicationContext.getBean("user");

好样的,恭喜你成功已经学会了Spring框架的使用。

@Bean

通过第一个例子可以看出,在@Configuration注解标注的配置类中可以通过@Bean注解标注方法进而将方法返回值导入容器。

@ComponentScan

批量导入,@ComponentScan注解也是一个不错的选择,它可以将指定包下的所有@Component @Service @Repository @Controller 注解标注的类加入容器,这里就不多说了。

@Import

@Import注解主要提供配置类导入的功能。

准备一个配置类:

@Configuration
public class ForImport {
    @Bean
    public User user(){
        return new User("Q");
    }
}

使用@Import导入该配置:

@Import(ForImport.class)
public class ImportConfig {
}

测试:

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ImportConfig.class);
        Object user = applicationContext.getBean("user");
        System.out.println(user);

结果:

User{name='Q'}

这只是其中一种用法。

@Import注解还可以直接将普通类作为Bean导入到容器。

准备一个类:

public class Dog {
}

修改之前的ImportConfig类:

@Import({ForImport.class, Dog.class})
public class ImportConfig {
}

测试:

    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ImportConfig.class);
        printBeans(applicationContext);
    }

    public static void printBeans(ApplicationContext applicationContext) {
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }

结果:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
importConfig
top.hellooooo.blog.config.ForImport
user
top.hellooooo.blog.bean.Dog

可以看到,通过@Import导入的类作为Bean在容器中的名字是类的全限定路径加上类名。

你以为这就是@Import的全部功能了吗?Too young,too naive

打开Spring关于Import注解的接口源码

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

除去Configuration,还有ImportSelector以及ImportBeanDefinitionRegistrar

ImportSelector

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);

一目了然,将返回的字符串代表的类导入。

下面实现我们自己的ImportSelector

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"top.hellooooo.blog.bean.Dog"};
    }
}

修改ImportConfig

@Import({ForImport.class, MyImportSelector.class})
public class ImportConfig {
}

结果:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
importConfig
top.hellooooo.blog.config.ForImport
user
top.hellooooo.blog.bean.Dog

ImportBeanDefinitionRegistrar

Import注解源码里出现过ImportBeanDefinitionRegistrar,一看这名字就懂了,肯定是直接将BeanDefinition导入容器。

这是ImportBeanDefinitionRegistrar的一个默认方法

    default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
            BeanNameGenerator importBeanNameGenerator) {
        registerBeanDefinitions(importingClassMetadata, registry);
    }

AnnotationMetadata接口可以获取@Import类上的所有注解信息,BeanDefinitionRegistry接口则用来注册Bean。

来一个例子:

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        Set annotationTypes = importingClassMetadata.getAnnotationTypes();
        for (String annotationType : annotationTypes) {
            System.out.println("---->" + annotationType);
        }
        BeanDefinition dog = new RootBeanDefinition(Dog.class);
        registry.registerBeanDefinition("dog", dog);
    }
}

同样,修改ImportConfig

@Import({ForImport.class, MyImportBeanDefinitionRegistrar.class})
public class ImportConfig {
}

结果:

---->org.springframework.context.annotation.Import
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
importConfig
top.hellooooo.blog.config.ForImport
user
dog

嗯,不错。

不过,针对复杂类的导入,Spring还提供了FactoryBean接口,我们来看看。

FactoryBean

FactoryBean总共含有3个方法:

    public Cat getObject() throws Exception;

    public Class getObjectType();

    default boolean isSingleton() {
        return true;
    }

getObject返回实例(返回此工厂创建的对象的实例。实例是否可以共享,具体取决于该工厂是否返回单例或原型。),getObjectType返回由getObject()方法返回的对象类型;如果类型未知,则返回null。

就不举例了,获取FactoryBean生成的Bean和普通Bean一样,getBean即可,如果要获取FactoryBean本身的话,传入getBean的字符串前加上"&"即可。

"&"在BeanFactory中被定义:

  String FACTORY_BEAN_PREFIX = "&";

参考

  1. Spring——The IoC Container
  2. Spring源码解析

你可能感兴趣的:(将Bean导入Spring容器的方法)