Spring中ImportBeanDefinitionRegistrar源码和使用方式
作者:Dubbo-罗
Spring容器扩展流程总结:1. 定义Mapper层,2. 通过FactoryBean创建代理对象,3. 使用ImportBeanDefinitionRegistrar修改Bean定义,4. 应用自定义注解@LuoyanImportBeanDefinitionRegistrar,5. 配置类中执行后置处理器,6. 启动类中查看源码,希望对大家有所帮助
ImportBeanDefinitionRegistrar源码和使用
第一步
定义的Mapper层:
@Mapper public interface PayMapper { @Select("select * from city") public List<Map<String,Object>> list(); }
第二步
使用FactoryBean,通过getObject方式,创建一个对象,放入到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进行操作,可以修改Bean的描述,此时还没有变成对象,这里是把创建注入的类型,创建了构造方法中需要的接口,最后取Bean的名字:payServiceTest,一个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: 注入构造方法 beanDefinition.getConstructorArgumentValues().addGenericArgumentValue("com.luoyan.dao.mapper.PayMapper"); //TODO: 放入到beanDefinitionMap中 registry.registerBeanDefinition("payServiceTest",beanDefinition); } }
第四步
自定义注解,把@Import(MyImportDefinitionRegistrar.class)注解,MyImportDefinitionRegistrar类放入到sprinig中运行。
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(MyImportDefinitionRegistrar.class) public @interface LuoyanImportBeanDefinitionRegistrar { }
第五步
配置类:需要使用自定义注解@LuoyanImportBeanDefinitionRegistrar,把后置处理器的代码内容执行。
@Configuration @ComponentScan("com.luoyan") @MapperScan("com.luoyan.dao.mapper") @LuoyanImportBeanDefinitionRegistrar public class AppConfig { }
第六步
启动类:
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 { /** * * 因为@Import(xxx.class,xxxx.class)这里可以放多个 * importCandidates:表示被放在@Import注解中的class类的报名+类名。比如:com.shadow.imports.MyImportSelector. * candidate:就表示com.shadow.imports.MyImportSelector */ for (SourceClass candidate : importCandidates) { /** * ImportSelector,判断这个MyImportSelector.class是否实现了ImportSelector类 */ if (candidate.isAssignable(ImportSelector.class)) { // Candidate class is an ImportSelector -> delegate to it to determine imports //得到Import的类loadClass Class<?> candidateClass = candidate.loadClass(); //反射实现一个对象 //这个instantiateClass()方法底层比较复杂 /******************************instantiateClass()这个方法很重要*************************************/ //new出来当前实现了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 { /** * 这里重点 * 普通类就是加了@component类 */ //得到所有字符串.//循环引用这类用的是递归,就是说你配置类上有了@Impont,但是你实现了ImportSelector类的类上还是有@Impont //TODO: selector表示你实现ImportSelector接口的类的对象. String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); //把名字传入得到importSourceClasses,把这个类添加到annotation这个变量中去了asSourceClasses()这个方法. //importClassNames=com.shadow.service.TestService3 Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter); //然后又进行循环判断了.继续调用processImports()方法,刚刚进来的时候也是这个方法. //递归,这里第二次调用processImports. //如果是一个普通类,会进else processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false); } } /** * ImportBeanDefinitionRegistrar实现了这个接口的类放到了addImportBeanDefinitionRegistrar()方法 * importBeanDefinitionRegistrarsMap当中去了。 * 而 * 实现ImportSelector接口的类却放到了configurationClassesMap当中去了。 * 所以在解析这些类的时候使用了不同的方法存放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后调用processConfigurationClass进行处理 * processConfiguration里面主要就是把类放到configrationClasses * 可以看到普通类再扫描出来的时候就被注册了 * 如果importSelector,回显放到configurationClasses后面进行注册 * 注意这里的processConfigurationClass前面已经解释过这个方法了 */ this.importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); //processConfigurationClass()这个方法,是继续判断当前普通类是否加了@Configuration注解 //candidate.asConfigClass(configClass)这个方法,是把通过@Import注解得到的类,执行方法后,得到返回回来的类字符串,反射出来的类.放入到this.importedBy.add(importedBy);集合中 //真正导入到spring的BeanDefinitionMap中的时候使用到 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) -> /** * 这个registrar.registerBeanDefinitions就是自己实现了ImportBeanDefinitionRegistrar接口 * 的类中逻辑,注册到beanMap中的方法 */ registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator)); }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。