Spring只定义接口自动代理接口实现类

  • 2019 年 10 月 7 日
  • 笔记

能够扫描到包

@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;  }