条件化 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. [leetcode] 80. Remove Duplicates from Sorted Array II (Medium)

    排序数组去重题,保留重复两个次数以内的元素,不申请新的空间. 解法一: 因为已经排好序,所以出现重复的话只能是连续着,所以利用个变量存储出现次数,借此判断. Runtime: 20 ms, faste ...

  2. python函数基础-参数-返回值-注释-01

    什么是函数 函数就是有特定功能的工具 # python中有内置函数(python解释器预先封装好的)与自定义函数(用户自定义封装的)之分 为什么要用函数 # 可以减少代码冗余,增加代码复用性 # 使代 ...

  3. 通过VS2017发布.net core程序并使用Web 部署到远程服务器最新教程

    最近一个项目中,为App开发后台接口,技术选型为最新 .net core版本,使用.net core开发web api接口过程中,为了方便app团队成员直接在线调用接口,找了公网上的一台服务器做为ap ...

  4. 单页面(如react,vue)网站的服务器渲染 SSR 之 SEO 大杀器 Rendertron

    单页面网站,比如vue.recat框架的网站,一般都是直接从服务器推送index.html,再根据自身路由通过js在客户端浏览器渲染出完整的html页面. 但是搜索引擎的爬虫可没有这么智能(实际上go ...

  5. 《JSP数据交互总结》

    1.1.1为什么需要动态网页 静态网页的内容是固定的,不能提供个性化和定制化的服务,因此,动态网页技术逐渐发展起来. 1.1.2什么是动态页面 动态网页是指在服务器端运行的使用程序语言设计的交互式网页 ...

  6. Web Worker 多线程

    Web Workers多线程 1  浏览器把所有事件都通过操作系统安排到事件队列中(例如:你去一个·窗口买菜,需要排队):浏览器使用单线程处理队列中的事件和执行用户代码(也就是单线程:web work ...

  7. 【Android】SDK Manager 设置代理

    这里是 Mac 系统下,Windows 环境类似.打开 Android SDK Manager, Proxy Settings 设置如下所示: PS: 注意勾选 "Force https:/ ...

  8. Keil uVision4 ——如何新建一个项目

    一.打开Keil4软件,点击Project,再点击New μVision Projrct. 二.新建一个文件夹,并在里面输入这个项目的名字. 三.点击Intel,根据实际情况选择,这里选择的是80/8 ...

  9. Spark Streaming消费Kafka Direct保存offset到Redis,实现数据零丢失和exactly once

    一.概述 上次写这篇文章文章的时候,Spark还是1.x,kafka还是0.8x版本,转眼间spark到了2.x,kafka也到了2.x,存储offset的方式也发生了改变,笔者根据上篇文章和网上文章 ...

  10. hdoj 4706 Children's Day

    题目意思就是用a-z组成一个N,然后到z后又跳回a,输出宽从3到10的N. #include <stdio.h> #include <string.h> char s[14][ ...