springBean注入的几种形式,你是否都知道

1.基于spring5
代码的目录结构,以下代码都是基于此:
在这里插入图片描述
其它类都加了注解除Person:

@Repository
public class OrderDao {
}
@Service
public class SampleService {
}
@Controller
public class TestController {
}

启动类SampleTest类代码:

public class SampleTest {
	@Test
	public void test01(){
		AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(SampleConfig.class);
		String[] names = app.getBeanDefinitionNames();
		for (String name : names) {
			System.out.println(name);
		}
	}
}

@Bean

这个是最简单注入方式(配合@Scope可配置单例,多例/@lazy懒加载):

@lazy懒加载

在这里插入图片描述

@Scope可配置单例在这里插入图片描述

@Configuration
public class Config {
	//给容器中注册一个bean,类型为返回值的类型
	@Bean
	public Person person(){
		return new Person();
	}
}

@ComponentScan先来说说这个scan的其它几个功能:

1.导入包下指定类:

@Configuration
@ComponentScan(value="com.demo.service",includeFilters = {
		//包含的类型,可以是注解,class,正则,自定义,等等
		@Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
},
//注意这个属性要改false(是否使用默认过滤器)
useDefaultFilters = false)
public class SampleConfig {
}

打印:
在这里插入图片描述
看一下为useDefaultFilters=true的结果:
在这里插入图片描述

注意使用includeFilters时,useDefaultFilters必需为false原因为:
org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#registerDefaultFilters此类中(追到此类的代码,可以再这个方法打断点自己看一下debug的线程调用栈能看到调用顺序):

protected void registerDefaultFilters() {
		//导入Component标注的类
		this.includeFilters.add(new AnnotationTypeFilter(Component.class));
		ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
		try {
			this.includeFilters.add(new AnnotationTypeFilter(
					((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
			logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
		}
		catch (ClassNotFoundException ex) {
			// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
		}
		try {
			this.includeFilters.add(new AnnotationTypeFilter(
					((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
			logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
		}
		catch (ClassNotFoundException ex) {
			// JSR-330 API not available - simply skip.
		}
	}

@service/@controller/@Repository都继承了Component所以会

2.排除包下部分类或指定类:

@Configuration
@ComponentScan(value="com.demo",excludeFilters = {
		//排除的类型,可以是注解,class,正则,自定义,等等
		@Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
},useDefaultFilters = true)
public class SampleConfig {
}

打印结果(此时useDefaultFilters就可以为true了):
在这里插入图片描述

3.自定义导入或排除

@Configuration
@ComponentScan(value="com.demo",excludeFilters = {
		//包含的类型,可以是注解,class,正则,自定义,等等
		@Filter(type = FilterType.CUSTOM,classes = {CustomTypeFilter.class})
},useDefaultFilters = false)
public class SampleConfig {

}

/**
*在此类的此match方法中写自己的逻辑,返回true或false代表是否满足条件
*/
public class CustomTypeFilter implements TypeFilter {
	/**
	 * @param metadataReader 读取当前正在扫描的类信息
	 * @param 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);
		return false;
	}
}

@Conditional条件注入

public class WinCondition implements Condition {
	/**
	 * @param context 判断条件可以使用的上下文(环境)
	 * @param metadata 注解信息
	 */
	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		//能获取到IOC容器正在使用的BeanFactory
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		//获取当前环境变量(包括操作系统的类型)
		Environment environment = context.getEnvironment();
		String property = environment.getProperty("os.name");
		if(property.contains("Windows")) return true;
		return false;
	}
}

@Configuration
public class SampleConfig {
	@Conditional(WinCondition.class)
	@Bean
	public Person person(){
		return new Person();
	}
}

返回:
在这里插入图片描述
如果想为不为windows自己改一下启动的注入参数即可测试。

@import[快速给容器注入一个组件]相对于@bean有点简单

1.注入的bean的id为全类名(带包的)

@Configuration
@Import(value={Person.class})
public class SampleConfig {
}

返回:
在这里插入图片描述

2.ImportSelector:是一个接口,返回需要导入的全类名数组

public class SampleImportSelector implements ImportSelector {
	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		//这里写逻辑,返回需要的class全类名数组,切记不可返回null不然会报异常
		return new String[]{"com.demo.model.demo3.model.Person"};
	}
}

@Configuration
@Import(value={SampleImportSelector.class})
public class SampleConfig {
}

此org.springframework.context.annotation.ConfigurationClassParser#processImports代码没有做null校验:

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, boolean checkForCircularImports) {

		if (importCandidates.isEmpty()) {
			return;
		}

		if (checkForCircularImports && isChainedImportOnStack(configClass)) {
			this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
		}
		else {
			this.importStack.push(configClass);
			try {
				for (SourceClass candidate : importCandidates) {
					if (candidate.isAssignable(ImportSelector.class)) {
						// Candidate class is an ImportSelector -> delegate to it to determine imports
						Class<?> candidateClass = candidate.loadClass();
						ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
						ParserStrategyUtils.invokeAwareMethods(
								selector, this.environment, this.resourceLoader, this.registry);
						if (selector instanceof DeferredImportSelector) {
							this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
						}
						else {
							String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
							//这里没有做null的校验,但可对空数组做处理
							Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
							processImports(configClass, currentSourceClass, importSourceClasses, false);
						}
					}
					else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
						// Candidate class is an ImportBeanDefinitionRegistrar ->
						// delegate to it to register additional bean definitions
						Class<?> candidateClass = candidate.loadClass();
						ImportBeanDefinitionRegistrar registrar =
								BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
						ParserStrategyUtils.invokeAwareMethods(
								registrar, this.environment, this.resourceLoader, this.registry);
						configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
					}
					else {
						// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
						// process it as an @Configuration class
						this.importStack.registerImport(
								currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
						processConfigurationClass(candidate.asConfigClass(configClass));
					}
				}
			}
			catch (BeanDefinitionStoreException ex) {
				throw ex;
			}
			catch (Throwable ex) {
				throw new BeanDefinitionStoreException(
						"Failed to process import candidates for configuration class [" +
						configClass.getMetadata().getClassName() + "]", ex);
			}
			finally {
				this.importStack.pop();
			}
		}
	}

3.ImportBeanDefinitionRegistrar是一个接口,用于自身业务特定需求(可对Bean的BeanDefinition进行定义)

public class SampleImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
	/**
	 * @param importingClassMetadata 当前类的注解信息
	 * @param registry BeanDefinition注册类
	 *                 把所有需要添加到容器的bean加入
	 */
	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		boolean b = registry.containsBeanDefinition("com.demo.model.demo3.model.Pig");
		boolean b1 = registry.containsBeanDefinition("com.demo.model.demo3.model.Person");
		//如果b和b1同时存在于IOC容器中时,给bean进行封装
		if(b && b1){
			RootBeanDefinition beanDefinition = new RootBeanDefinition(Dog.class);
			registry.registerBeanDefinition("dog",beanDefinition);
		}
	}
}

@Configuration
@Import(value={SampleImportSelector.class, SampleImportBeanDefinitionRegistrar.class})
public class SampleConfig {
}

FactoryBean接口

1.FactoryBean与BeanFactory的区别?

	FactoryBean: 是生成java对象,把对象转为spring管理的bean
	BeanFacotry: 从spring管理的bean中取出实例bean

2.用法

public class SampleFactoryBean implements FactoryBean<Person> {
	//返回实例bean
	@Override
	public Person getObject() throws Exception {
		return new Person();
	}
	//返回class
	@Override
	public Class<?> getObjectType() {
		return Person.class;
	}
	//是否单例
	public  boolean isSingleton() {
		return true;
	}
}

@Configuration
public class SampleConfig {
	@Bean
	public SampleFactoryBean sampleFactoryBean(){
		return new SampleFactoryBean();
	}
}

public class SampleTest {
	@Test
	public void test01(){
		AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(SampleConfig.class);
		String[] names = app.getBeanDefinitionNames();

		//factoryBean正常直接取,取的是Person
		Object bean = app.getBean("sampleFactoryBean");
		System.out.println(bean);
		//factoryBean在取值方面有说法:beanName前加"&"取的是sampleFactoryBean
		Object bean2 = app.getBean("&sampleFactoryBean");
		System.out.println(bean2);

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

在这里插入图片描述
注意实FactoryBean在getBean时要注意“&”的用法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值