ImportBeanDefinitionRegistrar手動控制BeanDefinition創(chuàng)建注冊詳解
一、什么是ImportBeanDefinitionRegistrar
ImportBeanDefinitionRegistrar接口是也是spring的擴展點之一,ImportBeanDefinitionRegistrar類只能通過其他類 @Import的方式來加載,通常是啟動類或配置類。它可以支持我們自己寫的代碼封裝成BeanDefinition對象;實現此接口的類會回調postProcessBeanDefinitionRegistry方法,注冊到spring容器中。
把bean注入到spring容器不止有 @Service @Component等注解方式;還可以實現此接口,這種方式是最靈活的,能在registerBeanDefinitions方法中獲取到BeanDefinitionRegistry容器注冊對象,可以手動控制BeanDefinition的創(chuàng)建和注冊。
public interface ImportBeanDefinitionRegistrar {
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
this.registerBeanDefinitions(importingClassMetadata, registry);
}
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
}
二、ImportBeanDefinitionRegistrar使用很簡單
加載指定類
定義一個類TestImportBeanDefinitionRegistrar實現ImportBeanDefinitionRegistrar接口,在配置類TestConfiguration加上注解@Import一個TestImportBeanDefinitionRegistrar實現類,重寫registerBeanDefinitions方法,手動注冊bean,實現注冊BeanDefinition到容器中,也可以實現一些Aware接口,以便獲取Spring的一些數據。加載指定DataSource類如下:
public class TestImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar,ResourceLoaderAware,BeanFactoryAware {
private static ResourceLoader resourceLoader;
private static BeanFactory beanFactory;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//創(chuàng)建DataSourceBean
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(DataSource.class);
beanDefinition.setSynthetic(true);
MutablePropertyValues mpv = beanDefinition.getPropertyValues();
//spring名稱約定為defaultTargetDataSource和targetDataSources
mpv.addPropertyValue("defaultTargetDataSource", defaultTargetDataSource);
mpv.addPropertyValue("targetDataSources", DataSourceSet.getTargetDataSourcesMap());
beanDefinitionRegistry.registerBeanDefinition("dataSource", beanDefinition);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory=beanFactory;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader=resourceLoader;
}
}
@Import(TestImportBeanDefinitionRegistrar.class)
@Configuration
public class TestConfiguration {
}
這樣就注冊了一個bean名字是dataSource,有兩個屬性分別是defaultTargetDataSource和targetDataSources。那么使用這個類直接用 @Autowired和@Resource注入即可。
加載掃描器類
如果我們并不知道需要register哪些bean。這里我們還需要借助一個掃描器類ClassPathBeanDefinitionScanner,通過掃描器獲取我們需要注冊的bean。首先創(chuàng)建一個@Mapper注解,再新建一個CountryMapper類,使用該Mapper注解。
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public @interface Mapper {
}
@Mapper
public class CountryMapper {
}
創(chuàng)建一個MyClassPathBeanDefinitionScanner類繼承ClassPathBeanDefinitionScanner,掃描使用@Mapper的注解的類,ClassPathBeanDefinitionScanner又繼承ClassPathScanningCandidateComponentProvider類,ClassPathScanningCandidateComponentProvider中有兩個TypeFilter集合,includeFilters、excludeFilters。滿足任意includeFilters會被加載,同樣的滿足任意excludeFilters不會被加載。
@Slf4j
public class MyClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner{
public MyClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
super(registry, useDefaultFilters);
}
protected void registerFilters() {
addIncludeFilter(new AnnotationTypeFilter(Mapper.class));
}
@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
return super.doScan(basePackages);
}
}
registerFilters()方法
核心代碼就是registerFilters()方法,然后在我們的ImportBeanDefinitionRegistrar實現類中調用:
public class MapperAutoConfiguredMyBatisRegistrar
implements ImportBeanDefinitionRegistrar{
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
MyClassPathBeanDefinitionScanner scanner = new MyClassPathBeanDefinitionScanner(registry, false);
scanner.setResourceLoader(resourceLoader);
scanner.registerFilters();
scanner.doScan("com.faderw.school.domain");
}
}
這里我們自定義帶有@Mapper注解的類CountryMapper就被注入到IOC容器了,可以直接使用噢。
三、ImportBeanDefinitionRegistrar原理
TestImportBeanDefinitionRegistrar是被TestConfiguration類import導入的,所以要加載到TestImportBeanDefinitionRegistrar必然要先加載TestConfiguration才行;所以先看Configuration的代碼,找到ConfigurationClassPostProcessor類,它實現了BeanDefinitionRegistryPostProcessor接口。
Map<String, BeanDefinitionRegistryPostProcessor> beanMap =
beanFactory.getBeansOfType(BeanDefinitionRegistryPostProcessor.class, true, false);
List<BeanDefinitionRegistryPostProcessor> registryPostProcessorBeans =
new ArrayList<BeanDefinitionRegistryPostProcessor>(beanMap.values());
OrderComparator.sort(registryPostProcessorBeans);
for (BeanDefinitionRegistryPostProcessor postProcessor : registryPostProcessorBeans) {
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
從spring容器中拿到實現了BeanDefinitionRegistryPostProcessor接口的類。拿到后執(zhí)行對應的postProcessBeanDefinitionRegistry方法:
//從invokeBeanFactoryPostProcessors方法調過來
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
RootBeanDefinition iabpp = new RootBeanDefinition(ImportAwareBeanPostProcessor.class);
iabpp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(IMPORT_AWARE_PROCESSOR_BEAN_NAME, iabpp);
//取得registry的id并做判重處理或記錄
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called for this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called for this post-processor against " + registry);
}
//保存處理過的registry,避免重復處理
this.registriesPostProcessed.add(registryId);
//處理java配置形式的bean定義
processConfigBeanDefinitions(registry);
}
最后調用了processConfigBeanDefinitions方法處理java配置形式的bean定義:
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
Set<BeanDefinitionHolder> configCandidates = new LinkedHashSet<BeanDefinitionHolder>();
//加載當前已知所有bean定義
for (String beanName : registry.getBeanDefinitionNames()) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
// 判斷對應bean是否為配置類,如果是,則加入到configCandidates
// @Configuration, @Component, @ComponentScan, @Import, @Bean等注解
if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
//如果找不到@configuration類,則立即返回
if (configCandidates.isEmpty()) {
return;
}
...........
// 實例化ConfigurationClassParser 為了解析 各個配置類
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
System.out.println("ConfigurationClassPostProcessor bd " + bd.getBeanClassName());
try {
if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parser.parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parser.parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("Failed to load bean class: " + bd.getBeanClassName(), ex);
}
}
parser.validate();
......
ConfigurationClassParser#parse方法:
public void parse(String className, String beanName) throws IOException {
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
processConfigurationClass(new ConfigurationClass(reader, beanName));
}
processConfigurationClass方法:
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
...................
do {
//重點
metadata = doProcessConfigurationClass(configClass, metadata);
}
while (metadata != null);
.................
}
doProcessConfigurationClass方法:
protected AnnotationMetadata doProcessConfigurationClass(ConfigurationClass configClass, AnnotationMetadata metadata) throws IOException {
// Recursively process any member (nested) classes first
processMemberClasses(metadata);
// Process any @PropertySource annotations
//處理@PropertySource 加載外面資源文件
AnnotationAttributes propertySource = MetadataUtils.attributesFor(metadata,
org.springframework.context.annotation.PropertySource.class);
if (propertySource != null) {
processPropertySource(propertySource);
}
// Process any @ComponentScan annotations
//處理 @ComponentScan 掃描包
AnnotationAttributes componentScan = MetadataUtils.attributesFor(metadata, ComponentScan.class);
if (componentScan != null) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, metadata.getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if necessary
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {
this.parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
}
}
}
//處理@Import注解
// Process any @Import annotations
Set<Object> imports = new LinkedHashSet<Object>();
Set<String> visited = new LinkedHashSet<String>();
collectImports(metadata, imports, visited);
if (!imports.isEmpty()) {
processImport(configClass, metadata, imports, true);
}
// Process any @ImportResource annotations
if (metadata.isAnnotated(ImportResource.class.getName())) {
AnnotationAttributes importResource = MetadataUtils.attributesFor(metadata, ImportResource.class);
String[] resources = importResource.getStringArray("value");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// Process individual @Bean methods
//處理@Bean注解
Set<MethodMetadata> beanMethods = metadata.getAnnotatedMethods(Bean.class.getName());
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process superclass, if any
if (metadata.hasSuperClass()) {
String superclass = metadata.getSuperClassName();
if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// superclass found, return its annotation metadata and recurse
if (metadata instanceof StandardAnnotationMetadata) {
Class<?> clazz = ((StandardAnnotationMetadata) metadata).getIntrospectedClass();
return new StandardAnnotationMetadata(clazz.getSuperclass(), true);
}
else {
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(superclass);
return reader.getAnnotationMetadata();
}
}
}
// No superclass -> processing is complete
return null;
}
processImport方法:
private void processImport(ConfigurationClass configClass, AnnotationMetadata metadata,
Collection<?> classesToImport, boolean checkForCircularImports) throws IOException {
if (checkForCircularImports && this.importStack.contains(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack, configClass.getMetadata()));
}else {
this.importStack.push(configClass);
try {
for (Object candidate : classesToImport) {
Object candidateToCheck = (candidate instanceof Class ? (Class) candidate :
this.metadataReaderFactory.getMetadataReader((String) candidate));
//實現ImportSelector接口的處理
if (checkAssignability(ImportSelector.class, candidateToCheck)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = (candidate instanceof Class ? (Class) candidate :
this.resourceLoader.getClassLoader().loadClass((String) candidate));
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
processImport(configClass, metadata, Arrays.asList(selector.selectImports(metadata)), false);
}
//實現ImportBeanDefinitionRegistrar接口的處理
else if (checkAssignability(ImportBeanDefinitionRegistrar.class, candidateToCheck)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = (candidate instanceof Class ? (Class) candidate :
this.resourceLoader.getClassLoader().loadClass((String) candidate));
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
invokeAwareMethods(registrar);
//調用該類的registerBeanDefinitions方法
registrar.registerBeanDefinitions(metadata, this.registry);
}
else {
//候選類不是importSelector或importBeanDefinitionRegistrar
//@Configuration類的處理
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as a @Configuration class
this.importStack.registerImport(metadata,
(candidate instanceof Class ? ((Class) candidate).getName() : (String) candidate));
processConfigurationClass(candidateToCheck instanceof Class ?
new ConfigurationClass((Class) candidateToCheck, true) :
new ConfigurationClass((MetadataReader) candidateToCheck, true));
}
}
}
catch (ClassNotFoundException ex) {
throw new NestedIOException("Failed to load import candidate class", ex);
}
finally {
this.importStack.pop();
}
}
}
最終調用了DefaultListableBeanFactory的registerBeanDefinition方法,這里就是處理ImportBeanDefinitionRegistrar接口實現類的地方:
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if("configurationTest".equals(beanName)){
System.out.println(" registerBeanDefinition(String beanName, BeanDefinition beanDefinition) " + beanName);
}
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
// old? 還記得 “允許 bean 覆蓋” 這個配置嗎?allowBeanDefinitionOverriding
BeanDefinition oldBeanDefinition;
synchronized (this.beanDefinitionMap) {
// 之后會看到,所有的 Bean 注冊后會放入這個 beanDefinitionMap 中
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
// 處理重復名稱的 Bean 定義的情況
if (oldBeanDefinition != null) {
if (!this.allowBeanDefinitionOverriding) {
// 如果不允許覆蓋的話,拋異常
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
else {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
}
else {
//添加beanDefinitionNames
this.beanDefinitionNames.add(beanName);
this.frozenBeanDefinitionNames = null;
}
// 覆蓋
this.beanDefinitionMap.put(beanName, beanDefinition);
}
if (oldBeanDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
public void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
Assert.hasText(beanName, "'beanName' must not be empty");
synchronized (this.beanDefinitionMap) {
BeanDefinition bd = this.beanDefinitionMap.remove(beanName);
if (bd == null) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("No bean named '" + beanName + "' found in " + this);
}
throw new NoSuchBeanDefinitionException(beanName);
}
this.beanDefinitionNames.remove(beanName);
this.frozenBeanDefinitionNames = null;
}
resetBeanDefinition(beanName);
}
這段代碼主要就是把定義的bean放到beanDefinitionMap里去。beanDefinitionMap維護的就是bean的定義,當需要獲取的時候就從里面拿到對應的BeanDefinition,根據BeanDefinition生成一個對象。
以上就是ImportBeanDefinitionRegistrar手動控制BeanDefinition創(chuàng)建注冊詳解的詳細內容,更多關于BeanDefinition創(chuàng)建注冊的資料請關注腳本之家其它相關文章!
相關文章
從Myeclipse 導入到eclipse中無法識別為 web項目 問題的解決步驟
這篇文章主要介紹了從Myeclipse 導入到eclipse中無法識別為 web項目 問題的解決步驟,需要的朋友可以參考下2018-05-05
SpringBoot使用AOP+注解實現簡單的權限驗證的方法
這篇文章主要介紹了SpringBoot使用AOP+注解實現簡單的權限驗證的方法,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-05-05
Java實現拖拽文件上傳dropzone.js的簡單使用示例代碼
本篇文章主要介紹了Java實現拖拽文件上傳dropzone.js的簡單使用示例代碼,具有一定的參考價值,有興趣的可以了解一下2017-07-07

