导入组件的方式:

  • @Component + @ComponentScan 搭配使用

  • @Bean

  • @Import

    • ImportSelector
    • ImportBeanDefinitionRegistry
  • FactoryBean 接口

    对于该类型的 Bean 对象,Spring 框架的 getBean() 方法有特殊处理。默认情况下获取该 FactoryBean 的 getObject() 方法注入的 Bean 对象,使用 & 前缀才能获取该 FactoryBean 对象本身。

@Import导入组件

@Import注解和@Bean注解导入组件的功能没有本质区别

  • @Bean注解可以自定义一些属性, bean对象的默认id为方法名
  • @Import注解导入一个无参构造器生成的对象, bean对象的默认id为全类名

配置类

package com.xiong.config;

import com.xiong.bean.User;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

//通过@Import注解导入User类对象
@Import({User.class})
@Configuration
public class UserConfig {

}

测试类

package com.xiong;

import com.xiong.bean.Person;
import com.xiong.bean.User;
import com.xiong.config.PersonConfig;
import com.xiong.config.UserConfig;
import com.xiong.service.PersonService;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainTest {

    @Test
    public void importTest() {
        ApplicationContext context = new AnnotationConfigApplicationContext(UserConfig.class);
        String[] userNames = context.getBeanNamesForType(User.class);
        for (String userName : userNames) {
            User user = context.getBean(userName, User.class);
            System.out.println(userName + ": " + user);
        }
    }
}

实体类: 模拟第三方包

不添加空参构造器使其报错

package com.xiong.bean;

import lombok.AllArgsConstructor;
import lombok.Data;

//不存在空参构造器
@Data
@AllArgsConstructor
public class User {
    private String name;
    private Integer age;
}

image-20221118113042418

添加空参构造器

package com.xiong.bean;

import lombok.AllArgsConstructor;
import lombok.Data;


@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private String name;
    private Integer age;
}

image-20221118113505906

ImportSelector自定义导入器

可以根据规则批量导入, 避免@Import单个导入.

导入的组件的id默认为全类名, 不可以自定义修改

自定义导入器类

package com.xiong.selector;

import com.xiong.bean.Person;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

import java.util.ArrayList;
import java.util.List;

public class XImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        List<String> importingClass = new ArrayList<>();
        // 自定义导入器中添加Person类
        importingClass.add(Person.class.getName());

        // List<String> => String[] 的写法
        return importingClass.toArray(new String[0]);
    }
}

配置类

package com.xiong.config;

import com.xiong.bean.User;
import com.xiong.selector.XImportSelector;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

//使用自定义导入器
@Import({User.class, XImportSelector.class})
@Configuration
public class UserConfig {

}

测试类

package com.xiong;


import com.xiong.bean.Person;
import com.xiong.bean.User;
import com.xiong.config.PersonConfig;
import com.xiong.config.UserConfig;
import com.xiong.service.PersonService;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainTest {
    @Test
    public void importSelectorTest() {
        ApplicationContext context = new AnnotationConfigApplicationContext(UserConfig.class);
        //这里通过importSelector导入person对象
        String[] personNames = context.getBeanNamesForType(Person.class);
        for (String personName : personNames) {
            Person person = context.getBean(personName, Person.class);
            System.out.println(personName + ": " + person);
        }
    }
}

ImportBeanDefinitionRegistrar自定义导入

ImportSelector不能够定义导入的bean对象的id, 而使用ImportBeanDefinitionRegistrar可以自定义

自定义组件注册类

package com.xiong.registrar;

import com.xiong.bean.Customer;
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;

public class XImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        RootBeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClass(Customer.class);
        //设置该bean对象的id为customer
        registry.registerBeanDefinition("customer", beanDefinition);
    }
}

配置类

package com.xiong.config;

import com.xiong.bean.User;
import com.xiong.registrar.XImportBeanDefinitionRegistrar;
import com.xiong.selector.XImportSelector;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Import({User.class, XImportSelector.class, XImportBeanDefinitionRegistrar.class})
@Configuration
public class UserConfig {

}

测试类

package com.xiong;


import com.xiong.bean.Person;
import com.xiong.bean.User;
import com.xiong.config.PersonConfig;
import com.xiong.config.UserConfig;
import com.xiong.service.PersonService;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainTest {
    @Test
    public void importBeanDefinitionRegistrarTest() {
        ApplicationContext context = new AnnotationConfigApplicationContext(UserConfig.class);
        Customer customer = context.getBean("customer", Customer.class);
        System.out.println(customer);
    }
}

实验结果

image-20221118123639246

通过 FactoryBean导入组件

容器中注入的 Bean 的类型如果是 FactoryBean,那么在获取 FactoryBean 对象时,默认会调用 FactoryBean 对象的 getObject() 方法。

FactoryBean接口实现类

package com.xiong.bean;

import org.springframework.beans.factory.FactoryBean;

public class XFctoryBean implements FactoryBean<Consumer> {
    @Override
    public Consumer getObject() throws Exception {
        return new Consumer("consumer", 18);
    }

    @Override
    public Class<?> getObjectType() {
        return Consumer.class;
    }

    /**
     * 返回该对象是否是单实例
     *
     * @return true表示是单实例, 容器中只保存一份;
     * false表示是多实例, 每次调用都会创建一个bean对象
     */
    @Override
    public boolean isSingleton() {
        return true;
    }
}

配置类

package com.xiong.config;

import com.xiong.bean.XFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FactoryBeanConfig {

    //将FactoryBean实现类作为bean对象注入, 默认会通过委托模式获取该FactoryBean内部的getObject()
    @Bean
    XFactoryBean xFactoryBean() {
        return new XFactoryBean();
    }
}

测试类

package com.xiong;


import com.xiong.bean.Person;
import com.xiong.bean.User;
import com.xiong.config.PersonConfig;
import com.xiong.config.UserConfig;
import com.xiong.service.PersonService;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainTest {
    @Test
    public void factoryBeanTest() {
        ApplicationContext context = new AnnotationConfigApplicationContext(FactoryBeanConfig.class);
        //FactoryBean默认会获取getObject()返回的对象
        Object bean = context.getBean("xFactoryBean");
        System.out.println(bean.getClass().getName());

        //如果想要获得FactoryBean对象本身, 使用&前缀
        Object xFactoryBean = context.getBean("&xFactoryBean");
        System.out.println(xFactoryBean.getClass().getName());

        Consumer consumer = context.getBean("xFactoryBean", Consumer.class);
        System.out.println(consumer);

        //由于在XFactoryBean中设置isSingleton为true, 所以返回单实例bean对象, 因此bean和consumer是同一个对象
        System.out.println(bean == consumer);
    }
}

实验结果

image-20221118131056449

拓展学习

AnnotationMetadata类

获取当前类的注解信息

todo

BeanDefinitionRegistry类

通过registerBeanDefinition()可以手动的向容器中注册bean对象, 这种方式可以自定义bean对象的名字

todo

RootBeanDefinition 类


   转载规则


《》 熊水斌 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
从文件到Mapper 输入分片InputSplit@InterfaceAudience.Public @InterfaceStability.Stable public abstract class InputSplit { //
2022-11-18
下一篇 
Source -> [PUT事务] -> Channel <- [TAKE事务] Sink Source端 推送 到Channel中 Sink端 主动拉取 Channel中的数据 Flume进阶学习中断 http
2022-11-17
  目录