Spring只定義接口自動代理接口實現類

能夠掃描到包

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章