Spring中ImportBeanDefinitionRegistrar源碼和使用方式
ImportBeanDefinitionRegistrar源碼和使用
第一步
定義的Mapper層:
@Mapper public interface PayMapper { @Select("select * from city") public List<Map<String,Object>> list(); }
第二步
使用FactoryBean,通過(guò)getObject方式,創(chuàng)建一個(gè)對(duì)象,放入到spring容器中,這里使用代理對(duì)象,放入到spring容器中。
public class MyFactoryBean implements FactoryBean, InvocationHandler { private Class aClass; public MyFactoryBean(Class aClass) { this.aClass = aClass; } @Override public Object getObject() throws Exception { Class[] interfaces = new Class[]{aClass}; Object proxyInstance = Proxy.newProxyInstance(this.getClass().getClassLoader(), interfaces, this); return proxyInstance; } @Override public Class<?> getObjectType() { return null; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("代理對(duì)象,獲取sql語(yǔ)句"); Method method1 = proxy.getClass().getInterfaces()[0].getMethod(method.getName(), null); Select declaredAnnotation = method1.getDeclaredAnnotation(Select.class); System.out.println(declaredAnnotation.value()[0]); return null; } }
第三步
spring的ImportBeanDefinitionRegistrar處理器,可以對(duì)于spring的BeanDefinitionMap進(jìn)行操作,可以修改Bean的描述,此時(shí)還沒(méi)有變成對(duì)象,這里是把創(chuàng)建注入的類(lèi)型,創(chuàng)建了構(gòu)造方法中需要的接口,最后取Bean的名字:payServiceTest,一個(gè)BeanDefinition描述。
public class MyImportDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(PayMapper.class); AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition(); //TODO: 注入類(lèi)型 beanDefinition.setBeanClass(MyFactoryBean.class); //TODO: 注入構(gòu)造方法 beanDefinition.getConstructorArgumentValues().addGenericArgumentValue("com.luoyan.dao.mapper.PayMapper"); //TODO: 放入到beanDefinitionMap中 registry.registerBeanDefinition("payServiceTest",beanDefinition); } }
第四步
自定義注解,把@Import(MyImportDefinitionRegistrar.class)注解,MyImportDefinitionRegistrar類(lèi)放入到sprinig中運(yùn)行。
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(MyImportDefinitionRegistrar.class) public @interface LuoyanImportBeanDefinitionRegistrar { }
第五步
配置類(lèi):需要使用自定義注解@LuoyanImportBeanDefinitionRegistrar,把后置處理器的代碼內(nèi)容執(zhí)行。
@Configuration @ComponentScan("com.luoyan") @MapperScan("com.luoyan.dao.mapper") @LuoyanImportBeanDefinitionRegistrar public class AppConfig { }
第六步
啟動(dòng)類(lèi):
public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(AppConfig.class); applicationContext.addBeanFactoryPostProcessor(new MyBeanFactoryPostProcessor()); applicationContext.refresh(); PayMapper payServiceTest = (PayMapper) applicationContext.getBean("payServiceTest"); payServiceTest.list(); }
源碼:
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter, boolean checkForCircularImports) { if (importCandidates.isEmpty()) { return; } if (checkForCircularImports && isChainedImportOnStack(configClass)) { this.problemReporter.error(new CircularImportProblem(configClass, this.importStack)); } else { this.importStack.push(configClass); try { /** * * 因?yàn)锧Import(xxx.class,xxxx.class)這里可以放多個(gè) * importCandidates:表示被放在@Import注解中的class類(lèi)的報(bào)名+類(lèi)名。比如:com.shadow.imports.MyImportSelector. * candidate:就表示com.shadow.imports.MyImportSelector */ for (SourceClass candidate : importCandidates) { /** * ImportSelector,判斷這個(gè)MyImportSelector.class是否實(shí)現(xiàn)了ImportSelector類(lèi) */ if (candidate.isAssignable(ImportSelector.class)) { // Candidate class is an ImportSelector -> delegate to it to determine imports //得到Import的類(lèi)loadClass Class<?> candidateClass = candidate.loadClass(); //反射實(shí)現(xiàn)一個(gè)對(duì)象 //這個(gè)instantiateClass()方法底層比較復(fù)雜 /******************************instantiateClass()這個(gè)方法很重要*************************************/ //new出來(lái)當(dāng)前實(shí)現(xiàn)了ImportSelector接口的類(lèi)對(duì)象 ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry); Predicate<String> selectorFilter = selector.getExclusionFilter(); if (selectorFilter != null) { exclusionFilter = exclusionFilter.or(selectorFilter); } if (selector instanceof DeferredImportSelector) { this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); } else { /** * 這里重點(diǎn) * 普通類(lèi)就是加了@component類(lèi) */ //得到所有字符串.//循環(huán)引用這類(lèi)用的是遞歸,就是說(shuō)你配置類(lèi)上有了@Impont,但是你實(shí)現(xiàn)了ImportSelector類(lèi)的類(lèi)上還是有@Impont //TODO: selector表示你實(shí)現(xiàn)ImportSelector接口的類(lèi)的對(duì)象. String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); //把名字傳入得到importSourceClasses,把這個(gè)類(lèi)添加到annotation這個(gè)變量中去了asSourceClasses()這個(gè)方法. //importClassNames=com.shadow.service.TestService3 Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter); //然后又進(jìn)行循環(huán)判斷了.繼續(xù)調(diào)用processImports()方法,剛剛進(jìn)來(lái)的時(shí)候也是這個(gè)方法. //遞歸,這里第二次調(diào)用processImports. //如果是一個(gè)普通類(lèi),會(huì)進(jìn)else processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false); } } /** * ImportBeanDefinitionRegistrar實(shí)現(xiàn)了這個(gè)接口的類(lèi)放到了addImportBeanDefinitionRegistrar()方法 * importBeanDefinitionRegistrarsMap當(dāng)中去了。 * 而 * 實(shí)現(xiàn)ImportSelector接口的類(lèi)卻放到了configurationClassesMap當(dāng)中去了。 * 所以在解析這些類(lèi)的時(shí)候使用了不同的方法存放this.reader.loadBeanDefinitions(configClasses); * */ else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { // Candidate class is an ImportBeanDefinitionRegistrar -> // delegate to it to register additional com.luoyan.bean definitions Class<?> candidateClass = candidate.loadClass(); ImportBeanDefinitionRegistrar registrar = ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, 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 /** * 否則,加入到importStack后調(diào)用processConfigurationClass進(jìn)行處理 * processConfiguration里面主要就是把類(lèi)放到configrationClasses * 可以看到普通類(lèi)再掃描出來(lái)的時(shí)候就被注冊(cè)了 * 如果importSelector,回顯放到configurationClasses后面進(jìn)行注冊(cè) * 注意這里的processConfigurationClass前面已經(jīng)解釋過(guò)這個(gè)方法了 */ this.importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); //processConfigurationClass()這個(gè)方法,是繼續(xù)判斷當(dāng)前普通類(lèi)是否加了@Configuration注解 //candidate.asConfigClass(configClass)這個(gè)方法,是把通過(guò)@Import注解得到的類(lèi),執(zhí)行方法后,得到返回回來(lái)的類(lèi)字符串,反射出來(lái)的類(lèi).放入到this.importedBy.add(importedBy);集合中 //真正導(dǎo)入到spring的BeanDefinitionMap中的時(shí)候使用到 processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter); } } } 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(); } } }
源碼:
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) { registrars.forEach((registrar, metadata) -> /** * 這個(gè)registrar.registerBeanDefinitions就是自己實(shí)現(xiàn)了ImportBeanDefinitionRegistrar接口 * 的類(lèi)中邏輯,注冊(cè)到beanMap中的方法 */ registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator)); }
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring Boot 非web應(yīng)用程序的實(shí)現(xiàn)
SpringBoot框架中,要?jiǎng)?chuàng)建一個(gè)非Web應(yīng)用程序(純 Java 程序),有兩種方式,下面就來(lái)介紹一下,感興趣的可以來(lái)了解一下2025-03-03struts2實(shí)現(xiàn)簡(jiǎn)單文件下載功能
這篇文章主要為大家詳細(xì)介紹了struts2實(shí)現(xiàn)簡(jiǎn)單文件下載功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01java如何將list中的某個(gè)元素移動(dòng)位置
在Java編程中我們經(jīng)常會(huì)使用List數(shù)據(jù)結(jié)構(gòu)來(lái)存儲(chǔ)一組元素,下面這篇文章主要給大家介紹了關(guān)于java如何將list中的某個(gè)元素移動(dòng)位置的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-05-05一文帶你探究Spring中Bean的線程安全性問(wèn)題
很多人都想spring中的bean是線程安全的嗎?本文將帶你探究Spring中Bean的線程安全性問(wèn)題,感興趣的同學(xué)可以參考閱讀下2023-05-05Java并發(fā)編程示例(二):獲取和設(shè)置線程信息
這篇文章主要介紹了Java并發(fā)編程示例(二):獲取和設(shè)置線程信息,本文是系列文章的第二篇,本文著重講解Thread類(lèi)的幾個(gè)重要屬性,需要的朋友可以參考下2014-12-12基于獲取JAVA路徑,包括CLASSPATH外的路徑的方法詳解
本篇文章是對(duì)獲取JAVA路徑,包括CLASSPATH外的路徑的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05