目录

  • spring profile

  • 条件化的bean声明

  • 自动装配与歧义性

  • bean的作用域

  • Spring表达式语言

一、环境与profile

配置profile  bean

在软件开发的时候,有一个很大的挑战就是将应用程序从一个环境迁移到另外一个环境。数据库配置、加密算法以及与外部系统的集成是夸环境部署时会发生变化的几个典型例子。

以下是两个不同环境的DataSource   bean。

package springdemo.test1.entity.datasource;

import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; @Configuration
@Profile("dev")
public class DevelopmentProfileConfig { @Bean(destroyMethod="shutdown")
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:schema.sql")
.addScript("classpath:test-data.sql")
.build();
}
}
package springdemo.test1.entity.datasource;

import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jndi.JndiObjectFactoryBean; @Configuration
@Profile("prod")
public class ProductionProfileConfig { @Bean
public DataSource dataSource() {
JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
jndiObjectFactoryBean.setJndiName("jdbc/myDs");
jndiObjectFactoryBean.setResourceRef(true);
jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
return (DataSource)jndiObjectFactoryBean.getObject();
}
}

在该两个配置类中,使用了注解@Profile,它会告诉spring这个配置类中的bean只有在dev profile/prod profile激活时才会创建,如果对应profile没有激活,那么带有@Bean注解的方法都会被忽略掉。

在spring3.2开始,@Profile注解可以使用到方法级别上了,可以与@Bean注解一同使用,这样可以将两个bean的声明放在一个配置类中。

package springdemo.test1.entity.datasource;

import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.jndi.JndiObjectFactoryBean; @Configuration
public class DataSourceConfig { @Bean(destroyMethod="shutdown")
@Profile("dev")
public DataSource devDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:schema.sql")
.addScript("classpath:test-data.sql")
.build();
} @Bean
@Profile("prod")
public DataSource proDataSource() {
JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
jndiObjectFactoryBean.setJndiName("jdbc/myDs");
jndiObjectFactoryBean.setResourceRef(true);
jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
return (DataSource)jndiObjectFactoryBean.getObject();
}
}

以上是使用javaConfig配置

也可使用xml配置Profile

使用<beans>元素的profile属性,在xml中配置profile bean

可以创建用于不同环境的xml,但只有profile属性与当前激活profile相匹配的配置文件才会被用到。

另外可以在跟<beans>元素中嵌套定义<beans>元素,而不是为每个环境都创建一个profile xml文件

激活profile

spring在确定哪个profile处于激活状态时,需要依赖两个独立的属性:spring.profiles.active 和 spring.rofiles.default。如果设置了active属性的话,那么它的值就用来确定哪个profile是激活的。如果没有设置active的值,spring会查找default的值。如果两个值都没有设置的话,那就是没有激活的profile,因此只会创建哪些没有定义在profile中的bean。

有多种方式来设置这两个属性:

  • 作为DispatcherServlet的初始化参数
  • 作为web应用的上下文参数
  • 作为JNDI条目
  • 作为环境变量
  • 作为jvm的系统属性
  • 在集成测试类上,使用ActiveProfiles注解设置

二、条件化的bean

spring4引入了一个新的@Conditional注解,它可以用到带有@Bean注解的方法上。如果给定的条件计算结果为true,就会创建这个bean,否则,这个bean会被忽略。


@Bean
@Conditional(EngineCondition.class)
public IEngine setIEngine()
{
return new AudiEngine();
}

@Conditional注解中给定了一个class,它指明了条件。@Conditional将会通过Condition接口进行条件对比:

public abstract interface Condition
{
public abstract boolean matches(ConditionContext paramConditionContext,
AnnotatedTypeMetadata paramAnnotatedTypeMetadata);
}

设置给@Conditional的类可以是任意实现了Condition接口的类型。如果matches()方法返回true,那么就会创建带有@Conditional注解的bean,如果返回为false,将不会创建这些bean。

matches()方法会得到ConditionContext和AnnotatedTypeMetadata对象用来做出决策。

ConditionContext是一个接口,大致如下:

public interface ConditionContext {

    /**
* Return the {@link BeanDefinitionRegistry} that will hold the bean definition
* should the condition match or {@code null} if the registry is not available.
* @return the registry or {@code null}
*/
BeanDefinitionRegistry getRegistry(); /**
* Return the {@link ConfigurableListableBeanFactory} that will hold the bean
* definition should the condition match or {@code null} if the bean factory
* is not available.
* @return the bean factory or {@code null}
*/
ConfigurableListableBeanFactory getBeanFactory(); /**
* Return the {@link Environment} for which the current application is running
* or {@code null} if no environment is available.
* @return the environment or {@code null}
*/
Environment getEnvironment(); /**
* Return the {@link ResourceLoader} currently being used or {@code null}
* if the resource loader cannot be obtained.
* @return a resource loader or {@code null}
*/
ResourceLoader getResourceLoader(); /**
* Return the {@link ClassLoader} that should be used to load additional
* classes or {@code null} if the default classloader should be used.
* @return the class loader or {@code null}
*/
ClassLoader getClassLoader(); }

AnnotatedTypeMetadata能够让我们检查带有@Bean注解的方法上还有什么其他注解,AnnotatedTypeMetadata也是一个接口:

public interface AnnotatedTypeMetadata {

    /**
* Determine whether the underlying element has an annotation or meta-annotation
* of the given type defined.
* <p>If this method returns {@code true}, then
* {@link #getAnnotationAttributes} will return a non-null Map.
* @param annotationType the annotation type to look for
* @return whether a matching annotation is defined
*/
boolean isAnnotated(String annotationType); /**
* Retrieve the attributes of the annotation of the given type, if any (i.e. if
* defined on the underlying element, as direct annotation or meta-annotation),
* also taking attribute overrides on composed annotations into account.
* @param annotationType the annotation type to look for
* @return a Map of attributes, with the attribute name as key (e.g. "value")
* and the defined attribute value as Map value. This return value will be
* {@code null} if no matching annotation is defined.
*/
Map<String, Object> getAnnotationAttributes(String annotationType); /**
* Retrieve the attributes of the annotation of the given type, if any (i.e. if
* defined on the underlying element, as direct annotation or meta-annotation),
* also taking attribute overrides on composed annotations into account.
* @param annotationType the annotation type to look for
* @param classValuesAsString whether to convert class references to String
* class names for exposure as values in the returned Map, instead of Class
* references which might potentially have to be loaded first
* @return a Map of attributes, with the attribute name as key (e.g. "value")
* and the defined attribute value as Map value. This return value will be
* {@code null} if no matching annotation is defined.
*/
Map<String, Object> getAnnotationAttributes(String annotationType, boolean classValuesAsString); /**
* Retrieve all attributes of all annotations of the given type, if any (i.e. if
* defined on the underlying element, as direct annotation or meta-annotation).
* Note that this variant does <i>not</i> take attribute overrides into account.
* @param annotationType the annotation type to look for
* @return a MultiMap of attributes, with the attribute name as key (e.g. "value")
* and a list of the defined attribute values as Map value. This return value will
* be {@code null} if no matching annotation is defined.
* @see #getAllAnnotationAttributes(String, boolean)
*/
MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationType); /**
* Retrieve all attributes of all annotations of the given type, if any (i.e. if
* defined on the underlying element, as direct annotation or meta-annotation).
* Note that this variant does <i>not</i> take attribute overrides into account.
* @param annotationType the annotation type to look for
* @param classValuesAsString whether to convert class references to String
* @return a MultiMap of attributes, with the attribute name as key (e.g. "value")
* and a list of the defined attribute values as Map value. This return value will
* be {@code null} if no matching annotation is defined.
* @see #getAllAnnotationAttributes(String)
*/
MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationType, boolean classValuesAsString); }

三、处理自动装配的歧义性

加入我们使用@Autowired 注解标注了setDessert()方法:

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

在本例中,Dessert是一个接口,并且有三个实现类,分别是Cake,Cookies和IceCream

@Component
public class Cake implements Dessert{ @Override
public void showDessert() {
// TODO Auto-generated method stub
}
}
@Component
public class Cookies implements Dessert{ @Override
public void showDessert() {
// TODO Auto-generated method stub }
}
@Component
public class IceCream implements Dessert{ @Override
public void showDessert() {
// TODO Auto-generated method stub }
}

因为这三个类使用了@Component注解,在组件扫描的时候,能够发现它们并将其创建为Spring应用上下文里的bean。然后spring在试图自动装配setDessert()中的Dessert参数时,它并没有唯一、无歧义的可选bean。spring会抛出NoUniqueBeanDefinitionException异常。

解决自动装配bean的歧义性问题有两种方法:

  • 将可选bean中的某一个设置为首选的bean
  • 使用限定符(qualifier)来帮助spring将可选bean的范围缩小到只有一个bean

标识首选的bean

使用@Primary注解来标识首选。@Primary注解可以与@Component组合用在组件扫描的bean上,也可以与@Bean注解组合用在java配置的bean声明中。

@Component
@Primary
public class IceCream implements Dessert{ @Override
public void showDessert() {
// TODO Auto-generated method stub }
}

xml中配置首选bean,可以使用<bean>元素的primary属性来指定。

使用限定符限定自动装配的bean

设置首选bean的局限在于@Primary注解无法将可选的方案的范围限定到唯一一个无歧义性的选项中。它只能标识一个优选的方案,当首选bean的数量超过一个时,没法进一步缩小可选范围。

@Qualifier是使用限定符的主要方式。可以与@Autowired协同使用。在注入的时候指定想要注入进去的是哪个bean。例如:

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

@Qualifier注解所设置的参数就是想要注入的bean的ID。这里的问题在于setDessert()方法上所指定的限定符与要注入的bean的名称是紧耦合的。

我们可以为bean设置自己的限定符,而不是依赖于bean的ID。如下:

@Component
@Qualifier("cold")
public class Cake implements Dessert{ @Override
public void showDessert() {
// TODO Auto-generated method stub
}
}

如此,则code限定符分配给了Cake bean。在注入的地方,只要引用cold限定符就可以了:

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

四、bean的作用域

spring定义了多种作用域,可以基于这些作用域创建bean,包括:

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

单例是默认的作用域,如果选择其他作用域,要使用@Scope注解,它可以域@Component或@Bean一起使用。

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class IceCream implements Dessert{ @Override
public void showDessert() {
// TODO Auto-generated method stub }
}

同样,如果使用xml来配置的话,可以使用<bean>元素的scope属性来设置作用域。

会话和请求作用域需要注意作用域代理的问题。

五、运行时注入---spring表达式语言

spring一直支持将属性定义到外部的属性文件中,并使用占位符值将其插入到Spring bean中。在Spring装配中,占位符的形式为使用“${...}”包装的属性名称。

    <bean id="iEngine" class="springdemo.test1.entity.impl.AudiEngine"
c:_title="${engine.title}" c:_outValue="${engine.outValue}"/>

为了使用属性占位符,我们必须要配置一个PropertyPlaceholderConfigurer  bean 或者PropertySourcesPlaceholderConfigurer  bean 。因为他能够基于spring  Environment 及属性源来解析占位符。

<context:property-placeholder location=""/>

spring表达式-----SpEL

首先SpEL表达式要放到“#{...}”之中。

示例:

#{T(System).currentTimeMillis()} ---计算表达式的那一刻当前的时间的毫秒数。

#{userinfo.userName}---应用其他bean或其他bean的属性。

SpEL拥有很多特性,包括:

  • 使用bean的id来引用bean;
  • 调用方法和访问对象的属性;
  • 对值进行算术、关系和逻辑运算;
  • 正则表达式匹配
  • 集合操作。

此篇中不在详述SpEL,具体使用细节可查询spring相关文档或书籍。

Spring高级装配bean的更多相关文章

  1. (三)Spring 高级装配 bean的作用域@Scope

    1.默认情况下,spring通过@Autowared注入的bean是单例的bean,但有些情况是不满足的,例如:购物车,每个会话,或每个用户登录使用的购物车都是独立的 spring的定义的作用域: a ...

  2. Spring高级装配

    Spring高级装配 目录 一.Profile(根据开发环境创建对应的bean) 二.条件化的创建bean(根据条件创建bean) 三.处理自动装配歧义性(指定首选bean.限定符限制bean) 四. ...

  3. Spring高级装配(一) profile

    Spring高级装配要学习的内容包括: Spring profile 条件化的bean声明 自动装配与歧义性 bean的作用域 Spring表达式语言 以上属于高级一点的bean装配技术,如果你没有啥 ...

  4. Spring 自动装配 Bean

    Spring3系列8- Spring 自动装配 Bean 1.      Auto-Wiring ‘no’ 2.      Auto-Wiring ‘byName’ 3.      Auto-Wiri ...

  5. Spring自动装配Bean详解

    1.      Auto-Wiring ‘no’ 2.      Auto-Wiring ‘byName’ 3.      Auto-Wiring ‘byType 4.      Auto-Wirin ...

  6. Spring实战(四)Spring高级装配中的bean profile

    profile的原意为轮廓.剖面等,软件开发中可以译为“配置”. 在3.1版本中,Spring引入了bean profile的功能.要使用profile,首先要将所有不同的bean定义整理到一个或多个 ...

  7. [spring]03_装配Bean

    3.1 JavaBean 3.1.1 JavaBean 是什么 JavaBean 是一种JAVA语言写成的可重用组件. 为写成JavaBean,类必须是具体的和公共的,并且具有无参数的构造器. Jav ...

  8. spring IOC装配Bean(注解方式)

    1 Spring的注解装配Bean (1) Spring2.5 引入使用注解去定义Bean @Component 描述Spring框架中Bean (2) Spring的框架中提供了与@Componen ...

  9. Spring自动化装配bean

    1. 场景 用CD(Compact disc)和CD播放器(CDPlayer)阐述DI(依赖注入). 如果不将CD插入(注入)到CDPlayer中,那么CDPlayer其实没有太大的用处,所以,可以这 ...

随机推荐

  1. 《ServerSuperIO Designer IDE使用教程》-4.增加台达PLC驱动及使用教程,从0到1的改变。发布:v4.2.3版本

    v4.2.3 更新内容:1.优化数据存储部分,提高效率.2.修复数据库服务停止造成程序异常退出的现象.3.修复本机没有串口造成无法增加设备驱动的情况.4.增加编辑设备和监测点配置信息功能.5.增加台达 ...

  2. 数组实现int随机数的阶乘(避免大数问题)

    面试的一道题目,实现int随机数的阶乘.这道题就是考察你考没考虑大数问题,如何避免它. 我能想到的就是用数组去实现,然后写了一下代码.但是当i的值很大,接近Max_value时的情况还没有考虑到. 直 ...

  3. 2019工作计划idea

    2019.2.24 工作需求: 汇总 2008-2018年 销售订单数据; 分类历史订单数据, 并可能采取方法进行预测(预测只是一种行为不代表结果) 目前已知条件: 订单生产周期; 45天(标准天数) ...

  4. 把ssl模块加入到已经编译好的apache中实现HTTPS

    为了使Apache支持https访问,系统需要安有apache.openssl.mod_ssl.so 1.安装openssl: 基本上系统都已经安装了,在/usr/bin/openssl下,直接使用o ...

  5. data parameter is nil 异常处理

    似乎是NSData的问题,用排除法分析了一下 NSString *urlStr = [NSString stringWithFormat:@"%@/config/AppConfig.json ...

  6. ubuntu/deepin 下 Sha 哈 dow 哈 socks 全局配置

    1. 安装编译环境 Ubuntu在默认情况下没有提供C/C++的编译环境,因此需要手动安装.如果单独安装gcc以及g++比较麻烦,为了方便,我们直接安装Ubuntu提供的build-essential ...

  7. NOIP-螺旋矩阵

    题目描述 一个 n 行 n 列的螺旋矩阵可由如下方法生成: 从矩阵的左上角(第 1 行第 1 列)出发,初始时向右移动:如果前方是未曾经过的格子,则继续前进,否则右转:重复上述操作直至经过矩阵中所有格 ...

  8. ajax常见的面试题

    1. 什么是ajax? AJAX = Asynchronous (异步) JavaScript and XML,是一种用于创建快速动态网页的技术. 2. ajax接受到的数据类型是什么? 2.1 st ...

  9. 纯javascript实现可拖住/大小的div

    好久没写了,不得不说人懒了好多.. 也不打算实现什么太厉害的功能,因为不喜欢网上那些一大堆代码的,看的头晕,于是自己写了一个 旨在越简单越好(当然也走点形式- -其实是自己菜),所以一些宽度和高度都写 ...

  10. CentOS 7.3/Linux .net core sdk 安装

    执行下列命令,安装.NET Core SDK(微软官方教程地址 https://www.microsoft.com/net/learn/get-started/linuxcentos) 点开链接,选择 ...