条件化 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. kubernetes的volume的权限设置(属主和属组)

    apiVersion: v1kind: Podmetadata: name: hello-worldspec:  containers:  # specification of the pod's c ...

  2. python课堂整理6---字典

    一.字典  dict info = {"k1" : "v1", "k2" : "v2"} 以键值对形式组成字典 字典里的 ...

  3. windos10专业版激活(可用)

    电脑提示Windows许可证即将到期,于是自己就在网上找了一些教程,但是并没有激活成功,反而由即将到期变为了通知状态,尝试了各种密钥都不行,也下载了激活工具如暴风激活工具,KMS都不管用,尝试了好多方 ...

  4. 面试中的 ThreadLocal 原理和使用场景

    相信大家不管是在网上做题还是在面试中都经常被问过 ThreadLocal 的原理和用法,虽然一直知道这个东西的存在但是一直没有好好的研究一下原理,没有自己的知识体系.今天花点时间好好学习了一下,分享给 ...

  5. 序列化Serializable接口

    一.序列化 1.什么是序列化? 序列化就是将对象的状态存储到特定存储介质中的过程,也就是将对象状态转换为可保持或传输格式的过程. 在序列化过程中,会将对象的公有成员.私有成员(包括类名),转换为字节流 ...

  6. ListActivity

    ListActivity的使用 ListActivity类中集成了一个ListView控件. 通过继承ListActivity类可方便地使用ListView控件 1 public class 类名ex ...

  7. Integrating Thymeleaf with Spring

    这个是基于注解的配置方式,基于配置文件的http://www.cnblogs.com/honger/p/6875148.html 一.整体结构图 二.web.xml文件,这里使用了注解的方式 < ...

  8. 为什么for循环可以遍历list:Python中迭代器与生成器

    1 引言 只要你学了Python语言,就不会不知道for循环,也肯定用for循环来遍历一个列表(list),那为什么for循环可以遍历list,而不能遍历int类型对象呢?怎么让一个自定义的对象可遍历 ...

  9. 【转载】C/C++中long long与__int64的区别

    在C99标准(详情请猛击:C语言的发展及其版本)中,增加了对64位长整型数据的支持,它的类型就是 long long,占用8个字节. 由于C99标准发布较晚,一些较老的C/C++编译器不支持,新编译器 ...

  10. spring注解不支持静态变量注入

    spring注解不支持静态变量注入:今天敲代码  自动配置 配置: Animal.java package study01_autoconfig.beanConfig; import org.spri ...