Spring中ImportBeanDefinitionRegistrar源碼和使用方式
ImportBeanDefinitionRegistrar源碼和使用
第一步
定義的Mapper層:
@Mapper public interface PayMapper { @Select("select * from city") public List<Map<String,Object>> list(); }
第二步
使用FactoryBean,通過getObject方式,創(chuàng)建一個(gè)對象,放入到spring容器中,這里使用代理對象,放入到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("代理對象,獲取sql語句"); 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處理器,可以對于spring的BeanDefinitionMap進(jìn)行操作,可以修改Bean的描述,此時(shí)還沒有變成對象,這里是把創(chuàng)建注入的類型,創(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: 注入類型 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類放入到sprinig中運(yùn)行。
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(MyImportDefinitionRegistrar.class) public @interface LuoyanImportBeanDefinitionRegistrar { }
第五步
配置類:需要使用自定義注解@LuoyanImportBeanDefinitionRegistrar,把后置處理器的代碼內(nèi)容執(zhí)行。
@Configuration @ComponentScan("com.luoyan") @MapperScan("com.luoyan.dao.mapper") @LuoyanImportBeanDefinitionRegistrar public class AppConfig { }
第六步
啟動(dòng)類:
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類的報(bào)名+類名。比如:com.shadow.imports.MyImportSelector. * candidate:就表示com.shadow.imports.MyImportSelector */ for (SourceClass candidate : importCandidates) { /** * ImportSelector,判斷這個(gè)MyImportSelector.class是否實(shí)現(xiàn)了ImportSelector類 */ if (candidate.isAssignable(ImportSelector.class)) { // Candidate class is an ImportSelector -> delegate to it to determine imports //得到Import的類loadClass Class<?> candidateClass = candidate.loadClass(); //反射實(shí)現(xiàn)一個(gè)對象 //這個(gè)instantiateClass()方法底層比較復(fù)雜 /******************************instantiateClass()這個(gè)方法很重要*************************************/ //new出來當(dāng)前實(shí)現(xiàn)了ImportSelector接口的類對象 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) * 普通類就是加了@component類 */ //得到所有字符串.//循環(huán)引用這類用的是遞歸,就是說你配置類上有了@Impont,但是你實(shí)現(xiàn)了ImportSelector類的類上還是有@Impont //TODO: selector表示你實(shí)現(xiàn)ImportSelector接口的類的對象. String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); //把名字傳入得到importSourceClasses,把這個(gè)類添加到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)來的時(shí)候也是這個(gè)方法. //遞歸,這里第二次調(diào)用processImports. //如果是一個(gè)普通類,會進(jìn)else processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false); } } /** * ImportBeanDefinitionRegistrar實(shí)現(xiàn)了這個(gè)接口的類放到了addImportBeanDefinitionRegistrar()方法 * importBeanDefinitionRegistrarsMap當(dāng)中去了。 * 而 * 實(shí)現(xiàn)ImportSelector接口的類卻放到了configurationClassesMap當(dāng)中去了。 * 所以在解析這些類的時(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里面主要就是把類放到configrationClasses * 可以看到普通類再掃描出來的時(shí)候就被注冊了 * 如果importSelector,回顯放到configurationClasses后面進(jìn)行注冊 * 注意這里的processConfigurationClass前面已經(jīng)解釋過這個(gè)方法了 */ this.importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); //processConfigurationClass()這個(gè)方法,是繼續(xù)判斷當(dāng)前普通類是否加了@Configuration注解 //candidate.asConfigClass(configClass)這個(gè)方法,是把通過@Import注解得到的類,執(zhí)行方法后,得到返回回來的類字符串,反射出來的類.放入到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接口 * 的類中邏輯,注冊到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 程序),有兩種方式,下面就來介紹一下,感興趣的可以來了解一下2025-03-03java如何將list中的某個(gè)元素移動(dòng)位置
在Java編程中我們經(jīng)常會使用List數(shù)據(jù)結(jié)構(gòu)來存儲一組元素,下面這篇文章主要給大家介紹了關(guān)于java如何將list中的某個(gè)元素移動(dòng)位置的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-05-05Java并發(fā)編程示例(二):獲取和設(shè)置線程信息
這篇文章主要介紹了Java并發(fā)編程示例(二):獲取和設(shè)置線程信息,本文是系列文章的第二篇,本文著重講解Thread類的幾個(gè)重要屬性,需要的朋友可以參考下2014-12-12基于獲取JAVA路徑,包括CLASSPATH外的路徑的方法詳解
本篇文章是對獲取JAVA路徑,包括CLASSPATH外的路徑的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05