能够扫描到包
@ComponentScan("org.zxp.esclientrhl")
ESCRegistrar类主要实现ImportBeanDefinitionRegistrar接口
@Configuration public class ESCRegistrar extends AbstractESCRegister implements BeanFactoryAware,ApplicationContextAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
实现下面方法,会在spring启动早期调用生成代理bean
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) { //扫描entity new ESIndexProcessor().scan(annotationMetadata,beanFactory,applicationContext); //扫描接口 super.registerBeanDefinitions(beanFactory, environment, resourceLoader, annotationMetadata, registry); }
扫描entity,通过注解配置或者启动目录扫描实体类并托管给Spring管理(和自动代理接口实现类无关,用于自动创建索引)
public void scan(AnnotationMetadata annotationMetadata,BeanFactory beanFactory,ApplicationContext applicationContext){ GetBasePackage getBasePackage = new GetBasePackage(EnableESTools.class); ESEntityScanner scanner = new ESEntityScanner((BeanDefinitionRegistry) beanFactory); scanner.setResourceLoader(applicationContext); scanner.scan(getBasePackage.getEntityPackage(annotationMetadata).toArray(String[]::new)); }
通过getCandidates方法获取继承ESCRepository的接口
public Stream<BeanDefinition> getCandidates(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry, Environment environment, ResourceLoader resourceLoader) { ESCRepositoryComponentProvider scanner = new ESCRepositoryComponentProvider(registry); scanner.setEnvironment(environment); scanner.setResourceLoader(resourceLoader); //输入是basepackages,输出是BeanDefinition的Stream return getBasePackage(annotationMetadata).flatMap(it -> scanner.findCandidateComponents(it).stream()); }
下面这两种scan不同,第一个就是扫描后能被spring识别,第二个是扫描到后返回BeanDefinition
scanner.findCandidateComponents(it) scanner.scan(getBasePackage.getEntityPackage(annotationMetadata).toArray(String[]::new));
获取继承ESCRepository的接口(BeanDefinition)并遍历
通过BeanDefinitionBuilder给RepositoryFactorySupport传递扫描到接口的类类型、以及要生成代理bean的name
调用beanDefinitionRegistry.registerBeanDefinition(beanName, bd);将RepositoryFactorySupport托管给spring(注意RepositoryFactorySupport并不是目的,是通过RepositoryFactorySupport生成代理bean)
RepositoryFactorySupport的作用就是注册bean
public void registerBeanDefinitions(BeanFactory factory, Environment environment, ResourceLoader resourceLoader, AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) { getCandidates(annotationMetadata, registry, environment, resourceLoader).forEach(beanDefinition -> { BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(RepositoryFactorySupport.class); String beanClassName = beanDefinition.getBeanClassName(); //传入要实例化的接口 beanDefinitionBuilder.addConstructorArgValue(beanClassName); //获取bean的定义 BeanDefinition bd = beanDefinitionBuilder.getRawBeanDefinition(); //生成beanname String beanName = beanClassName.substring(beanClassName.lastIndexOf(".") + 1); if(org.zxp.esclientrhl.auto.util.EnableESTools.isPrintregmsg()){ logger.info("generate ESCRegistrar beanClassName:" + beanClassName); logger.info("generate ESCRegistrar beanName:" + beanName); } BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) factory; //注册bean beanName是代理bean的名字 不是RepositoryFactorySupport的名字 beanDefinitionRegistry.registerBeanDefinition(beanName, bd); }); }
repositoryInterface用于接收传入的接口类类型(准备通过动态代理生成)
通过afterPropertiesSet在RepositoryFactorySupport注册完成后生成并注册真正的代理bean
public class RepositoryFactorySupport<T extends ESCRepository<S, ID>, S, ID> implements ApplicationContextAware, ResourceLoaderAware, InitializingBean, FactoryBean<T>, BeanClassLoaderAware, BeanFactoryAware, ApplicationEventPublisherAware { …… private final Class<? extends T> repositoryInterface; public RepositoryFactorySupport(Class<? extends T> repositoryInterface) { this.repositoryInterface = repositoryInterface; } @Override public void afterPropertiesSet() { try { this.repository = this.getRepository(repositoryInterface); } catch (Exception e) { logger.error("ESCRepository proxy create fail !", e); } }
生成代理bean的细节注意注释:
public <T> T getRepository(Class<T> repositoryInterface) throws Exception { SimpleESCRepository target = new SimpleESCRepository(applicationContext);//传入applicationContext的目的是为了能让代理bean在运行时能通过applicationContext获取需要注入的bean getMetadata(target);//下面单独说,获取对应实体类的类类型以及主键类型 //spring动态代理用法 ProxyFactory result = new ProxyFactory(); result.setTarget(target); result.addAdvice(new MethodInterceptor() { @Override public Object invoke(MethodInvocation invocation) throws Throwable { Object result = invocation.proceed(); return result; } }); result.setInterfaces(this.repositoryInterface, ESCRepository.class); T repository = (T) result.getProxy(classLoader); return repository; }
只要拿到了接口或者类,是能通过api获得定义接口的泛型名称的(不能获得全限定类名,有类名就可以匹配)
getEntityList()方法通过缓存的entitypaths遍历所有的entity并与之匹配
private void getMetadata(SimpleESCRepository target) throws Exception { Type[] types = repositoryInterface.getGenericInterfaces(); ParameterizedType parameterized = (ParameterizedType) types[0]; //实体类类型名称 String domainClassName = parameterized.getActualTypeArguments()[0].getTypeName(); //实体类主键类型名称 String idClassName = parameterized.getActualTypeArguments()[1].getTypeName(); if (org.zxp.esclientrhl.auto.util.EnableESTools.isPrintregmsg()) { logger.info("domainClassName:" + domainClassName + " idClassName:" + idClassName); } //按照实体类类型名称匹配实体类类型 List<String> entityList = getEntityList(); for (int i = 0; i < entityList.size(); i++) { if (entityList.get(i).lastIndexOf("." + domainClassName) != -1) { if (target.getDomainClass() == null) { target.setDomainClass(Class.forName(entityList.get(i))); break; } else { target.setDomainClass(null); throw new Exception("Entity Overmatched !"); } } } //按照实体类主键类型名称主键类型 Map<String, Class> idTypeMap = getIdTypeMap(); if (idTypeMap.containsKey(idClassName)) { target.setIdClass(idTypeMap.get(idClassName)); } else { throw new Exception("Not Supported ID Type !"); } }
实现了FactoryBean可以将生成的代理bean托管给spring
/** * 实现了FactoryBean可以将生成的代理bean托管给spring * * @return * @throws Exception */ @Override public T getObject() throws Exception { return this.repository; } /** * 实现了FactoryBean可以将生成的代理bean托管给spring * * @return */ @Override public Class<?> getObjectType() { return repositoryInterface; }