Pregunta Spring: delegado a un contenedor proxy personalizado para la inyección de interfaz


En una aplicación heredada muy grande, tengo interfaces y clases que hacen no implementar esas interfaces.

Las interfaces se generan en función de la clase, por lo que las firmas son las mismas (excepto que la interfaz agrega otra excepción en la parte superior) y los nombres son similares (por lo que es fácil encontrar el nombre de la clase a partir del nombre de la interfaz).

Para obtener una implementación de la interfaz, hacemos un montón de procesamiento y registro de llamadas, pero básicamente utilizamos java.lang.reflect.Proxy delegar a la clase. Simplificado, se ve así:

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

¿Es posible con el procesamiento de anotaciones Spring, a genéricamente @Autowire todos los campos de tipo *Interface y tener uso de primavera MyProxyUtil.getInstance(*Interface.class) ¿Para inyectar la implementación?

Tal que

@Autowire HelloWorldInterface a;

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

@Autowire AnotherInterface c;

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


a == b
c == d

6
2017-07-16 21:22


origen


Respuestas:


Sí, necesitas implementar un AutowireCandidateResolver.

Por ejemplo:

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

}

Puedes usar un BeanFactoryPostProcessor para configurarlo en el contexto de la aplicación:

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>

9
2017-07-16 22:31



Si estoy leyendo esto correctamente, deberías poder definirlos en una clase anotada JavaConfig @Configuration y luego usarlos en otra parte.

De los documentos (Primavera)

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

Podrías hacer algo similar:

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

En ese momento, Spring usaría esa definición cada vez que se necesitara ese frijol.

Tendría que vincularse en la clase @Configuration de alguna manera (exploración de classpath, programáticamente, etc.), pero eso depende de cómo esté configurando el contexto de su aplicación.

Creo que esto debería funcionar. He usado JavaConfig bastante, pero nunca me ha gustado esto. Pero parece razonable.


1
2017-07-16 21:53