zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    yaml 文件解析
    python 实现自动部署测试环境
    运行ride.py报错,闪退
    selenium 配置ie11 浏览器
    自动化测试(1)selenium+python+chrome 连接测试
    scrapy爬虫框架
    drf内置排序源码
    celery基本使用
    C# 如何复制(拷贝)Label控件上的文本【新方法】
    C# 使用PictureBox实现图片按钮控件
  • 原文地址:https://www.cnblogs.com/zxporz/p/11630107.html
Copyright © 2011-2022 走看看