条件化 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. 微信小程序post 服务端无法获得参数问题

    header中需要改为 "Content-Type": "application/x-www-form-urlencoded"

  2. compute节点上开启服务openstack-nova-compute.service时,无法启动的解决方法

          本文前一部分为本人解决问题的过程,但最终没有解决:无奈在网上找方法时,看到有网友评论说:修改controller上的guest账号密码,再重启openstack-nova-compute. ...

  3. Django的学习进阶(三)————ORM

    django框架是将数据库信息进行了封装,采取了 类——>数据表 对象——>记录 属性——>字段 通过这种一一对应方式完成了orm的基本映射官方文档:https://docs.dja ...

  4. 了解下Java中的Serializable

      在项目中也写了不少的JavaBean,也知道大多的JavaBean都实现了Serializable接口,也知道它的作用是序列化,序列化就是保存,反序列化就是读取.主要体现在这两方面: 1.存储.将 ...

  5. JavaScript-基本语法和数据类型

           前奏:在HTML中使用JavaScript 1_推荐src引用外部JavaScript文件,方便管理与维护,标签位置在页面最下面,使浏览器更优先加载页面内容. 2_HTML页面需要有标准 ...

  6. Linux下安装配置Jmeter5.1,并执行jmx文件

    Windows下的jmeter是GUI模式,可查看操作,但是GUI对性能的干扰比较大,所有一般压测会在Linux上运行. 下面是Linux下安装配置Jmeter5.1,并执行jmx文件的步骤, 一.安 ...

  7. 【iOS】CocoaPods 使用问题

    这两天使用 CocoaPods 安装时遇到了这个问题,之前从没遇到过需要用户名和密码的情况.刚开始都是强退了重新在终端进入,后来不行…… [!] /usr/local/bin/git clone ht ...

  8. 湫湫系列故事——设计风景线 HDU - 4514

    题目链接:https://vjudge.net/problem/HDU-4514 题意:判断没有没有环,如果没有环,通俗的讲就是找出一条最长的路,相当于一笔画能画多长. 思路:dfs判环. 最后就是没 ...

  9. Lua语言学习

    1,语法 语句不用分号结尾 function ... end if .. else .. end 2, io库, string库, table库, OS库, 算术库, debug库 3, dofile ...

  10. k8s+istio:流量控制之灰度发布

    通过Kubernetes+Istio的流量控制实现灰度发布,主要演示通过流量权重实现蓝绿,通过http自定义头实现金丝雀 准备环境 k8s和istio不想自己装的话可以在云上买个按量付费集群,用完即删 ...