Bean的高级装配

配置profile bean

针对不同的环境需要配置不同的bean,Spring提供的解决方案是bean profile功能。首先将所有不同的bean定义到一个或多个profile中,然后根据激活的profile来决定创建哪个bean。

@Profile注解指定bean属于哪一个profile,只有该profile激活时,该bean才能被创建。

@Profile可以被用在类级别上,也可以用在方法级别上。

@Configuration
public class DataSourceConfig {

@Profile("dev")
public DataSource embeddedDataSource() {
}

@Profile("prod")
public DataSource jndiDataSource() {
}
}

可以将不同环境的bean定义在一个配置类中,使用@Profile("")指定其profile,表明只有在相应的profile激活时才会创建该bean。

在XML配置文件中一样可以这样配置。

没有声明在任一profile中的bean始终都会创建,与激活哪个profile没关系。

激活profile

Spring在确定激活那个profile时,依赖两个属性:spring.profiles.active和spring.profiles.default。

如果设置了active属性,它的值就是来确定激活哪个profile

如果没有设置active,Spring会查找default的值来确定激活哪个profile

如果active和default都没设置,Spring只会创建没有定义profile的bean

设置两个属性的方式:

  • 作为DispatcherServlet的初始化参数
  • 作为Wen应用的上下文参数
  • 作为JNDI条目
  • 作为环境变量
  • 作为JVM的系统属性
  • 集成测试类上,使用@ActiveProfiles设置

系统优先使用spring.profiles.active的值。

可以同时激活多个profile,用,隔开。

条件化的bean

@Conditional注解用来条件化配置bean,可以用到带@Bean的方法上,如果给定的条件结果为true,就会创建这个bean,否则忽略这个bean。

@Bean
@Conditional(MagicExistsCondition.class)
public MagicBean magicBean() {
    return new MagicBean();
}

MagicExistsCondition这个类指明了条件,需要实现Condition接口。如果matches()返回true,bean会被创建,返回false,bean不会被创建。

public class MagicExistsCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        return environment.containsProperty("magicBean");
    }
}

Condition接口简单但功能强大,matches()方法的参数ConditionContext和AnnotatedTypeMetadata提供了很多的功能。

@Profile在Spring4中进行了重构,使用了@Conditional和Condition接口。

处理自动装配的歧义性

只要一个bean匹配所需的结果时,自动装配才是有效的。如果不仅有一个bean能够匹配结果的话,这种歧义性会阻碍Spring自动装配属性、构造参数或方法参数。可以使用以下方法来解决这个问题。

用首选标识bean

使用@Primary注解将其中一个可选的bean设置为首选,可以和@Component组合使用,可以和@Bean组合使用,告诉Spring在遇到歧义性时要选择的bean。

@Primary
@Component
public class IceCream implements Dessert {
}
@Primary
@Bean
public Dessert iceCream() {
    return new IceCream();
}

限定自动装配的bean

Spring中的限定符能够在可选的bean上缩小范围,直到只有一个bean满足这些范围。@Qualifier注解是限定符的主要方式,可以与@Autowired和@Inject使用。

以下是简单使用@Qualifier的例子,参数是想要注入bean的限定符,默认和bean id一致。

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

为了方便修改类名,可以自定义限定符,这样限定符和类名不耦合。

@Component
@Qualifier("cold")
public class IceCream implements Dessert {
}

@Qualifier也可以和@Bean一起使用。

@Bean
@Qualifier("cold")
public Dessert iceCream() {
    return new IceCream();
}

如果需要在一个bean上添加个限定符(@Qualifier),在Java8前的是不允许在单个条目上使用同一注解多次,这时需要自定义限定符注解,如下,然后再把这些注解添加到bean上,需要注入的方法或字段上。

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

bean的作用域

默认情况下,Spring上下文中的Bean都是单例的。Spring定义了多种作用域。

  • 单例(singleton),整个应用中,只创建bean的一个实例
  • 原型(prototype),每次注入或者通过Spring上下文获取时,都会创建一个新的实例
  • 会话(session),在Web应用中,为每个会话创建一个bean实例
  • 请求(request),在Web应用中,为每个请求创建一个bean实例

使用@Scope来声明

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class IceCream implements Dessert {
}

如下是创建会话或请求作用域bean的声明。proxyMode的作用是解决将会话或请求作用域的bean注入到单例bean中遇到的问题。遇到单例的bean实例,Spring注入的不是特定的ShoppingCart实例,而是一个代理,这个代理会暴露与ShoppingCart相同的接口,随后调用真正ShoppingCart的方法。proxyMode指定的就是为接口创建代理(ScopedProxyMode.INTERFACES)还是为类创建代理(ScopedProxyMode.TARGET_CLASS)。

@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.INTERFACES)
public class ShoppingCart {
}

运行时注入值

Spring提供了两种运行时求值的方法:

属性占位符(property placeholder)

Spring表达式语言(SpEL)

属性占位符

Spring支持将属性定义到外部的属性文件中,然后在程序中引用。

下面的代码首先声明了属性源,并通过Environment来检索属性。

@Configuration
@PropertySource("classpath:/com/soundsystem/app.properties")
public class DemoConfig {

    @Autowired
    Environment environment;

    public BlankDisc blankDisc() {
        return new BlankDisc(environment.getProperty("disc.title"), environment.getProperty("disc.artist"));
    }
}

另一种方式是用占位符将值插入到bean中。占位符的形式是用${}包装属性名,使用@Value注解来插入。

public BlankDisc(@Value("${disc.title}") String title, @Value("${disc.artist}") String artist) {
    this.title = title;
    this.artist = artist;
}

 

posted on 2019-06-02 20:38  nyk1234  阅读(108)  评论(0编辑  收藏  举报

导航