环境与配置
由于在项目开发时和项目配置时, 可能会有一些代码的不同, 如何根据不同的环境来进行配置. Spring有bean profile的功能.
在JavaConfig中配置profile
在Java配置中, 使用@Profile注解来确定这个bean属于哪一个profile.
public class Config{
@Bean
@Profile("dev")
public ClassName function1(){
...
}
@Bean
@Profile("prod")
public ClassName function2(){
...
}
}
在XML中配置profile
激活profile
Spring依据两个独立的属性来判断激活哪一个profile. 这两个独立的属性便是spring.profiles.active和spring.profiles.default. 如果设置了active参数, 则根据该值来判断激活哪一个profile. 若没有, 则根据default值来判断, 若两个值都未设置, 则只会加载那些没有定义在profile中的bean.
条件化bean
Spring4之后引入了@Conditional注解, 给定的计算结果为true, 则创建该bean, 否则该bean会被忽略.
@Bean
@Conditional(MagicExistsCondition.class)
public .....{
...
}
public class MagicExistsCondition implements Condition{
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata){
...
}
}
其中ConditionContext是一个接口, 主要有以下几个作用:
- 借助getRegistry()返回的BeanDefinitionRegistry检查bean定义
- 借助getBeanFactory()返回的ConfigurableListableBeanFactory检查bean是否存在, 探查bean的属性
- 借助getEnvironment()返回的Environment检查环境变量是否存在以及它的值是什么
- 读取并探查getResourceLoader()返回的ResourceLoader所加载的资源
- 借助getClassLoader()返回的ClassLoader加载并检查类是否存在
AnnotatedTypeMetadata可以检查带有@Bean注解的方法上还有什么注解
可以借助isAnnotated(String str) 来判断带有@Bean注解的方法是否还有特定的注解
处理自动装配的歧义性
当有多个bean都满足某一装配要求时, 就会出现问题. 解决方法有:
- 标示首选的bean
- 使用限定符
标示首选的bean
@Primary可以来将一个bean标识为首选bean. @Primary既可以与@Component组合用在组件扫描的bean上, 也可以与@Bean组合用在JavaConfig文件中.
@Componen
@Primary
public class ClassName implements InterfaceName{
....
}
@Bean
@Primary
public ClassName getBean1(...){
....
}
当然, 也可以使用XML文件来标识首选bean
<bean id="id" class="ClassName"
primary="true"/>
不可以将多个可能产生歧义的bean设为首选.
使用限定符
标示首选bean只可以解决较为简单的bean歧义问题, 如果问题较为复杂, 则无法解决.
使用@Qualifier注解是使用限定符的主要方式. 它可以与@Autowired和@Inject协同使用.
@Autowired
@Qualifier("bean_id")
public void setArg1(InterfaceName i){
...
}
@Qualifier注解声明了想要注入的bean的ID.
@Qualifier也可以使用自定义的限定符而非bean的id, 方法如下:
@Component
@Qualifier("qualifier_id")
public class ClassName implements InterfaceName{
...
}
使用自定义限定符
当一个限定符仍然不可以解决歧义问题, 就需要使用自定义限定符号(Java8之前版本不支持同一个条目重复出现多个相同类型的注解).
注: Java8允许出现重复注解, 只要注解本身在定义的时候带有@Repeatable即可.
@Target({ElementType.CONSTRUCTOR,ElementType.FIELD,
ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Cold{
}
@Target({ElementType.CONSTRUCTOR,ElementType.FIELD,
ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Warm{
}
使用:
@Component
@Cold
@Warm
public class ClassName implements InterfaceName{
...
}
Bean的作用域
Spring定义了多个作用域:
- 单例(Singleton): 在整个应用中, 只创建一个实例( 默认实现)
- 原型(Prototype): 每次注入或者通过Spring应用上下文获取的时候, 都会创建一个新的实例
- 会话(Session): 在Web应用中, 为每一个回话创建一个bean实例
- 请求(Request): 在Web应用中, 为每个请求创建一个bean实例
如果要使用单例以外的作用域, 可以使用@Scope注解.
使用组件扫描来发现和声明bean, 则可以在bean的类上使用@Scope注解
@Componet
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ClassName{
}
使用@Scope("prototype")
也是可以的, 但是更易出错.
同样可以在Java配置中来声明原型bean
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public ClassName getArg(){
....
}
使用XML文件也可以来配置bean.
<bean id="id"
class="ClassName"
scope="prototype"/>