众所周知,在spring内部,所有的bean都是交由spring来统一管理的,有些bean可能是直接通过BeanDefinitionRegistry定义的,有些是通过FactoryBean注册的(一般用作其他框架与spring整合)

1、Spring引入bean组件的三种方式

引入@Import注解

实现FactoryBean 接口

以springboot为例,当一个配置类需要导入另外一个组件的时候,可以使用上面的方式进行导入。当然,spring为我们提供了一系列的注解,在引入其他组件的时候,可以使用@Import注解: 下面是Import注解的源码:

点开@Import注解源码,可以看到

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
	
	/**
	 * 除了直接在配置类上,还可以实现ImportSelector ,ImportBeanDefinitionRegistrar接口
	 * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar} 
	 */
	Class<?>[] value();

}

看了上面的源码之后,我们现在有了三种选择,分别是下面的三种引入方式

1) 直接引入源class

@Import(value = {UserInput.class})
public class MovieApplication {
	public static void main(String[] args) {
		ApplicationContext context = SpringApplication.run(MovieApplication.class, args);
	}
}

2) 实现ImportSelector接口,手动定义bean

package org.choviwu.movie.config;

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

public class MovieImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
		//注册一个字符串数组(数组格式为类的全路径),数组不能为null
        return new String[]{"org.choviwu.movie.model.Articles"};
    }
}

3) 实现ImportBeanDefinitionRegistrar接口,动态定义bean

package org.choviwu.movie.config;
 
public class MovieImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                                        BeanDefinitionRegistry registry) {
		//利用bean定义注册类注册,可以进行bean的别名设置
        boolean flagArticle = registry.containsBeanDefinition("org.choviwu.movie.model.Articles");

        if(!flagArticle) {
            BeanDefinition definition = new RootBeanDefinition(Articles.class);
            registry.registerBeanDefinition("articles",definition);
        }

    }
}

4) 实现FactoryBean接口

package org.choviwu.movie.config;

import org.choviwu.movie.model.Config;
import org.springframework.beans.factory.FactoryBean;
//这种方式适合框架与spring整合
public class MovieFactoryBean implements FactoryBean<Config> {

  //这个方法是创建定义bean,也是实际执行的方法
    @Override
    public Config getObject() throws Exception {
        Config config = new Config();
        return config;
    }
	//类的类型
    @Override
    public Class<?> getObjectType() {
        return Config.class;
    }
	//单例模式
    @Override
    public boolean isSingleton() {
        return true;
    }
}

编写main方法,执行定义的bean

package org.choviwu.movie; 

import lombok.extern.slf4j.Slf4j;
import java.util.List;

@SpringBootApplication  
//这里分别引入了上面的三种组件方式
@Import(value = {UserInput.class,
		MovieImportSelector.class,
		MovieImportBeanDefinitionRegistrar.class}) 
//
public class MovieApplication {

	public static void main(String[] args) {
		ApplicationContext context = SpringApplication.run(MovieApplication.class, args);
		UserInput us = context.getBean(UserInput.class);
		UserInput us2 = context.getBean(UserInput.class);
		Articles articles= context.getBean(Articles.class);
		//这里返回的是实际注册的对象,也就是getObject的返回值
		Object obj =  context.getBean("movieFactoryBean");
		//这里返回一个movieFactoryBean对象,为什么加&符号呢? 可以查看BeanFactory源码查看定义
		Object obj2 =  context.getBean("&movieFactoryBean");
		System.out.println(us==us2);
		System.out.println(articles);
		System.out.println(obj);
		System.out.println(obj2);
	}
	//第四种方式,实现工厂bean方式,
	@Bean
	public MovieFactoryBean movieFactoryBean(){
		return new MovieFactoryBean();
	}
 
	@Bean 
	public UserInput userInput(){
		System.out.println(".........................................啊");
		return new UserInput();
	}
}

执行main方法结果如下:

true
org.choviwu.movie.model.Articles@138aa3cc
org.choviwu.movie.model.Config@5382184b
org.choviwu.movie.config.MovieFactoryBean@36417a54