Buscar..


Autowiring todos los frijoles de un tipo específico

Si tiene varias implementaciones de la misma interfaz, Spring puede convertirlas en un objeto de colección. Voy a usar un ejemplo usando un patrón Validator 1

Foo Class:

public class Foo {
     private String name;
     private String emailAddress;
     private String errorMessage;
     /** Getters & Setters omitted **/
}

Interfaz:

public interface FooValidator {
    public Foo validate(Foo foo);
}

Nombre Validador Clase:

@Component(value="FooNameValidator")
public class FooNameValidator implements FooValidator {
    @Override
    public Foo validate(Foo foo) {
        //Validation logic goes here.
    }
}

Correo electrónico Validador de clase:

@Component(value="FooEmailValidator")
public class FooEmailValidator implements FooValidator {
    @Override
    public Foo validate(Foo foo) {
        //Different validation logic goes here.
    }
}

Ahora puede habilitar automáticamente estos validadores individualmente o juntos en una clase.

Interfaz:

public interface FooService {
    public void handleFoo(Foo foo);
}

Clase:

@Service
public class FooServiceImpl implements FooService {
    /** Autowire all classes implementing FooValidator interface**/
    @Autowired
    private List<FooValidator> allValidators;

    @Override
    public void handleFoo(Foo foo) {
       /**You can use all instances from the list**/
       for(FooValidator validator : allValidators) {
           foo = validator.validate(foo);               
       }
    }   
}

Vale la pena señalar que si tiene más de una implementación de una interfaz en el contenedor Spring IoC y no especifica cuál desea usar con la anotación @Qualifier , Spring lanzará una excepción cuando intente iniciar, porque ganó " No sé qué instancia utilizar.

1: Esta no es la forma correcta de hacer validaciones tan simples. Este es un ejemplo simple sobre autowiring. Si desea tener una idea de un método de validación mucho más sencillo, vea cómo Spring realiza la validación con anotaciones.

Declarando frijol

Para declarar un bean, simplemente anote un método con la anotación @Bean o anote una clase con la anotación @Component (las anotaciones @Service , @Repository , @Controller podrían usarse también).

Cuando JavaConfig encuentra tal método, ejecutará ese método y registrará el valor de retorno como un bean dentro de un BeanFactory. Por defecto, el nombre del bean será el del nombre del método.

Podemos crear frijol usando una de tres maneras:

  1. Usando la configuración basada en Java : En el archivo de configuración necesitamos declarar el bean usando la anotación @bean

    @Configuration
    public class AppConfig {
        @Bean
        public TransferService transferService() {
            return new TransferServiceImpl();
        }
    }
    
  2. Uso de la configuración basada en XML : para la configuración basada en XML necesitamos crear el bean declarar en la configuración de la aplicación XML, es decir

     <beans>
         <bean name="transferService" class="com.acme.TransferServiceImpl"/>
     </beans>
    
  3. Componente controlado por anotación: para los componentes controlados por anotación, debemos agregar la anotación @Component a la clase que queremos declarar como bean.

     @Component("transferService")
     public class TransferServiceImpl implements TransferService {
         ...
     }
    

Ahora los tres beans con nombre transferService están disponibles en BeanFactory o ApplicationContext .

Anotación básica autowiring

Interfaz:

public interface FooService {
    public int doSomething();
}

Clase:

@Service
public class FooServiceImpl implements FooService {
    @Override
    public int doSomething() {
        //Do some stuff here
        return 0;
    }
}

Se debe tener en cuenta que una clase debe implementar una interfaz para que Spring pueda conectar automáticamente esta clase. Existe un método para permitir que Spring autowire clases autónomas utilizando el tiempo de carga de tejido, pero está fuera del alcance de este ejemplo.

Puede obtener acceso a este bean en cualquier clase que haya sido instanciada por el contenedor Spring IoC utilizando la anotación @Autowired .

Uso:

@Autowired([required=true])

La anotación @Autowired intentará primero autowire por tipo y luego volverá al nombre del bean en caso de ambigüedad.

Esta anotación se puede aplicar de varias maneras diferentes.

Inyección de constructor:

public class BarClass() {
    private FooService fooService         

    @Autowired
    public BarClass(FooService fooService) {
        this.fooService = fooService;
    }
}

Inyección de campo:

public class BarClass() {
    @Autowired
    private FooService fooService;
}

Inyección de Setter:

public class BarClass() {
    private FooService fooService;

    @Autowired
    public void setFooService(FooService fooService) {
        this.fooService = fooService;
    }
}

Usando FactoryBean para la instanciación dinámica de frijol

Para decidir dinámicamente qué frijoles inyectar, podemos usar FactoryBean s. Estas son clases que implementan el patrón de método de fábrica, proporcionando instancias de beans para el contenedor. Son reconocidos por Spring y se pueden usar de manera transparente, sin necesidad de saber que el frijol proviene de una fábrica. Por ejemplo:

public class ExampleFactoryBean extends AbstractFactoryBean<String> {
    // This method determines the type of the bean for autowiring purposes
    @Override
    public Class<?> getObjectType() {
        return String.class;
    }

    // this factory method produces the actual bean
    @Override
    protected String createInstance() throws Exception {
        // The thing you return can be defined dynamically,
        // that is read from a file, database, network or just
        // simply randomly generated if you wish.
        return "Something from factory";
    }
}

Configuración:

@Configuration
public class ExampleConfig {
    @Bean
    public FactoryBean<String> fromFactory() {
        return new ExampleFactoryBean();
    }
}

Obteniendo el frijol

AbstractApplicationContext context = new AnnotationConfigApplicationContext(ExampleConfig.class);
String exampleString = (String) context.getBean("fromFactory");

Para obtener el FactoryBean real, use el prefijo ampersand antes del nombre del bean:

FactoryBean<String> bean = (FactoryBean<String>) context.getBean("&fromFactory");

Tenga en cuenta que solo puede usar prototype o ámbitos singleton : para cambiar el alcance al método de anulación de prototype anulación isSingleton :

public class ExampleFactoryBean extends AbstractFactoryBean<String> {
    @Override
    public boolean isSingleton() {
        return false;
    }

    // other methods omitted for readability reasons
}

Tenga en cuenta que el alcance se refiere a las instancias reales que se crean, no al propio bean de fábrica.

Inyectar frijoles prototipo en singletons

El contenedor crea un bean singleton e inyecta colaboradores en él solo una vez. Este no es el comportamiento deseado cuando un bean singleton tiene un colaborador de ámbito prototipo, ya que el bean de ámbito prototipo debe inyectarse cada vez que se accede a él mediante el acceso.

Hay varias soluciones a este problema:

  1. Utilice el método de búsqueda de inyección
  2. Recupere un bean de ámbito de prototipo a través de javax.inject.Provider
  3. Recupere un bean de ámbito prototipo a través de org.springframework.beans.factory.ObjectFactory (un equivalente de # 2, pero con la clase que es específica de Spring)
  4. Haga que un contenedor de bean singleton sea consciente mediante la implementación de la interfaz ApplicationContextAware

Los enfoques # 3 y # 4 generalmente no se recomiendan, ya que vinculan fuertemente una aplicación al marco de Spring. Por lo tanto, no están cubiertos en este ejemplo.

Inyección del método de búsqueda a través de la configuración XML y un método abstracto.

Clases de Java

public class Window {
}

public abstract class WindowGenerator {

    public Window generateWindow() {
        Window window = createNewWindow(); // new instance for each call
        ... 
    }

    protected abstract Window createNewWindow(); // lookup method
}

XML

<bean id="window" class="somepackage.Window" scope="prototype" lazy-init="true"/>

<bean id="windowGenerator" class="somepackage.WindowGenerator">
    <lookup-method name="createNewWindow" bean="window"/>
</bean>

Método de búsqueda de inyección a través de la configuración de Java y @Component.

Clases de Java

public class Window {
}

@Component
public class WindowGenerator {

    public Window generateWindow() {
        Window window = createNewWindow(); // new instance for each call
        ...
    }

    @Lookup
    protected Window createNewWindow() {
        throw new UnsupportedOperationException();
    }
}

Configuración de Java

@Configuration
@ComponentScan("somepackage") // package where WindowGenerator is located
public class MyConfiguration {

    @Bean
    @Lazy
    @Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public Window window() {
        return new Window();
    }
}

Inyección del método de búsqueda manual a través de la configuración de Java.

Clases de Java

public class Window {
}

public abstract class WindowGenerator {

    public Window generateWindow() {
        Window window = createNewWindow(); // new instance for each call
        ...
    }

    protected abstract Window createNewWindow(); // lookup method
}

Configuración de Java

@Configuration
public class MyConfiguration {

    @Bean
    @Lazy
    @Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public Window window() {
        return new Window();
    }

    @Bean
    public WindowGenerator windowGenerator(){
        return new WindowGenerator() {
            @Override
            protected Window createNewWindow(){
                return window();
            }
        };
    }
}

Inyección de un bean de ámbito protoype en singleton a través de javax.inject.Provider

Clases de java

public class Window {
}

public class WindowGenerator {

    private final Provider<Window> windowProvider;

    public WindowGenerator(final Provider<Window> windowProvider) {
        this.windowProvider = windowProvider;
    }

    public Window generateWindow() {
        Window window = windowProvider.get(); // new instance for each call   
        ...
    }
}

XML

<bean id="window" class="somepackage.Window" scope="prototype" lazy-init="true"/>

<bean id="windowGenerator" class="somepackage.WindowGenerator">
    <constructor-arg>
        <bean class="org.springframework.beans.factory.config.ProviderCreatingFactoryBean">
            <property name="targetBeanName" value="window"/>
        </bean>
    </constructor-arg>
</bean>

Los mismos enfoques también se pueden utilizar para otros ámbitos (por ejemplo, para inyectar un bean de ámbito de solicitud en singleton).

Autowiring instancias de bean específicas con @Qualifier

Si tiene varias implementaciones de la misma interfaz, Spring necesita saber cuál de ellas debería incluir en una clase. Voy a usar un patrón Validator en este ejemplo. 1

Foo Class:

public class Foo {
     private String name;
     private String emailAddress;
     private String errorMessage;
     /** Getters & Setters omitted **/
}

Interfaz:

public interface FooValidator {
    public Foo validate(Foo foo);
}

Nombre Validador Clase:

@Component(value="FooNameValidator")
public class FooNameValidator implements FooValidator {
    @Override
    public Foo validate(Foo foo) {
        //Validation logic goes here.
    }
}

Correo electrónico Validador de clase:

@Component(value="FooEmailValidator")
public class FooEmailValidator implements FooValidator {
    @Override
    public Foo validate(Foo foo) {
        //Different validation logic goes here.
    }
}

Ahora puede autowire estos validadores individualmente en una clase.

Interfaz:

public interface FooService {
    public void handleFoo(Foo foo);
}

Clase:

@Service
public class FooServiceImpl implements FooService {
    /** Autowire validators individually **/
    @Autowired
    /* 
     * Notice how the String value here matches the value 
     * on the @Component annotation? That's how Spring knows which 
     * instance to autowire.
     */
    @Qualifier("FooNameValidator")
    private FooValidator nameValidator;

    @Autowired
    @Qualifier("FooEmailValidator")
    private FooValidator emailValidator;

    @Override
    public void handleFoo(Foo foo) {
        /**You can use just one instance if you need**/
        foo = nameValidator.validate(foo);
    }   
}

Vale la pena señalar que si tiene más de una implementación de una interfaz en el contenedor Spring IoC y no especifica cuál desea usar con la anotación @Qualifier , Spring lanzará una excepción cuando intente iniciar, porque ganó " No sé qué instancia utilizar.

1: Esta no es la forma correcta de hacer validaciones tan simples. Este es un ejemplo simple sobre autowiring. Si desea tener una idea de un método de validación mucho más sencillo, vea cómo Spring realiza la validación con anotaciones.

Autowiring instancias específicas de clases usando parámetros de tipo genérico

Si tiene una interfaz con un parámetro de tipo genérico, Spring puede usarlo solo para implementaciones de autowire que implementan un parámetro de tipo que usted especifique.

Interfaz:

public interface GenericValidator<T> {
    public T validate(T object);
}

Clase Validador Foo:

@Component
public class FooValidator implements GenericValidator<Foo> {
    @Override
    public Foo validate(Foo foo) {
        //Logic here to validate foo objects.
    }
}

Clase Validador de Barras:

@Component
public class BarValidator implements GenericValidator<Bar> {
    @Override
    public Bar validate(Bar bar) {
        //Bar validation logic here
    }
}

Ahora puede activar automáticamente estos validadores utilizando parámetros de tipo para decidir qué instancia autowire.

Interfaz:

public interface FooService {
    public void handleFoo(Foo foo);
}

Clase:

@Service
public class FooServiceImpl implements FooService {
    /** Autowire Foo Validator **/
    @Autowired
    private GenericValidator<Foo> fooValidator;

    @Override
    public void handleFoo(Foo foo) {
        foo = fooValidator.validate(foo);
    }   
}


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow