向Spring容器中注册组件的几种方式

一、引言

在SpringBoot还没有普及之前,我们创建Spring+SpringMVC项目的时候,首先要配置web.xml、Spring.xml、SpringMVC.xml三个配置文件。对于下面的包扫描配置,就是把该包下的带有@Controller、@Service、@Repository、@Component注解的组件注册到Spring容器中,在该标签中,还有include-filter、exclude-filter属性。

<context:component-scan base-package="com.scorpios"></context:component-scan>

在SpringBoot中,我们用@Configuration注解配置类代替了xml文件,用@ComponentScan注解代替了context:component-scan标签,它们的意思等价。

在介绍向容器中注册组件之前,我们先来介绍一下include-filter中的自定义规则


//excludeFilters = Filter[] :指定扫描的时候按照什么规则 排除哪些组件
//includeFilters = Filter[] :指定扫描的时候 只需要包含 哪些组件
//FilterType.ANNOTATION:按照注解
//FilterType.ASSIGNABLE_TYPE:按照给定的类型
//FilterType.CUSTOM:使用自定义规则

@Configuration
//@ComponentScan  value:指定要扫描的包
@ComponentScan(value="com.scorpios",includeFilters = {
	// @Filter(type=FilterType.ANNOTATION,classes={Controller.class}), 
	// @Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}),
	@Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})},
	useDefaultFilters = false)	
public class MainConfig {
	
	// 给容器中注册一个Bean;  类型为返回值的类型,id默认是用方法名作为id
	@Bean("person")
	public Person person(){
		return new Person("lisi", 20);
	}
}

这里需要注意的是:在使用只扫描特定includeFilters属性时,要把useDefaultFilters设置为false。下面重点看一下自定义规则MyTypeFilter。

public class MyTypeFilter implements TypeFilter {

	/**
	 * metadataReader:读取到的,当前正在扫描的类的信息
	 * metadataReaderFactory:可以获取到其他任何类信息的
	 */
	@Override
	public boolean match(MetadataReader metadataReader, 
							MetadataReaderFactory metadataReaderFactory)
								throws IOException {
		//获取当前类注解的信息
		AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
		//获取当前正在扫描的类的类信息
		ClassMetadata classMetadata = metadataReader.getClassMetadata();
		//获取当前类资源(类的路径)
		Resource resource = metadataReader.getResource();
		String className = classMetadata.getClassName();
		System.out.println("--->"+className);
		// 如果类名中包含er,则加入到Spring容器中,否则不加入容器
		if(className.contains("er")){  
			return true;
		}
		return false;
	}
}

二、向Spring容器中注册组件的四种方式

1、包扫描+组件标注注解

这种方式是我们最常使用的方式,对我们自己写的组件,只需要加上下面四类注解即可,@Controller、@Service、@Repository、@Component。这里就不做过多介绍。

2、@Bean

这种方式,在SpringBoot中也最经常使用,无论是自己定义的,还是第三方的组件(比如数据源的配置),都可以直接使用该注解。这里需要注意的是:@Bean修饰方法,类型为返回值的类型,容器中组件的id默认是用方法名作为id。这里也不做过多的介绍。

3、@Import——快速给容器中导入一个组件

已知一个Color类,若要把这个类注入到Spring容器中,除了上面的两种方式,还有哪些方式呢?

public class Color {
	String color;
}
(1)、@Import(要导入到容器中的组件):容器中就会自动注册这个组件,id默认是全类名;

如果想把Color注册到Spring容器中,第一种方式,使用@Import注解,只需要做如下配置即可:

@Configuration
@Import({Color.class})
public class MainConfig{
}
(2)、ImportSelector:返回需要导入的组件的全类名数组;

如果想把Color注册到Spring容器中,第二种方式,使用ImportSelector接口,需要做如下配置:

@Configuration
@Import({MyImportSelector.class}))
public class MainConfig{
}
public class MyImportSelector implements ImportSelector {

	//返回值,就是到导入到容器中的组件全类名
	//AnnotationMetadata:当前标注@Import注解的类的所有注解信息
	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		//方法不要返回null值
		return new String[]{"com.scorpios.bean.Color","com.scorpios.bean.Blue"};
	}
}

selectImports返回类的全类名数组,就会把返回的类全都放入Spring容器中。

(3)、ImportBeanDefinitionRegistrar:手动注册bean到容器中

如果想把Color注册到Spring容器中,第三种方式,使用ImportBeanDefinitionRegistrar接口,需要做如下配置:

扫描二维码关注公众号,回复: 9196763 查看本文章
@Configuration
@Import({MyImportBeanDefinitionRegistrar.class}))
public class MainConfig{
}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

	/**
	 * AnnotationMetadata:当前类的注解信息
	 * BeanDefinitionRegistry:BeanDefinition注册类;
	 * 	把所有需要添加到容器中的bean;
	 * 调用BeanDefinitionRegistry.registerBeanDefinition手工注册进来
	 */
	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, 
												BeanDefinitionRegistry registry) {
		
		boolean definition = registry.containsBeanDefinition("com.scorpios.bean.Color");
		boolean definition2 = registry.containsBeanDefinition("com.scorpios.bean.Blue");
		if(definition && definition2){
			//指定Bean定义信息;(Bean的类型)
			RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
			//注册一个Bean,指定bean名
			registry.registerBeanDefinition("rainBow", beanDefinition);
		}
	}
}
4、使用Spring提供的 FactoryBean(工厂Bean);

默认获取到的是工厂bean调用getObject创建的对象。

//创建一个Spring定义的FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {

	//返回一个Color对象,这个对象会添加到容器中
	@Override
	public Color getObject() throws Exception {
		System.out.println("ColorFactoryBean...getObject...");
		return new Color();
	}

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

	//true:这个bean是单实例,在容器中保存一份
	//false:多实例,每次获取都会创建一个新的bean;
	@Override
	public boolean isSingleton() {
		return false;
	}
}

三、补充

@Conditional注解
作用:按照一定的条件进行判断,满足条件给容器中注册Bean 。自定义的条件类需要实现Condition接口,并实现其中的mathc()方法,可作用于类或者方法上
如果当前操作系统是Windows,会导入这个WindowsCondition这个组件,反之,不会导入这个组件。

// 类中组件统一设置。满足当前条件,这个类中配置的所有bean注册才能生效;
@Conditional({WindowsCondition.class})
@Configuration
public class MainConfig {

	@Bean("bill")
	public Person person(){
		return new Person("Bill Gates",62);
	}
}
//判断是否windows系统
public class WindowsCondition implements Condition {

	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		Environment environment = context.getEnvironment();
		String property = environment.getProperty("os.name");
		if(property.contains("Windows")){
			return true;
		}
		return false;
	}

}
发布了123 篇原创文章 · 获赞 230 · 访问量 19万+

猜你喜欢

转载自blog.csdn.net/zxd1435513775/article/details/100625879