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