美文网首页
02.高级装配

02.高级装配

作者: apieceof2_d368 | 来源:发表于2020-04-08 11:23 被阅读0次

配置 profile 的原因

不同的环境有不同的配置, spring 需要根据需求的不同决定哪些需要创建哪些bean, 哪些不需要

spring 在

环境与Profile

创建Profile

不同的开发环境需要不同的配置, Spring 需要根据需求的不同决定哪些 Bean 需要创建, 哪些不需要. 所以需要配置文件, 决定配置.

在 Spring 中使用 @Profile 指定某个 bean 属于哪一个 profile. 如果一个 bean 属于 dev profile, 那么只有在激活 dev profile 的时候, 这个 bean 才会创建.

  • 用 @Profile 修饰类
@Configuration
@Profile("dev")
public class DevelopmentProfileConfig{
    @Bean
    public CDPlayer player(){
        return new CDPlayer(segPepper());
    }
    @Bean
    public SegPepper segPepper(){
        return new SegPepper();
    }
}

在上面这个配置文件中的 bean 只有在激活了 dev profile 的时候才会创建

  • 用 @Profile 修饰方法
@Configuration
public class ProductionProfileConfig{
    @Bean
    @Profile("prod")
    public CDPlayer player(){
        return new CDPlayer(segPepper());
    }
}

要注意没有创建 profile 的 bean 始终会被创建.

激活Profile

激活 Profile 用的两个属性

  • spring.profiles.active
  • spring.profiles.default

这两个属性对于 profile 的影响是这样的

  1. 如果设置了 active属性, 那么 profile 由 active 决定
  2. 如果没有设置 active 属性, 那么 profile 由 default 决定
  3. 如果都没有, 那么生效的 bean 只有没有由 profile 管理的 bean

条件化的Bean

Spring4 支持 条件化创建 Bean. 比如我们希望某个 Bean 只有某个类存在的时候才创建, 或者要求只有某个变量存在的时候才创建某个 Bean

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

其中 MagicExistsCondition 实现了接口 Condition

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

这个 Condition 会检查是否存在属性 magic, 如果没有就不会创建 bean

matches 有两个参数 ConditionContext contextAnnotatedTypeMetadata metadate

  • ConditionContext:
    • getRegistry() 返回BeanDefinitionRegistry检查 bean 定义
    • getBeanFactory() 返回ConfigurableListableBeanFactory检查 bean 是否存在, 探查 bean 的属性
    • getResourceLoader() 返回 ResourceLoader所加载的资源
    • getClassLoader() 返回的 ClassLoader加载并检查类是否存在
  • AnnotatedTypeMetadata: 能够检查带有 @Bean 注解的方法还有什么其他的注解, 也可以借助其中的 isAnnotated() 方法, 判断除了 @Bean 还有什么其他的注解

首选的Bean

如果在装配 Bean 的时候有两个或以上符合条件的 Bean, 那么会产生歧义, 报错. 可以在优先使用的bean 加上 @Primary 标签, 这样产生歧义的时候会优先使用这个组件

// 可以用在组件类中
@Component
@Primary
public class Component{}

//也可以用在Bean上
@Bean
@Primary
public Component component(){
    return new Component();
}

限定符修饰的Bean

如前面所讲, 如果一个自动装配会试图尽量符合目标的注入. 如果没有符合的组件, 或者有多个符合的组件产生歧义就会报错.

可以使用 @Qualifier 注解来给组件设定一个标识符

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

这里, 会把一个标识符为 code 的组件注入到参数 dessert 中. 但是组件的默认标识符是它的 id, 可以用 @Qualifier 标签修改组件的标识符

@Component
// 也可以用在 @Bean 下面
@Qualifier("code")
public class IceCream implements Dessert{...}

这里推荐标识符设置为组件的某个特性, 比如"冰激凌"是"code"的, 强调"一一对应"的注入关系不是什么好事情. 那么就又产出了一个新问题. 如果有多个组件有"code"特性呢? 这样不是又产出了歧义?

也许可以给一个组件加上多个标识符

@Component
@Qualifier("code")
@Qualifier("creamy")
public class IceCream implements Dessert{...}

但是 Java 不允许把一个注解多次使用在一个目标上. 解决方法是:

@Targer({ElementType.CONSTRUCTOR, ElementType.Field,
        ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Creamy{}

这样我们就有了一个 @Creamy 注解, 同样声明一个 @Code 注解, 这样就可以同时使用两个标识符了

@Component
@Code
@Creamy
public class IceCream implements Dessert{...}

@Autowired
@Code
@Creamy
public void setDessert(Dessert dessert){
    this.dessert = dessert;
}

Bean 的作用域

Spring 定义了多种定义域

  • 单例
  • 原型
  • 会话
  • 请求

默认, 创建的组件都是单例, 如果要使用原型:

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

//也可以使用@Scope("prototype"), 但是好像不如上面安全

使用会话

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

第一个参数, 告诉 Spring 对于每一个 Web 会话, 都会创建一个 cart 会话. 第二个参数解决了单例和会话的配置的问题.

假设现在有一个 StoreService 单例, 它负责结账购物车中的内容.

@Component
public class StoreService{
    @Autowired
    public void setShoppingCart(ShoppingCart cart){
        this.ShoppingCart = cart;
    }
}

但是单例在上下文加载的时候创建, 这个时候会话作用域的 cart 并不存在. 于是 cart 需要一个代理, 单例在注入 cart 的时候获得的是一个代理.

运行时注入

对于一般的组件, 我们可能会以这种方式装配

@Bean
public CompactDisc setPeppers(){
    return new BlankDisc(
        "set Pepper",
        "The Beatles"
    );
}

这里的问题就在于, 实例化 BlankDisc 过程的数据是硬编码在配置文件里的. 这也许不是很合适.

这个时候就要通过注入外部的值尽量解决硬编码

@Configuration
@PropertySource("classpath:/com/soundsystem/app.properties")
public class ExpressiveConfig {
    
    @Autowired
    Environment env;
    
    @Bean
    public BlankDisc disc() {
        return new BlankDisc(env.getProperty("disc.title"),
                             env.getProperty("disc.artist"));
    }

}

在这里通过 @PropertySource() 标注导入了一个 app.properties 文件, 这个时候, 坏境会提供一个 Environment 组件. 将这个组件装配到 env 以后, 通过 getProperty 可以调用 app.properties 中的值

disc.title="Set. Peppers"
disc.artist="The Beaties"

关于 getProperty 的一些重载

  • String getProperty(String key)

  • String getProperty(String key, String defaultValue): 如果没有 key , 那么设置为默认值

  • T getProperty(String key, Class<T> type) 如果希望得到的是数字:

    env.getProperty("db.connection.count", Integer.class);
    
  • T getProperty(String key, Class<T> type, 12) 相似, 不过有默认值

属性占位符

除去上面的方法, 还有一种使用属性占位符的方式配置方法.

首先, 开启 PropertySourcePlaceholderConfigurer

    @Bean
    public
    static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }

然后, 组装 bean

    @Bean
    public BlankDisc disc(
                @Value("${disc.title}") String title,
                @Value("${disc.artist}")String artist
            ) {
        return new BlankDisc(title, artist);
    }

这里用到了@Value注解, 接受一个占位符, 格式为 ${...}, 大括号中占位符

相关文章

  • 02.高级装配

    配置 profile 的原因 不同的环境有不同的配置, spring 需要根据需求的不同决定哪些需要创建哪些bea...

  • Spring学习(三)高级装配

    Spring中有许多高级装配方式,下面是思维导图Spring高级装配思维导图

  • 高级装配

    3.1 环境和配置文件 实际情况中,不同的环境中所应使用的配置是不相同的。 3.1.1 配置配置文件Bean 是在...

  • 高级装配

    本节的主要内容是: Spring profile 条件化的bean声明 自动装配与歧义性 bean的作用域 运行时...

  • Spring之旅(四):Spring 高级装配(一)

    Bean的高级装配 我们已经学习了Bean的自动装配和java显式装配,但java的装配绝不仅限于此。 我们可能会...

  • 西门子高性能汽车制造与装配技术

    高级装配技术的集成合作伙伴 西门子定制的汽车制造与装配技术解决方案是您解决车身车间和最终装配过程中产品,产品装配和...

  • spring高级装配

    1.profile 在生产中我们通常会使用maven的-P来指定环境选项。在Spring中也有类似的功能实现环境隔...

  • 【Spring实战】高级装配

    本章内容: Spring profile 条件化的bean声明 自动装配与歧义性 bean的作用域 Spring表...

  • Spring实战——高级装配

    @profile("场景标识名") 不同的环境中(开发环境、生产环境、QA环境)用到的bean可能不同,那么在运行...

  • Spring高级装配Bean

    开发、测试和生产环境的配置和切换 开发 -> 测试 -> 部署上线,每个阶段的环境的配置参数会有不同,如数据源,文...

网友评论

      本文标题:02.高级装配

      本文链接:https://www.haomeiwen.com/subject/vlgzphtx.html