Spring: делегировать пользовательской оболочке прокси для инъекции интерфейса

в очень большом устаревшем приложении у меня есть интерфейсы и классы, которые делают не реализующие эти интерфейсы.

интерфейсы генерируются на основе класса, поэтому подписи одинаковы (за исключением того, что интерфейс добавляет еще одно исключение сверху), а имена похожи (поэтому легко найти имя класса из имени интерфейса).

чтобы получить реализацию интерфейса, мы делаем кучу вызовов обработки и регистрации, но в основном используем java.lang.reflect.Proxy делегировать класс. Упрощенно это выглядит так:

// This will create a proxy and invoke handler that calls HelloWorld.doSomething
HelloWorldInterface i = MyProxyUtil.getInstance(HelloWorldInterface.class);
i.doSomething();

public interface HelloWorldInterface {
    public void doSomething() throws Exception;  
}

public class HelloWorld {
    public void doSomething() {
     //something
    }
}

возможно ли с обработкой аннотаций Spring, чтобы в целом @Autowire все поля типа *Interface и весной использовать MyProxyUtil.getInstance(*Interface.class) ввести реализацию?

такое, что

@Autowire HelloWorldInterface a;

HelloWorldInterface b = MyProxyUtil.getInstance(HelloWorldInterface.class);

@Autowire AnotherInterface c;

AnotherInterface d = MyProxyUtil.getInstance(AnotherInterface.class);


a == b
c == d

2 ответов


да, вам нужно реализовать AutowireCandidateResolver.

например:

public class ProxyAutowiredCandidateResolver extends SimpleAutowireCandidateResolver {

    @Override
    public Object getSuggestedValue(DependencyDescriptor descriptor) {
        String dependencyClassName = descriptor.getDependencyType().getSimpleName();
        if (dependencyClassName.endsWith("Interface")) {
            return MyProxyUtil.getInstance(descriptor.getDependencyType());
        }

        return super.getSuggestedValue(descriptor);
    }

}

вы могли бы использовать BeanFactoryPostProcessor чтобы настроить его в контексте приложения:

public class AutowireCandidateResolverConfigurer implements BeanFactoryPostProcessor {

    private AutowireCandidateResolver autowireCandidateResolver;

    public void postProcessBeanFactory(
            ConfigurableListableBeanFactory beanFactory) throws BeansException {
        DefaultListableBeanFactory  bf = (DefaultListableBeanFactory) beanFactory;
        bf.setAutowireCandidateResolver(autowireCandidateResolver);


    }

    public AutowireCandidateResolver getAutowireCandidateResolver() {
        return autowireCandidateResolver;
    }

    public void setAutowireCandidateResolver(

            AutowireCandidateResolver autowireCandidateResolver) {
        this.autowireCandidateResolver = autowireCandidateResolver;
    }

}

<bean id="autowireCandidateResolverConfigurer" class="AutowireCandidateResolverConfigurer">
        <property name="autowireCandidateResolver">
            <bean class="ProxyAutowiredCandidateResolver" />
        </property>
</bean>

Если я читаю это правильно, вы должны иметь возможность определить их в аннотированном классе JavaConfig @Configuration, а затем использовать их в другом месте.

документы (Весна):

@Configuration
public class AppConfig {
  @Bean
  public MyService myService() {
      return new MyServiceImpl();
  }
}

вы могли бы сделать нечто подобное:

@Configuration
public class InterfaceConfig {
  @Bean
  public HelloWorldInterface helloWorldInterface() {
      return MyProxyUtil.getInstance(HelloWorldInterface.class);
  }
}

в этот момент весна будет использовать это определение всякий раз, когда этот Боб был необходим.

вам нужно будет как-то связать в классе @Configuration (сканирование пути к классу, программно и т. д.), Но это зависит от того, как вы настраиваете контекст приложения.

Я думаю, что это должно работать. Я использовал JavaConfig совсем немного, но никогда не совсем так. Но это кажется разумным.