­

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