关于@Profile、@Conditional、@Primary、@Qualifier及@Scope等实现高级装配的spring注解

1、关于@Profile注解的介绍

@Profile注解主要用在针对不同环境而条件选择的注入bean

在开发过程中由于环境的不同,我们可能在针对某些功能,需要开发不同的实现,然而在某种环境中,只能激活其中一种实现,其他的实现处于不激活的状态。这个时候我们在需要创建的bean上添加@Profile注解,如@Profile("dev"),"dev"是激活的标识。当此没有注解被激活时,该注解下的所有@Bean都会被忽略掉,激活则相反。@Profile注解在spring3.1只能在类级别上注解,spring3.2开始可以在方法级别注解。

如:

@Profile("dev")
public class TestProfile{
    
    @Bean
    public TofuCai setTofuCai(){
        return new TofuCai;
    }

}

@profile的激活依赖两个独立的属性:spring.profiles.active和spring.profiles.default。

有多种方式来设置这两个属性

    作为DispatcherServlet的初始化参数;

    作为Web应用的上下文参数;

    作为JNDI的条目;

    作为环境变量;

    作为JVM的系统属性;

    在集成车市类上,使用@ActiveProfiles注解设置

如为上下文设置默认的profile:

<context-param>
        <param-name>spring.profiles.default</param-name>
        <param-value>dev</param-value>
  </context-param>

注意:可以同时激活多个profile,通过逗号分隔来实现

2、@Conditional条件化的bean

当我们需要将某个bean注入到Spring中时,需要考虑到,这个bean只有在满足某些条件才去创建时,这个时候我们在这个bean的类或法返回对象的方法上,添加@Conditional注解。

例如:

@Bean
@Conditional(TofuCaiCondition.class)
public TofuCai tofuCaiBean(){
    return new TofuCai();
}

我们在创建TofuCai这个Bean时,添加了创建的条件。

再添加这个注解后,我们需要实现Condition这个接口,只需提供matches的实现就好。

例如:

public class TofuCaiCondition implements Condition{
    public boolean matches(
         ConditionContext context, AnnotatedTypeMetadata metadata){
        Environment env = context.getEnvironment();
        return env.containsProperty("tofu");    
    }
}

其实@Profile的内部实现,也是使用了@Conditional这个注解

如下是spring 4中 @Profile的实现:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional(ProfileCondition.class)
public @interface Profilr{
    String[] value();
}

3、@Qualifier 处理装配的歧义性

先解释下何为歧义性:当使用@Autowired或者其他注解(如:@Inject)进行装配的时候,由于该引用下存在多个实现,spring不知晓从众多实现中取其中的哪一个进行装配。这我们称之为歧义性。而@Qualifier则能很好解决装配的歧义性。

在这之前我先说下@Primary这个注解。

当我们在某个需要注入的bean使用这个注解的时候,当发生歧义性的时候,spring就会装配有@Primary这个实现。需要注意的是,在多个实现中,只能最多只有一个实现可以使用这个注解,否则还是会产生歧义性。

这个时候,就轮到@Qualifier这个注解大显身手了。

在装配的额外增加@Qualifier("tofu")这个注解,就意味着spring会直接寻找Tofu这个bean。当然你可以在注入这个bean指定下ID,同样加上@Qualifier这个注解。例如:

@Component
@Qualifier("cai")
public class Tofu implement TofuCai{
    
}

 注意:@Qualifier注解可以跟注入bean的注解一起使用,比如@Bean也是可以结合使用的,注意其不同组合之间的含义。在与注入bean的相关注解使用时,是代表限定符的意思,在装配的时候某种角度来说,是引用的指向的意思。

@Autowired
@Qualifier("cai")
public void setTofuCai(TofuCai tofuCai){
    this.tofuCai = tofuCai;
}

其实介绍到现在基本上已经很难再存在歧义性的问题了,但是有时候吧,有的人在注入bean的时候,限定符重复使用。这就导致在装配时,又出现歧义性。而且Java是不允许同一个条目上重复出现相同的注解。因此spring提供终极解决方案,即使用自定义的限定符注解,就是用户自己定义限定符注解,既可单个使用,也可组合使用。

例如:

@Target({ElementType.CONSTRUCTOR,ElementType.FILED,
        ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Cai{}
@Target({ElementType.CONSTRUCTOR,ElementType.FILED,
        ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Tofu{}

就分别定义了@Tofu和@Cai两个注解,直接在装配和注入的时候使用这两个注解就好了,而且既可以单独使用,也可以组合使用,不必考虑注解的重复。

4、@Scope注解——bean的作用域

先大致介绍下spring定义的一些作用域:

       单例(Singleton):在整个应用中,只创建bean的一个实例。

       原型(Prototype):每次注入或者通过spring应用上下文获取的时候,都会创建一个新的bean的实例。

       会话(Session):在Web应用中,为每个会话创建一个bean的实例。

       请求(Request):在Web应用中,为每个请求创建一个bean的实例。

在bean的类上使用@Scope注解,例如:

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class TofuCai{...}

这里就将这TofuCai设置成原型作用域。

也可以这么使用@Scope("prototype"),但是没有使用SCOPE_PROTOTYPR更加安全,不易出错。

猜你喜欢

转载自blog.csdn.net/TofuCai/article/details/82863610
今日推荐