Spring journey -- 处理自动装配歧义性问题
2017-03-04 19:58
381 查看
当需要把一个 Bean 装配到另一个 Bean 中时,一般都是给传一个接口,但是如果这个接口有多个实现类的话,Spring 就会不知道应该把哪一个实现类传过去,这个时候就会出现歧义性,像下面的这段代码:
Dessert 是一个接口:
它的三个实现类:
Cake:
Cookie:
IceCream:
写一个测试类:
运行之后的结果就是这样:
解决办法:可以给三个当中任意一个实现加一个@Primary 注解,用来确定维一性。但是如果给了两个实现上都加了@Primary 呢,结果是必然的,因为这是肯定不是唯一了撒。
@Qualifier 是使用限定符的主要方式,用来缩小选择范围。像下面这样,是不会报错的(此时@Primary 已经不在使用了):
从上面的代码可以看到@Qualifier 里指定了『iceCream』,这个名字是IceCream 类在组件扫描时自动创建的,也就默认的名字,但是如果后面对这个类进行重构后,名字发生了改变,这里肯定就会报错了,自动装配就会失败。即 setDessert()上所指定的限定符与要注入的 bean 的名称是紧耦合的。对类名称的任意改动都会导致限定符失效。就像下面这样,我把 IceCream 的名字改成这样:
上面这里已经改了 IceCream 的名字,再去运行上面的测试代码时就会报错了,解决方法是要把 setDessert() 的 @Qualifier 改成下面这样:
所以这里就充分的说明了 setDessert()上面的限定符与要注入的 bean 的名称是紧耦合的关系。
那么解决办法是创建自定义的限定符,我们可以了 bean 设置自己的限定符,而不是依赖于将 beanID 作为出限定符。
现在把 IceCream 类改成这样:
那么在 setDessert()方法上面就可以这样写了:
面向特性的限定符要比基于 bean ID 的限定符更好一些。但是这个时候如果多个 bean 出现相同的特性的话,那上面的这种方法又会不能唯一确定到一个指定的 bean 了。如果你想再 setDessert()上面加多个限定符,情理之中,但是 Java 不允许在同一个条目上重复出现相同类型的多个注解,解决办法:创建自定义的限定符注解
自定义注解@Cold:
再来一个:
现在把这两个注解添加到 IceCream 上面:
为了得到 IceCream bean , 在 setDessert()上面就应该这样写了:
现在回过头来看 setDessert 方法以及它上面的注解,可以发现没有任何地方明确指定要把 IceCream 自动装配到该方法中,所以 setDessert()方法可以与特定的 bean 保持解耦关系。
Dessert 是一个接口:
package com.springinaction.dessert; /** * Created by user on 3/4/17. */ public interface Dessert { }
它的三个实现类:
Cake:
package com.springinaction.dessert; import org.springframework.stereotype.Component; /** * Created by user on 3/4/17. */ @Component public class Cake implements Dessert { }
Cookie:
package com.springinaction.dessert; import org.springframework.stereotype.Component; /** * Created by user on 3/4/17. */ @Component public class Cookie implements Dessert { }
IceCream:
package com.springinaction.dessert; import org.springframework.stereotype.Component; /** * Created by user on 3/4/17. */ @Component() public class IceCream implements Dessert { }
写一个测试类:
package com.springinaction.dessert; 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.assertNotNull; /** * Created by user on 3/4/17. */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = DessertConfig.class) public class DessertTest { @Autowired private Cookie cookie; @Test public void cdShouldNotBeNull(){ assertNotNull(cookie); } }
运行之后的结果就是这样:
解决办法:可以给三个当中任意一个实现加一个@Primary 注解,用来确定维一性。但是如果给了两个实现上都加了@Primary 呢,结果是必然的,因为这是肯定不是唯一了撒。
@Qualifier 是使用限定符的主要方式,用来缩小选择范围。像下面这样,是不会报错的(此时@Primary 已经不在使用了):
package com.springinaction.dessert; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; /** * Created by user on 3/4/17. */ @Component public class HandleQualifier { private Dessert dessert; @Autowired @Qualifier("iceCream") public void setDessert(Dessert dessert){ this.dessert = dessert; } }
从上面的代码可以看到@Qualifier 里指定了『iceCream』,这个名字是IceCream 类在组件扫描时自动创建的,也就默认的名字,但是如果后面对这个类进行重构后,名字发生了改变,这里肯定就会报错了,自动装配就会失败。即 setDessert()上所指定的限定符与要注入的 bean 的名称是紧耦合的。对类名称的任意改动都会导致限定符失效。就像下面这样,我把 IceCream 的名字改成这样:
package com.springinaction.dessert; import org.springframework.stereotype.Component; /** * Created by user on 3/4/17. */ @Component("aa") public class IceCream implements Dessert { }
上面这里已经改了 IceCream 的名字,再去运行上面的测试代码时就会报错了,解决方法是要把 setDessert() 的 @Qualifier 改成下面这样:
package com.springinaction.dessert; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; /** * Created by user on 3/4/17. */ @Component public class HandleQualifier { private Dessert dessert; @Autowired @Qualifier("aa") public void setDessert(Dessert dessert){ this.dessert = dessert; } }
所以这里就充分的说明了 setDessert()上面的限定符与要注入的 bean 的名称是紧耦合的关系。
那么解决办法是创建自定义的限定符,我们可以了 bean 设置自己的限定符,而不是依赖于将 beanID 作为出限定符。
现在把 IceCream 类改成这样:
package com.springinaction.dessert; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; /** * Created by user on 3/4/17. */ @Component() @Qualifier("cold") public class IceCream implements Dessert { }
那么在 setDessert()方法上面就可以这样写了:
package com.springinaction.dessert; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; /** * Created by user on 3/4/17. */ @Component public class HandleQualifier { private Dessert dessert; @Autowired @Qualifier("cold") public void setDessert(Dessert dessert){ this.dessert = dessert; } }
面向特性的限定符要比基于 bean ID 的限定符更好一些。但是这个时候如果多个 bean 出现相同的特性的话,那上面的这种方法又会不能唯一确定到一个指定的 bean 了。如果你想再 setDessert()上面加多个限定符,情理之中,但是 Java 不允许在同一个条目上重复出现相同类型的多个注解,解决办法:创建自定义的限定符注解
自定义注解@Cold:
package com.springinaction.dessert; 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; /** * Created by user on 3/4/17. */ @Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface Cold { }
再来一个:
package com.springinaction.dessert; 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; /** * Created by user on 3/4/17. */ @Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface Creamy { }
现在把这两个注解添加到 IceCream 上面:
package com.springinaction.dessert; import org.springframework.stereotype.Component; /** * Created by user on 3/4/17. */ @Component() @Cold @Creamy public class IceCream implements Dessert { }
为了得到 IceCream bean , 在 setDessert()上面就应该这样写了:
package com.springinaction.dessert; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * Created by user on 3/4/17. */ @Component public class HandleQualifier { private Dessert dessert; @Autowired @Cold @Creamy public void setDessert(Dessert dessert){ this.dessert = dessert; } }
现在回过头来看 setDessert 方法以及它上面的注解,可以发现没有任何地方明确指定要把 IceCream 自动装配到该方法中,所以 setDessert()方法可以与特定的 bean 保持解耦关系。
相关文章推荐
- 十四、处理自动装配的歧义性
- 处理自动装配的歧义性
- 处理自动装配的歧义性
- Spring-处理自动装配的歧义性
- Spring基础处理-自动装配的歧义性
- Spring-处理自动装配的歧义性
- Spring 实战-第三章-处理自动装配的歧义性
- 《spring in action》--处理自动装配的歧义性
- 高级装配 —— 如何处理自动装配的歧义性?
- Spring-处理自动装配的歧义性
- SpringInAction 学习笔记:自动装配歧义性处理
- Silverlight Xap包、配置文件修改后不能自动更新问题处理方式总结
- web程序通过dbcp连接池处理自动重新连接数据库问题(通过连接池dbcp处理方案)
- 任务栏无法自动隐藏的问题处理
- Aop拦截Action 使得@Autowired 无法自动装配问题解决
- IE7 自动打开 http://runonce.msn.com/runonce3.aspx 问题处理
- freemarker中处理页面自动四舍五入问题
- extjs 3,struts 动态form 提交action不能自动装配值问题
- 解决firefox保存winrar时不能勾选“以后自动采用相同的动作处理此类文件”的问题
- VB 表格导出Excel 自动处理‘0’的问题