Ambiguity automatic assembly: Spring entry (eight)

1. What is the ambiguity automatic assembly?

In Spring, fitted bean has the following three ways:

  1. Automated assembly
  2. Java configuration
  3. xml configuration

Among these three, the automated assembly brought us great convenience, greatly reducing the amount of code that we need to manually assemble the bean.

However, automatic assembly is not a panacea, because there is only one bean matching condition, Spring automatic assembly can be achieved, if more than one bean matching condition occurs, Spring will not know which bean to be assembled, throws org.springframework.beans.factory.NoUniqueBeanDefinitionExceptionan exception, which is automatic assembly ambiguity.

To facilitate understanding, we give a concrete example.

First, we create interfaces Dessert, this interface is only one method showName ():

package chapter03.ambiguity;

public interface Dessert {
    void showName();
}

3 then define the interface implementation class Cake, Cookies, IceCream:

package chapter03.ambiguity;

import org.springframework.stereotype.Component;

@Component
public class Cake implements Dessert {
    @Override
    public void showName() {
        System.out.println("蛋糕");
    }
}
package chapter03.ambiguity;

import org.springframework.stereotype.Component;

@Component
public class Cookies implements Dessert {
    @Override
    public void showName() {
        System.out.println("饼干");
    }
}
package chapter03.ambiguity;

import org.springframework.stereotype.Component;

@Component
public class IceCream implements Dessert {
    @Override
    public void showName() {
        System.out.println("冰激凌");
    }
}

Then stores the new class dessert DessertShop, class setDessert () method requires assembly of Example 1 Dessert bean:

package chapter03.ambiguity;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class DessertShop {
    private Dessert dessert;

    public Dessert getDessert() {
        return dessert;
    }

    @Autowired
    public void setDessert(Dessert dessert) {
        this.dessert = dessert;
    }

    public void showDessertName() {
        this.dessert.showName();
    }
}

But now in line with the conditions of the assembly has three bean, their bean ID (default is the class name first letter lowercase) respectively, cake, cookies, iceCream, Spring assembly which does this automatically?

With this question, we first create a new configuration class AmbiguityConfig:

package chapter03.ambiguity;

import org.springframework.context.annotation.ComponentScan;

@ComponentScan
public class AmbiguityConfig {
}

The key to this class is to add a @ComponentScancomment, make Spring automatically scan has been defined bean.

Finally, the new class Main, add the following test code in its main () method:

package chapter03.ambiguity;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AmbiguityConfig.class);

        DessertShop dessertShop = context.getBean(DessertShop.class);
        dessertShop.showDessertName();

        context.close();
    }
}

Run the code found thrown org.springframework.beans.factory.NoUniqueBeanDefinitionExceptionexception, as follows:

So how to solve the ambiguity of the automatic assembly of it? Spring provides the following two scenarios:

  1. Marking the first choice of bean
  2. Qualifier

2. Mark the bean of choice

Now that there are bean 3 matching criteria, we can @Primaryannotate mark under which is the preferred bean, so that when Spring found more than bean 1 matching criteria, will select the preferred bean.

For example, three kinds of desserts, I like to eat cookies, then I'll Cookies marked as preferred bean:

package chapter03.ambiguity;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

@Component
@Primary
public class Cookies implements Dessert {
    @Override
    public void showName() {
        System.out.println("饼干");
    }
}

Re-run the test code, the output results are as follows:

Biscuit

A satisfactory solution to the problem of ambiguity, but one day, a colleague accidentally on IceCream add a @Primarycomment:

package chapter03.ambiguity;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

@Component
@Primary
public class IceCream implements Dessert {
    @Override
    public void showName() {
        System.out.println("冰激凌");
    }
}

Compile all normal, and therefore did not pay attention, but when running after the release, but it throws the following exception:

Meaning is found in more than one choice of bean, Spring do not know at this time because the choice of which, that is, has a new ambiguity, it throws an exception thrown pot.

3. Use qualifier

3.1 Based on the bean ID qualifier

Spring also provides another comment @Qualifierambiguity annotations to solve automatic assembly, it can be with @Autowiredor @Injectused together to specify which bean you want to inject at the time of injection.

For example, we put IceCream injected into setDessert () method parameters among:

@Autowired
@Qualifier("iceCream")
public void setDessert(Dessert dessert) {
    this.dessert = dessert;
}

Herein refers to the transfer of iceCream IceCream default class generated bean ID.

Re-run the test code, the output results are as follows:

Ice cream

We can see that the use of @Qualifierthe comments, before we mark the @Primarycomment is ignored, that is to say, @Qualifierannotated priority than @Primaryhigh priority annotations.

Use the default qualifier solves the problem, but may introduce some problems. For example, when I refactor the code, the IceCream class name modified into Gelato:

package chapter03.ambiguity;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

@Component
@Primary
public class Gelato implements Dessert {
    @Override
    public void showName() {
        System.out.println("冰激凌");
    }
}

At this time, running the code shows that throw org.springframework.beans.factory.NoSuchBeanDefinitionExceptionexception, as follows:

This is because IceCream was renamed after Gelato, bean ID by the iceCream into a gelato, but we inject local code is still using iceCream this bean ID, leading to bean matching condition is not found.

Given this limitation using the default qualifier, we can use a custom qualifier to solve this problem.

Do not affect the test results, after the code change back to the class IceCream Gelato

3.2 based on the characteristics for the qualifiers

To avoid the problem modify the class name as a result of failure of the automatic assembly, we can add a comment on @Qualifier when @Component or @Bean annotation statement bean, as follows:

package chapter03.ambiguity;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component
@Qualifier("cold")
public class IceCream implements Dessert {
    @Override
    public void showName() {
        System.out.println("冰激凌");
    }
}

Then injected in place, no longer use the bean ID generated by default, but using just specified cold qualifiers:

@Autowired
@Qualifier("cold")
public void setDessert(Dessert dessert) {
    this.dessert = dessert;
}

Run test code, input results are shown below:

Ice cream

At this time, the class IceCream rename Gelato, code can run normally, will not be affected.

Then one day, someone has developed a new class Popsicle, the class also uses cold qualifiers:

package chapter03.ambiguity;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component
@Qualifier("cold")
public class Popsicle implements Dessert {
    @Override
    public void showName() {
        System.out.println("棒冰");
    }
}

In this case also brought new problems of ambiguity, because Spring did not know how to choose, and run the code will throw org.springframework.beans.factory.NoUniqueBeanDefinitionExceptionan exception, as follows:

At this point, we need to use a custom qualifier.

3.3 custom qualifier annotations

First, we create the following three notes:

package chapter03.ambiguity;

import org.springframework.beans.factory.annotation.Qualifier;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Cold {
}
package chapter03.ambiguity;

import org.springframework.beans.factory.annotation.Qualifier;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Creamy {
}
package chapter03.ambiguity;

import org.springframework.beans.factory.annotation.Qualifier;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Fruity {
}

Note: These three notes are added when defining a @Qualifier notes, so they have a @Qualifier annotation features

Then IceCream class amended as follows:

package chapter03.ambiguity;

import org.springframework.stereotype.Component;

@Component
@Cold
@Creamy
public class IceCream implements Dessert {
    @Override
    public void showName() {
        System.out.println("冰激凌");
    }
}

The Popsicle class amended as follows:

package chapter03.ambiguity;

import org.springframework.stereotype.Component;

@Component
@Cold
@Fruity
public class Popsicle implements Dessert {
    @Override
    public void showName() {
        System.out.println("棒冰");
    }
}

Finally, at the place of injection to modify the code so that it can only be matched to one of the bean to meet the conditions, as follows:

@Autowired
@Cold
@Creamy
public void setDessert(Dessert dessert) {
    this.dessert = dessert;
}

Run the test code, the output results are as follows:

Ice cream

Thus, we can also find, custom annotations and notes @Qualifier comparison, the following two advantages:

  1. You can use multiple custom notes simultaneously, but can only use one @Qualifier annotation
  2. Use custom annotation type is more secure than @Qualifier comment

4. The reference source and

Source Address: https://github.com/zwwhnly/spring-action.git , welcome to download.

Craig Walls "Spring combat (4th Edition)"

5. Finally

Play a little advertising, welcomed the focus on micro-channel scan code number public: "Shanghai is a stranger" on a regular basis to share Java technology dry goods, so that we progress together.

Guess you like

Origin www.cnblogs.com/zwwhnly/p/11355879.html