条件化 bean

有时候我们要满足某种情况才将bean 初始化放入容器中。

基于环境初始化不同的 bean

1.申明接口并创建两个实现类

public interface Teacher {

    void startWorking();
} public class JavaTeacher implements Teacher { public void startWorking() {
System.out.println("开始 Java 教学");
}
} public class SqlTeacher implements Teacher { public void startWorking() {
System.out.println("开始 SQL 教学");
}
}

2.JavaConfig 显式装配两个实现类

@Configuration
public class ApplicationConfig { @Bean(name = "teacher")
@Profile("Monday")
public Teacher sqlTeacher() {
return new SqlTeacher();
} @Bean(name = "teacher")
@Profile("Tuesday")
public Teacher javaTeacher() {
return new JavaTeacher();
} }

注:可以看到两个 bean 都取名为 teacher,但是 @Profile 值不同。

3.单元测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ApplicationConfig.class)
@ActiveProfiles("Monday")
//@ActiveProfiles("Tuesday")
public class ConditionTest {
@Autowired(required = false)
private Teacher teacher; @Test
public void test01(){
if(teacher != null){
teacher.startWorking();
}
}
}

执行单元测试,查看控制台输出:

开始 SQL 教学

通过 @ActiveProfiles 配置当前的环境,我们就能做到 周一 SQL教学 周二 Java 教学 了。

激活 Profile 方式

Spring 激活 Profile 依赖两个属性:

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

active 优先级比 default 高,如果两个属性都没有设置的话,将只会创建没有定义 profilebean

除了 @ActiveProfiles 配置外,我们还能通过很多种方式来设置属性:

  • 作为 DispatcherServlet 的初始化参数;
  • 作为 Web 应用的上下文参数;
  • 作为 JNDI 条目;
  • 作为环境变量;
  • 作为 JVM 的系统属性;

自定义提交

除了环境之外,我们也可以自定义条件来决定初始化那个 Bean。

例如刚刚的实例,老师开始 Java教学 ,除了时间是周二之外还应该有学生在,才能完成教学。

@Configuration
public class ApplicationConfig { @Bean(name = "teacher")
@Conditional(MyCondition.class)
public Teacher javaTeacher() {
return new JavaTeacher();
}
}

通过 @Conditional 注解来自定义条件,MyCondition 是我们实现 Condition 接口完成的自定义条件判断

public class MyCondition implements Condition {

    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment environment = conditionContext.getEnvironment();
// 1.获取属性值
List activeProfiles = Arrays.asList(environment.getActiveProfiles());
List defaultProfiles = Arrays.asList(environment.getDefaultProfiles());
// 2.判断属性是否存在
boolean activeFlag = activeProfiles.contains("Tuesday") && activeProfiles.contains("students");
boolean defaultFlag = defaultProfiles.contains("Tuesday") && defaultProfiles.contains("students");
return activeFlag || defaultFlag;
}
}

单元测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ApplicationConfig.class)
@ActiveProfiles({"Tuesday","students"})
public class ConditionTest {
@Autowired(required = false)
private Teacher teacher; @Test
public void test01(){
if(teacher!=null){
teacher.startWorking();
}
}
}

控制台输出:

开始 Java 教学

ConditionContext 方法

public interface ConditionContext {

    BeanDefinitionRegistry getRegistry();

    ConfigurableListableBeanFactory getBeanFactory();

    Environment getEnvironment();

    ResourceLoader getResourceLoader();

    ClassLoader getClassLoader();
}
  • getRegistry():返回 BeanDefinitionRegistry 检查 bean 定义;
  • getBeanFactory():返回 ConfigurableListableBeanFactory 检查 bean 是否存在和它的属性;
  • getEnvironment():返回 Environment 检查环境变量是否存在以及它的值;
  • getResourceLoader():返回 ResourceLoader 所加载的资源;
  • getClassLoader():返回 ClassLoader 加载并检查类是否存在

处理自动装配歧义性

在之前我们讲到自动装配如果匹配到多个 bean 的话,Spring 会抛出一个 NoUniqueBeanDefinitionException 异常,表示没有明确指明使用哪个 bean 来进行自动装配。

标识首选 bean

通过 @Primart 注解可以将一个 bean 设置为首选,但是要注意的是,如果出现多个 @Primart 也还是会抛出异常。

限定自动装配的 bean

@Primart 注解不能将可选范围缩小到唯一一个无歧义的选项中。我们可以通过限定符的形式进行范围的缩小,直到唯一为止。

@Qualifier 注解是使用限定符的主要方式。它可以与 @Autowired 注解协同使用,在注入 bean 的时候指定注入的是那个 bean

public interface Teacher {

    void startWorking();
} @Component
public class SqlTeacher implements Teacher { public void startWorking() {
System.out.println("开始 SQL 教学");
}
} @Component
public class JavaTeacher implements Teacher { public void startWorking() {
System.out.println("开始 Java 教学");
}
}
@Autowired
@Qualifier("javaTeacher")
private Teacher teacher; @Test
public void test01() {
teacher.startWorking();
}

@Qualifier 注解的参数就是想要注入的 beanid。如果 @Component 没有显示指定 bean 的 Id ,那么默认的 Id 是类型首字母小写。这跟类名就有耦合性了,我们可以在类上加 @Qualifier(name) 的形式设置别名避免和类名耦合。

自定义限定符

要注意的是 @Qualifier 也可能出现重复,我们可以通过增加多个条件来进一步缩小范围,但是 Java 不支持在同一个条目上出现相同类型的多个注解,我们可以通过自定义注解的形式来解决这个问题。

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

我们定义了一个 Monday 注解,我们来使用一下它:

@Component
@Monday
public class JavaTeacher implements Teacher { public void startWorking() {
System.out.println("开始 Java 教学");
}
}

使用:

@Autowired
@Monday
private Teacher teacher;

bean 作用域

默认情况下 Spring 应用上下文中的所有 bean 都是以单例形式注入,但是在某些情况下可能就不太适用。比如购物车组件这种有状态值的,针对每一个用户应该是不同的实例。

Spring 定义了多种作用域:

  • 单例(Singleton): 在整个应用中,只创建 bean 的一个实例;
  • 原型(Prototype):在每次注入或者通过 Spring 应用上下文获取的时候,都会创建一个实例;
  • 会话(Session):在 Web 应用中,为每个会话创建一个 bean 的实例;
  • 请求(Rquest):在 Web 应用中,为每一个请求创建一个 bean 的实例。

如何指定作用域

通过使用 @Scope 注解或 scope标签 可以配置作用域。

运行时注入属性

Spring 提供了两种在运行时赋值的方式:

  • 属性占位符(Property plaveholder)
  • Spring 表达式语言(SpEL)

它们都可以在应用运行时在获取值,避免硬编码的存在。

Spring学习之旅(四)--高级装配Bean的更多相关文章

  1. Spring第一课:基于XML装配bean(四),三种实例化方式:默认构造、静态工厂、实例工厂

    Spring中基于XML中的装配bean有三种方式: 1.默认构造 2.静态工厂 3.实例工厂 1.默认构造 在我们在Spring的xml文件中直接通过:     <bean id=" ...

  2. Spring学习之旅(八)Spring 基于AspectJ注解配置的AOP编程工作原理初探

    由小编的上篇博文可以一窥基于AspectJ注解配置的AOP编程实现. 本文一下未贴出的相关代码示例请关注小编的上篇博文<Spring学习之旅(七)基于XML配置与基于AspectJ注解配置的AO ...

  3. Spring入门(5)-自动装配Bean属性

    Spring入门(5)-自动装配Bean属性 本文介绍如何装配Bean属性. 0. 目录 ByName ByType constructor 默认自动装配 混合使用自动装配和显示装配 1. ByNam ...

  4. Spring学习之旅(十)--MockMvc

    在之前的 Spring学习之旅(八)--SpringMVC请求参数 我们是通过在控制台输出来验证参数是否正确,但是这样做实在是太耗时间了,我们今天来学习下 MockMvc,它可以让我们不需要启动项目就 ...

  5. Spring学习之旅(三)--装配Bean

    装配 Bean 的方式 在 XML 中进行显式配置 在 Java 中进行显式配置 隐式的 Bean 发现机制和自动装配 Spring 提供了以上三种方式进行 Bean 的配置,可以根据自己的需求选择一 ...

  6. Spring高级装配bean

    目录 spring profile 条件化的bean声明 自动装配与歧义性 bean的作用域 Spring表达式语言 一.环境与profile 配置profile  bean 在软件开发的时候,有一个 ...

  7. Spring学习之旅(四)Spring工作原理再探

    上篇博文对Spring的工作原理做了个大概的介绍,想看的同学请出门左转.今天详细说几点. (一)Spring IoC容器及其实例化与使用 Spring IoC容器负责Bean的实例化.配置和组装工作有 ...

  8. spring使用之旅(一) ---- bean的装配

           基础配置 启用组件扫描配置                     Java类配置文件方式 package com.springapp.mvc.application;   import ...

  9. spring学习笔记(四)我对spring中bean生命周期的理解

    我相信大部分同学对spring中bean的生命周期都不陌生,但是如果要详细的说出每一个步骤,可能能说出来的也不多,我之前也是这样,前几天调了一下spring的源码,看了一点书,突然一下明朗了,理解了s ...

随机推荐

  1. c语言进阶14-线性表之链表

    一.  线性表的链式存储结构 1.        顺序存储结构不足的解决办法 前面我们讲的线性表的顺序存储结构.它是有缺点的,最大的缺点就是插入和删除时需要移动大量元素,这显然就需要耗费时间.能不能想 ...

  2. 【转】DataTable 中数据筛选

    转自:http://blog.163.com/yangxw_2009/blog/static/155255217201032931755646/ 对DataTable进行过滤筛选的一些方法Select ...

  3. es6的基本用法

    1. let和const <!DOCTYPE html> <html lang="en"> <head> <meta charset=&q ...

  4. <<Modern CMake>> 翻译 2. CMake 基础

    <<Modern CMake>> 翻译 2. CMake 基础 最低版本 这是每个 CMakeLists.txt 文件的第一行.CMakeLists.txt 是 CMake 所 ...

  5. 关于sprintf的使用注意

    今天在使用sprintf时,本想简单一点,将第一个参数直接定义为一个字符型的指针(cher  *str;),结果没想到程序变得死死的,老老实实的将第一个参数重新变回字符型数组吧(char str[10 ...

  6. windows上node开发注意事项

    windows上进行node.react开发的必要步骤: 1.使用nvm进行node及npm包管理工具,记得使用npm config set ...:2.另外react仅支持python3.0以下的版 ...

  7. Java初中级程序员面试题宝典

    Java基础部分 &与&&区别? &和&&都是逻辑运算符,都是判断两边同时真则为真,否则为假:但是&&当第一个条件不成之后,后面的条件都 ...

  8. C语言编程入门之--第三章编写第一个C语言程序

    第三章 编写第一个C语言程序 导读:一般学一门计算机语言的第一堂上机课(“上机”顾名思义,上了计算机),就是往屏幕输出“hello world”,本章也不例外. 1.1 Hello,World! 这一 ...

  9. python 读取文件1

    1.脚本 from sys import argv script,filename = argv txt = open(filename) print ("the filename is % ...

  10. the license has been canceled

    ideal 的 注册码并没有失效,却显示这个信息 the license has been canceled 如果用的是Windows系统,在hosts文件添加下边的ip及映射 0.0.0.0 acc ...