SpringAction学习二、高级装配:处理自动装配歧义

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/MASORL/article/details/82659656

方法一:标示首选的bean

接口1:

package com.spring.ambiguity;

/**
 * 一个光盘接口
 */
public interface CompactDisc {
    void sing();//光盘的sing功能
}

接口1的第一个实现

package com.spring.ambiguity;


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

/**
 * CompactDisc实现类
 */
@Component
//@Primary
public class SgtPeppers implements CompactDisc {
    public void sing() {
        System.out.println("sgtPeppers的sing方法");
    }
}

接口1的第二个实现:加入了@Primary,标记成首选bean

package com.spring.ambiguity;


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

/**
 * CompactDisc实现类
 */
@Component
@Primary

public class OtherSgtPeppers implements CompactDisc {
    public void sing() {
        System.out.println("OtherSgtPeppers的sing方法");
    }
}

接口1 的第三个实现

package com.spring.ambiguity;


import org.springframework.stereotype.Component;

/**
 * CompactDisc实现类
 */
@Component
public class AnotherSgtPeppers implements CompactDisc {
    public void sing() {
        System.out.println("AnotherSgtPeppers的sing方法");
    }
}

接口2

package com.spring.ambiguity;

/**
 * 媒体播放器
 */
public interface MediaPlayer {
    void play(); //一个播放方法
}

接口2的实现

package com.spring.ambiguity;

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

@Component
public class CDPlayer implements MediaPlayer {
    private CompactDisc compactDisc;

    @Autowired
    public CDPlayer(CompactDisc compactDisc){
        this.compactDisc = compactDisc;
    }

    public void play() {
        System.out.println("CDPlayer");
        compactDisc.sing();
    }
}

测试

package com.spring.ambiguity;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import static org.junit.Assert.*;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AmbiguityConfig.class)
public class AmbiguityConfigTest {
    @Autowired
    private CompactDisc compactDisc;

    @Autowired
    private MediaPlayer mediaPlayer;

    @Test
    public void testProfiles(){
        mediaPlayer.play();
    }


}

测试结果:注入的是标志为首选Bean的OtherSgtPeppers

WARNING: All illegal access operations will be denied in a future release
CDPlayer
OtherSgtPeppers的sing方法

也可以通过xml来标志首选bean【不推荐,自动装配还是用注解吧,这样有点乱】

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.spring.ambiguity.xmlAmbiguity"/>
    <bean id="sgtPeppers" class="com.spring.ambiguity.xmlAmbiguity.SgtPeppers"/>
    <bean id="otherSgtPeppers" class="com.spring.ambiguity.xmlAmbiguity.OtherSgtPeppers" primary="true"/>
    <bean id="anotherSgtPeppers" class="com.spring.ambiguity.xmlAmbiguity.AnotherSgtPeppers"/>
</beans>

方式二:限定自动装配的bean

接下来的自动装配只考虑一种情况,所有bean都用@Component标示,配置类中启动组件扫描

直接通过@Qualifier("bean id") 与@Autowired 组合使用,找到对应的bean id进行注入

package com.spring.ambiguity.quaifier;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = QuaifierConfig.class)
public class QuaifierConfigTest {
    @Autowired
    @Qualifier("anotherSgtPeppers")
    private CompactDisc compactDisc;

    @Autowired
    private MediaPlayer mediaPlayer;

    @Test
    public void testProfiles(){
        compactDisc.sing();
    }
}

方式三:自定义限定符

通过@Component 与 @Qualifier连用,为bean 创建自定义限定符名称

@Component
@Qualifier("oks")
public class AnotherSgtPeppers implements CompactDisc {
    public void sing() {
        System.out.println("AnotherSgtPeppers的sing方法");
    }
}

测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = QuaifierConfig.class)
public class QuaifierConfigTest {
    @Autowired
//    @Qualifier("anotherSgtPeppers")
    @Qualifier("oks")
    private CompactDisc compactDisc;

    @Autowired
    private MediaPlayer mediaPlayer;

    @Test
    public void testProfiles(){
        compactDisc.sing();
    }
}

方式四:自定义一个注解

有点,当需要为一个bean定义多个限定符时,又不能使用多个@Qulifier(同名注解只能存在一个),所以自定义一个注解并使其拥有@Qulifier功能,完美解决这个问题

自定义方式如下

package com.spring.ambiguity.quaifier;

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 Okss {
}

使用这个注解

/**
 * CompactDisc实现类
 */
@Component
//@Qualifier("oks")
@Okss
public class AnotherSgtPeppers implements CompactDisc {
    public void sing() {
        System.out.println("AnotherSgtPeppers的sing方法");
    }
}

测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = QuaifierConfig.class)
public class QuaifierConfigTest {
    @Autowired
//    @Qualifier("anotherSgtPeppers")
    @Okss
    private CompactDisc compactDisc;

    @Autowired
    private MediaPlayer mediaPlayer;

    @Test
    public void testProfiles(){
        compactDisc.sing();
    }
}

补充:bean中的自动装配bean

package com.spring.ambiguity.quaifier;

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

@Component
public class CDPlayer implements MediaPlayer {
    private CompactDisc compactDisc;

    @Autowired
    public CDPlayer(CompactDisc compactDisc) {
        this.compactDisc = compactDisc;
    }

    public void play() {
        System.out.println("CDPlayer");
        compactDisc.sing();
    }
}

CDPlayer中自动装配CompactDisc再这里是存在歧义。怎么解决,第一个想到的就是@Qualifier方法来指定bean,但是!显示@Qualifier不能用在构造器方法上。

所以,这个方法不行,那么,通过setter方法注入呢

先添加了一个setter方法,并使用@Qualifier标注注入bean id为anotherSgtPeppers的bean,同时注释了构造方法上的@Autowired

package com.spring.ambiguity.quaifier;

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

@Component
public class CDPlayer implements MediaPlayer {
    private CompactDisc compactDisc;

    @Autowired
    @Qualifier("anotherSgtPeppers")
    public void setCompactDisc(CompactDisc compactDisc) {
        System.out.println("进入了setter方法");
        this.compactDisc = compactDisc;
    }

//    @Autowired
    public CDPlayer(CompactDisc compactDisc) {
        this.compactDisc = compactDisc;
    }
    

    public void play() {
        System.out.println("CDPlayer");
        compactDisc.sing();
    }
}

测试下,结果报错,错误的原因是错误的创建CDPlayer bean,NoUniqueBeanDefinitionException: No qualifying bean of 
type 'com.spring.ambiguity.quaifier.CompactDisc':没有限定CompactDisc。

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating
 bean with name 'CDPlayer' defined in file [D:\更改的路径\Desktop\临时
\14\SpringAction\target\classes\com\spring\ambiguity\quaifier\CDPlayer.class]: Unsatisfied 
dependency expressed through constructor parameter 0; nested exception is 
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of 
type 'com.spring.ambiguity.quaifier.CompactDisc' available: expected single matching bean 
but found 3: anotherSgtPeppers,otherSgtPeppers,sgtPeppers

接下来试着把构造器给注释掉,然后添加一个无参的构造器

package com.spring.ambiguity.quaifier;

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

@Component
public class CDPlayer implements MediaPlayer {
    private CompactDisc compactDisc;

    @Autowired
    @Qualifier("anotherSgtPeppers")
    public void setCompactDisc(CompactDisc compactDisc) {
        System.out.println("进入了setter方法");
        this.compactDisc = compactDisc;
    }

    public CDPlayer() {
        System.out.println("进入了构造器");
    }

    //    @Autowired
//    public CDPlayer(CompactDisc compactDisc) {
//        this.compactDisc = compactDisc;
//    }


    public void play() {
        System.out.println("CDPlayer");
        compactDisc.sing();
    }
}

测试结果,结果发现,就算没有@Autowire,也会进入构造器【创建bean实例当然需要构造器。。。】

这下就解释了,一开始的有参构造器,因为CompactDIsc有3个bean可以注入,所以测试结果会出错

进入了构造器
进入了setter方法
CDPlayer
AnotherSgtPeppers的sing方法

所以,第一种办法就是,不要提供有参构造器,然后再setter里面通过@Autowrie和@Qualifier来限定bean

那么,就想用构造器怎么办,试试看自定义注释

package com.spring.ambiguity.quaifier;

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

@Component
public class CDPlayer implements MediaPlayer {
    private CompactDisc compactDisc;

//    @Autowired
//    @Qualifier("anotherSgtPeppers")
//    public void setCompactDisc(CompactDisc compactDisc) {
//        System.out.println("进入了setter方法");
//        this.compactDisc = compactDisc;
//    }

//    public CDPlayer() {
//        System.out.println("进入了构造器");
//    }

    @Okss
    public CDPlayer(CompactDisc compactDisc) {
        System.out.println("进入了有参构造器");
        this.compactDisc = compactDisc;
    }


    public void play() {
        System.out.println("CDPlayer");
        compactDisc.sing();
    }
}

测试:结果可行!

进入了有参构造器
CDPlayer
AnotherSgtPeppers的sing方法

得出的结论就是

(1)任何bean的产生,都需要一个构造参数

(2)一个bean作为另一个bean的参数,即使不用@Autowire,也会自动注入

(3)如果想要再方法里注入bean,那么就必须要@Autowire了

猜你喜欢

转载自blog.csdn.net/MASORL/article/details/82659656