Spring learns from the past - bean assembly (continued)

 

Assemble beans conditionally

That is, the Spring container creates beans when certain conditions are met. In Spring, the @Conditional annotation is used to implement conditional configuration beans.

package com.sl.ioc;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

@Configuration
public  class AnimalConfig {
    
    @Bean("dog")
    @Conditional(DogCondition.class)
    public Dog DogInstance() {
        return new Dog();
    }
    
    @Bean("cat")
    @Conditional(CatCondition.class)
    public Cat CatInstance() {
        return new Cat();
    }
    
}

@Conditional and : Implementation of the Condition interface

public @interface Conditional {

    /**
     * All {@link Condition}s that must {@linkplain Condition#matches match}
     * in order for the component to be registered.
     */
    Class<? extends Condition>[] value();

}
public interface Condition {

    /**
     * Determine if the condition matches.
     * @param context the condition context
     * @param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
     * or {@link org.springframework.core.type.MethodMetadata method} being checked
     * @return {@code true} if the condition matches and the component can be registered,
     * or {@code false} to veto the annotated component's registration
     */
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}

The Conditional annotation passes in a class by value, implements the Condition interface, and decides whether to assemble the bean by implementing the matches method in the Condition interface. If the conditions are met, the bean needs to be created, and it returns true, otherwise it returns false

Define two classes that inherit the Condition interface: Find whether there is a dog or cat attribute in the current environment through the ConditionContext, and if so, create the corresponding bean object. The specific implementation is as follows:

package com.sl.ioc;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class DogCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {       
        Environment environment = context.getEnvironment();        
        boolean flag= environment.containsProperty("dog");        
        return flag;
    }
}

 

package com.sl.ioc;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class CatCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        
        Environment environment = context.getEnvironment();
        
        boolean flag= environment.containsProperty("cat");
        
        return flag;
        
    }
}
package com.sl.ioc;
import org.springframework.stereotype.Component;
@Component
public  interface Animal {
    
    void Say();
    
}

package com.sl.ioc;import org.springframework.stereotype.Component;

@Component
public class Cat implements Animal {
    @Override
    public void Say() {
        System.out.println("I am a cat");
    }
}

package com.sl.ioc;
import org.springframework.stereotype.Component;

@Component
public class Dat implements Animal {
    @Override
    public void Say() {
        System.out.println("I am a dog");
    }
}

Test code:

public class TestClass {

    @Test
    public void TestGetDoInstance() {
        
        System.setProperty("dog","");    
    ApplicationContext context = new AnnotationConfigApplicationContext(AnimalConfig.class);
        
        String [] beanNames = context.getBeanDefinitionNames ();

        for (String bean: beanNames) {

                System.out.println(bean);

        }
    }
}

Run the test and you can see that the output beanname will contain the dog's bean:

Autowiring ambiguity

If there are multiple beans that can match during Spring autowiring, then this situation will prevent Spring from wiring by properties, constructors or methods. In response to this situation, Spring provides a variety of options to solve this problem, you can choose a bean as the preferred bean, or use a qualifier to determine the only bean

1: Use Preferred Beans

Spring provides the @Primary annotation to set the preferred bean. When the autowiring ambiguity is selected, it will choose to assemble the bean with @Primary

Following the example code above, try to load animal

@Component
public class AnimalInstance {

    @Autowired
    public Animal animal;
       
}

When Spring tries to inject an animal instance, since both Dog and Cat inherit from Animal, there is ambiguity here. The following is to specify the preferred bean by using @Primary

@Component
@Primary    // Specify the preferred bean 
public  class Cat implements Animal {
    @Override
    public void Say() {
        System.out.println("I am a cat");
    }
}

This can also be done using XML configuration: the <bean> element provides the primary attribute to set the preferred bean

<bean id="cat"  class="com.sl.ioc.Cat" primary ="true" >

Test code:

public class TestClass {
    @Test
    public  void TestGetDoInstance() {
         // Application context 
        
        ApplicationContext context = new AnnotationConfigApplicationContext(AnimalConfig. class );
        
        AnimalInstance animalInstance = context.getBean(AnimalInstance.class);
        animalInstance.animal.Say();
    }
}

operation result:

The preference only identifies a bean that is preferentially loaded. If multiple @Primary is configured, it will bring new ambiguity. Spring still cannot complete automatic assembly. This problem can be solved by the following qualifiers

2: Use qualifiers

Spring provides the @Qualifier annotation to specify the specific bean you want to inject. For example, in the above example, if you specify the injection dog:

package com.sl.ioc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component
public class AnimalInstance {

    @Autowired
    @Qualifier("dog")
    public Animal animal;
    
}

Explain: @Qualifier("dog") indicates that the specified bean has the "dog" qualifier. If the bean in spring does not specify the qualifier, the default qualifier will be used, that is, the beanID will be used as the qualifier. So the above happens to use the ID of the dog bean as the qualifier. It can also be written as:

@Component
@Qualifier( "specialdog")     // Specify qualifier for bean 
public  class Dog implements Animal
{
    @Override
    public void Say() {
        System.out.println("I am a dog");
    }    
}
@Component
public class AnimalInstance {

    @Autowired
    @Qualifier( "specialdog")     // Use the qualifier defined above 
    public Animal animal;
}

Bean scope

While creating a bean instance, the Spring container also allows specifying the scope of the bean instance. There are several common scopes:

1: Singleton scope (Singleton)

2: Prototype scope (Prototype)

3: Session scope (Session)

4: Request scope (Request)

5: Global session scope (globalSession)

Singleton scope

In the whole application, the Spring IOC container creates only one instance for the bean using the singleton pattern, Spring will cache the bean instance, and any request for this type of bean will return the instance. Singletons are also Spring's default scope. The specific use is as follows, configured through XML

<bean id="beanid"  class="com.sl.ioc.xxx" scope="singleton" ></bean>

The <bean> element provides the scope attribute to set the singleton scope

Corresponding annotations:

@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)

Prototype prototype scope

A new bean instance is created every time it is injected or fetched from the Spring container:

<bean id="beanid"  class="com.sl.ioc.xxx" scope="prototype" ></bean>

The <bean> element provides the scope attribute to set the singleton scope

Corresponding annotations:

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

Session session scope

In a web application, for each session, the bean instance created by the Spring container according to the bean definition is only valid in the current session. The XML configuration is as follows:

<bean id="beanid"  class="com.sl.ioc.xxx" scope="session" ></bean>

For an HTTP Session, the Spring container will create a new bean instance according to the bean definition, and the bean is only valid within the current HTTP Session. Therefore, you can safely change the internal state of the bean instance as needed without affecting the bean instance in other Http Sessions. When the HTTP Session is finally discarded, the beans in the scope of the HTTP Session are also destroyed.

Request request scope

<bean id="beanid"  class="com.sl.ioc.xxx" scope="globalSession" ></bean>

 

In a web application, for each request, the Spring container creates a new bean instance based on the bean definition, which is only valid within the current request

<bean id="beanid"  class="com.sl.ioc.xxx" scope="request" ></bean>

 

The bean instance is only valid within the current request, and the bean will be destroyed after the request is processed.

globalSession global session scope

Similar to the session scope, except that it is used for web applications in the portlet environment. If in a non-portlet environment it will be considered session scoped.

 

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325473954&siteId=291194637