Recherche…


En utilisant tous les haricots d'un type spécifique

Si vous disposez de plusieurs implémentations de la même interface, Spring peut les envoyer automatiquement dans un objet de collection. Je vais utiliser un exemple utilisant un modèle de validateur 1

Classe Foo:

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

Interface:

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

Classe de validateur de nom:

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

Classe de validation de courrier électronique:

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

Vous pouvez maintenant envoyer ces validateurs individuellement ou ensemble dans une classe.

Interface:

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

Classe:

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

Il est intéressant de noter que si vous avez plus d'une implémentation d'une interface dans le conteneur Spring IoC et que vous ne spécifiez pas celle que vous souhaitez utiliser avec l'annotation @Qualifier , Spring lancera une exception en essayant de démarrer, car elle a gagné. t savoir quelle instance utiliser.

1: Ce n'est pas la bonne façon de faire de telles validations simples. Ceci est un exemple simple d’auto-activation. Si vous voulez une idée d'une méthode de validation beaucoup plus facile, regardez comment Spring effectue la validation avec les annotations.

Déclarer le haricot

Pour déclarer un bean, @Bean simplement une méthode avec l'annotation @Bean ou @Bean une classe avec l'annotation @Component (les annotations @Service , @Repository , @Controller peuvent également être utilisées).

Lorsque JavaConfig rencontre une telle méthode, il exécute cette méthode et enregistre la valeur de retour en tant que bean dans une BeanFactory. Par défaut, le nom du bean sera celui du nom de la méthode.

Nous pouvons créer un haricot de trois manières différentes:

  1. Utilisation de la configuration basée sur Java : Dans le fichier de configuration, nous devons déclarer le bean en utilisant l’annotation @bean

    @Configuration
    public class AppConfig {
        @Bean
        public TransferService transferService() {
            return new TransferServiceImpl();
        }
    }
    
  2. Utilisation de la configuration basée sur XML : Pour une configuration basée sur XML, nous devons créer declare bean dans le XML de configuration de l'application, par exemple

     <beans>
         <bean name="transferService" class="com.acme.TransferServiceImpl"/>
     </beans>
    
  3. Composant piloté par des annotations: Pour les composants pilotés par des annotations, nous devons ajouter l'annotation @Component à la classe que nous voulons déclarer en tant que bean.

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

Les trois beans BeanFactory transferService sont BeanFactory disponibles dans BeanFactory ou ApplicationContext .

Annotation de base automatique

Interface:

public interface FooService {
    public int doSomething();
}

Classe:

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

Il convient de noter qu'une classe doit implémenter une interface pour que Spring puisse générer automatiquement cette classe. Il existe une méthode pour autoriser Spring à créer automatiquement des classes autonomes en utilisant le tissage du temps de chargement, mais cela est hors de portée pour cet exemple.

Vous pouvez accéder à ce bean dans toute classe instanciée par le conteneur Spring IoC à l'aide de l'annotation @Autowired .

Usage:

@Autowired([required=true])

L'annotation @Autowired abord de s'exprimer automatiquement par type, puis utilisera le nom du bean en cas d'ambiguïté.

Cette annotation peut être appliquée de différentes manières.

Injection constructeur:

public class BarClass() {
    private FooService fooService         

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

Injection de champ:

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

Injection Setter:

public class BarClass() {
    private FooService fooService;

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

Utilisation de FactoryBean pour l'instanciation dynamique du bean

Afin de décider dynamiquement des beans à injecter, nous pouvons utiliser FactoryBean . Ce sont des classes qui implémentent le modèle de méthode de fabrique, fournissant des instances de beans pour le conteneur. Ils sont reconnus par le printemps et peuvent être utilisés de manière transparente, sans qu'il soit nécessaire de savoir que le grain provient d'une usine. Par exemple:

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

Configuration:

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

Obtenir le haricot:

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

Pour obtenir le FactoryBean réel, utilisez le préfixe ampersand avant le nom du bean:

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

S'il vous plaît noter que vous ne pouvez utiliser des prototype ou singleton champs - pour changer la portée de prototype override isSingleton méthode:

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

    // other methods omitted for readability reasons
}

Notez que la portée fait référence aux instances réelles en cours de création, pas au bean de fabrication lui-même.

Injecter des haricots à prototypes dans des singletons

Le conteneur crée un bean singleton et n'y injecte qu'une seule fois des collaborateurs. Ce comportement n’est pas souhaitable lorsque le bean singleton a un collaborateur de portée prototype, car le bean à portée de prototype doit être injecté à chaque fois qu’il est accédé via l’accesseur.

Il existe plusieurs solutions à ce problème:

  1. Utiliser l'injection de la méthode de recherche
  2. Récupère un bean prototype sur javax.inject.Provider
  3. Récupérer un bean prototype à portée via org.springframework.beans.factory.ObjectFactory (équivalent à # 2, mais avec la classe spécifique à Spring)
  4. Rendre conscient un conteneur de beans singleton via l'implémentation de l'interface ApplicationContextAware

Les approches n ° 3 et n ° 4 sont généralement déconseillées car elles associent fortement une application au framework Spring. Ainsi, ils ne sont pas couverts dans cet exemple.

Injection de méthode de recherche via une configuration XML et une méthode abstraite

Classes 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>

Injection de la méthode de recherche via la configuration Java et @Component

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

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

Injection de méthode de recherche manuelle via la configuration Java

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

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

Injection d'un bean à protoype dans singleton via javax.inject.Provider

Classes 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>

Les mêmes approches peuvent également être utilisées pour d'autres domaines (par exemple, pour l'injection d'un bean à portée de requête dans singleton).

En utilisant des instances de bean spécifiques avec @Qualifier

Si vous disposez de plusieurs implémentations de la même interface, Spring doit savoir laquelle elle doit utiliser dans une classe. Je vais utiliser un modèle de validateur dans cet exemple. 1

Classe Foo:

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

Interface:

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

Classe de validateur de nom:

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

Classe de validation de courrier électronique:

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

Vous pouvez maintenant envoyer ces validateurs individuellement dans une classe.

Interface:

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

Classe:

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

Il est intéressant de noter que si vous avez plus d'une implémentation d'une interface dans le conteneur Spring IoC et que vous ne spécifiez pas celle que vous souhaitez utiliser avec l'annotation @Qualifier , Spring lancera une exception en essayant de démarrer, car elle a gagné. t savoir quelle instance utiliser.

1: Ce n'est pas la bonne façon de faire de telles validations simples. Ceci est un exemple simple d’auto-activation. Si vous voulez une idée d'une méthode de validation beaucoup plus facile, regardez comment Spring effectue la validation avec les annotations.

En utilisant des instances spécifiques de classes en utilisant des paramètres de type génériques

Si vous disposez d'une interface avec un paramètre de type générique, Spring peut l'utiliser pour ne générer que des implémentations implémentant un paramètre de type que vous spécifiez.

Interface:

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

Foo Validator Classe:

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

Classe de validateur de barre:

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

Vous pouvez maintenant envoyer ces validateurs en utilisant les paramètres de type pour décider quelle instance envoyer automatiquement.

Interface:

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

Classe:

@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
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow