代码之家  ›  专栏  ›  技术社区  ›  Almas Abdrazak

无法通过限定符进行注入

  •  1
  • Almas Abdrazak  · 技术社区  · 8 年前

    我有一个名为Retry的自定义注释,带有以下Bean后处理器:

    @Component
    public final class RetryBPP implements BeanPostProcessor {
    
        private final Map<String, ClassDefinition> map = new HashMap<>(40);
    
        @Override
        public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
            final Class<?> asClass = bean.getClass();
            final Method[] methods = asClass.getMethods();
            final List<Method> collect = Stream.of(methods)
                    .filter(method -> method.isAnnotationPresent(Retriable.class))
                    .collect(Collectors.toList());
            if(!collect.isEmpty()){
                this.map.put(beanName,new ClassDefinition(collect,asClass));
            }
            return bean;
        }
    
        @Override
        public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
            final ClassDefinition definition = this.map.get(beanName);
            if(definition != null){
                final Class beanClass = definition.baseCLass;
                return Proxy.newProxyInstance(beanClass.getClassLoader(), beanClass.getInterfaces(), (proxy, method, args) -> {
                    if(definition.isMethodPresent(method)){
                        return this.retry(definition.originalMethod(method),bean,args);
                    } else{
                        return method.invoke(bean,args);
                    }
                });
            } else{
                return bean;
            }
        }
    
        private Object retry(final Method method,final Object originalBean,Object[] argc){
            final Retriable retriable = method.getAnnotation(Retriable.class);
            int attempts = retriable.attempts();
            while(true){
                try{
                    return method.invoke(originalBean,argc);
                }catch (final Exception throwable){
                    if(this.support(retriable.exceptions(),throwable.getClass()) && attempts != 0){
                        attempts--;
                        log.warn("Error on method {} wait to repeat",method.getName());
                        this.sleep(retriable.delay(),retriable.timeUnit());
                    } else{
                        throw new RuntimeException(throwable);
                    }
                }
            }
        }
    
        @SneakyThrows(InterruptedException.class)
        private void sleep(final long time, final TimeUnit timeUnit){
            timeUnit.sleep(time);
        }
    
        private boolean support(final Class<? extends Exception>[] exceptions,Class<? extends Exception> exception){
            boolean support = false;
            for (Class _class : exceptions) {
                if(_class.equals(exception)){
                    support = true;
                    break;
                }
            }
            return support;
        }
    
        @AllArgsConstructor
        private static final class ClassDefinition{
            final List<Method> methods;
            final Class baseCLass;
    
            boolean isMethodPresent(final Method method){
                return this.methods.stream()
                                   .anyMatch(mthd->methodEquals(mthd,method));
            }
    
            Method originalMethod(final Method method){
                return this.methods.stream()
                                   .filter(mthd->methodEquals(mthd,method))
                                   .findFirst().orElseThrow(NullPointerException::new);
    
            }
    
        }
    
    }
    

    接口:

    public interface Inter {
    
        String qqq(String url);
    
    }
    

    抽象类:

    public abstract class Abs implements Inter {
    
        @Override
        @Retry
        public String qqq(final String url) {
            someLogic(url);
        }
    
      protected String someLogic(String str);
    

    自定义限定符批注:

    @Retention(RetentionPolicy.RUNTIME)
    @Qualifier
    @Target({ElementType.CONSTRUCTOR, ElementType.TYPE, ElementType.PARAMETER, ElementType.FIELD})
    public @interface Qualif {
    
        VV.v type();
    
    }
    

    和实施:

    @Service
    @Qualif(type = First)
    @Slf4j
    public final class Impl extends Abs {
    
       @Override
       protected String someLogic(String str){...}
    }
    

    当我通过限定符自动连线时,如下所示:

    @Autowired
    @Qualif(type = First)
    Inter inter;
    

    Spring抛出异常 No qualifying bean of type [com.devadmin.downloader.sites.VideoDownloader] found for dependency Inter :,但当我从abctract类中删除@Retry annotation时,一切都很好。如何解决这个问题。

    顺便说一句,我检查了扩展抽象类的类 ApplicationContext 但是Spring没有看到我的自定义限定符

    1 回复  |  直到 8 年前
        1
  •  1
  •   Almas Abdrazak    5 年前

    我终于找到了答案。对于那些不了解问题的人。

    我有一个与方法的接口 foo() ,然后是一个实现此接口并具有自己的抽象方法的抽象类

    abstract class FooAbs impl Inter{
       @Override 
       void foo(){
          childMethod();
        }
       protected void childMethod();
    }
    

    最后我有一个@Component类,它扩展了FooAbs类。

    @Component 
    class First extends FooAbs{
    
      @Override
      void childMethod(){}
    }
    

    问题在于动态代理。

    下面的代码无法从类中获取接口

    Proxy.newProxyInstance(beanClass.getClassLoader(), beanClass.getInterfaces(), (proxy, method, args) -> {}
    

    动态代理看不到 getInterfaces 属于 First 类,因为该类不直接实现接口,因此我将我的类签名更改为:

    @Component 
        class First extends FooAbs implement Inter{
        
          @Override
          void childMethod(){}
        }
    

    在这一步之后

    beanClass.getInterfaces()
    

    查看内部接口。我知道这是一种变通方法,我将重写代码以避免这种结构。但它是有效的,也许对某些人会有帮助