第3章 高级装配

3.1 环境与profile

在 3.1 版本中, Spring 引入了 bean profile 的功能。要使用 profile ,你首先要将所有不同的 bean 定义整理到一个或多个 profile 之中,在将应用部署到每个环境时,要确保对应的 profile 处于激活( active )的状态。

在 Java 配置中,可以使用 @Profile 注解指定某个 bean 属于哪一个 profile 。

@Service
@Profile("prod")
public class TestImpl implements ITest {
    public void show() {
        System.out.println("正式环境!");
    }
}

注意:Profile注解可以用到配置类上,配置类方法上,以及Bean类上等地方。在 Spring 3.1 中,只能在类级别上使用 @Profile 注解。不过,从 Spring 3.2 开始,你也可以在方法级别上使用 @Profile 注解,与 @Bean 注解一同使用。

在xml配置中:

<beans ... profile="prod"></beans>

注意:beans标签可以嵌套的。

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

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

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

作为servlet参数

//方式一
package com.mkyong.servlet3;
?
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
?
public class MyWebInitializer extends
    AbstractAnnotationConfigDispatcherServletInitializer {
?
    //...
?
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        super.onStartup(servletContext);
        servletContext.setInitParameter("spring.profiles.active", "live");
?
        //Set multiple active profile
        //servletContext.setInitParameter("spring.profiles.active", "dev, testdb");
    }
?
}
//方式二
package com.mkyong.servlet3;
?
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
?
public class MyWebInitializer extends
    AbstractAnnotationConfigDispatcherServletInitializer {
?
    //If the @Profile beans are loaded via root context
    @Override
    protected WebApplicationContext createRootApplicationContext() {
?
        WebApplicationContext context =
                     (WebApplicationContext)super.createRootApplicationContext();
            ((ConfigurableEnvironment)context.getEnvironment()).setActiveProfiles("live");
?
        //Set multiple active profiles
        //((ConfigurableEnvironment)context.getEnvironment())
                //          .setActiveProfiles(new String[]{"live", "testdb"});
?
            return context;
?
    }
}

jvm参数

3.2 条件化的bean

? 假设你希望一个或多个 bean 只有在应用的类路径下包含特定的库时才创建。或者我们希望某个 bean 只有当另外某个特定的 bean 也声明了之后才会创建。我们还可能要求只有某个特定的环境变量设置之后,才会创建某个 bean 。 在 Spring 4 之前,很难实现这种级别的条件化配置,但是 Spring 4 引入了一个新的 @Conditional 注解,它可以用到带有 @Bean 注解的方法上。如果给定的条件计算结果为 true ,就会创建这个 bean ,否则的话,这个 bean 会被忽略

@Bean
@Conditional(MagicExitisCondition.class)
public MagicBean magicBean(){
    return new MagicBean();
}

可以看到, @Conditional 中给定了一个 Class ,它指明了条件 —— 在本例中,也就是 MagicExistsCondition 。 @Conditional 将会通过 Condition 接口进行条件对比:

public interface Condition{
    boolean matches(ConditionContext ctxt,AnnotatedTypeMetadata metadata);
}

如果 matches() 方法返回 true ,那么就会创建带有 @Conditional 注解的 bean 。如果 matches() 方法返回 false ,将不会创建这些 bean 。

public class MagicExitisCondition implements Condition{
    public boolean matches(ConditionContext context,AnnotatedTypeMetadata metadata){
        Environment env = context.getEnvironment();
        //检查magic变量是否存在
        return env.containsProperty("magic");
    }
}

通过 ConditionContext ,我们可以做到如下几点:

  • 借助 getRegistry() 返回的 BeanDefinitionRegistry 检查 bean 定义;
  • 借助 getBeanFactory() 返回的 ConfigurableListableBeanFactory 检查 bean 是否存在,甚至探查 bean 的属性;
  • 借助 getEnvironment() 返回的 Environment 检查环境变量是否存在以及它的值是什么;
  • 读取并探查 getResourceLoader() 返回的 ResourceLoader 所加载的资源;
  • 借助 getClassLoader() 返回的 ClassLoader 加载并检查类是否存在。

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

从 Spring 4 开始, @Profile 注解进行了重构,使其基于 @Conditional 和 Condition 实现。

3.3 处理自动装配的歧义性

当一个接口有多个实现时,使用Autowired注解自动注入时,程序不知道采用哪个实现会出现问题。

标示首选bean ,使用注解@Primary

限定自动装配的bean,使用注解@Qualifier

@Component
@Qualifier("cold")
public class IceCream implements Dessert{...}
?
@Autowired
@Qualifier("cold")
public void setDessert(Dessert desert){
    this.desert = desert;
}

注意:@Qualifier注解可以多个并用。

3.4 bean的作用域

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

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

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

//这里,使用 ConfigurableBeanFactory 类的 SCOPE_PROTOTYPE 常量设置了原型作用域。你当然也可以使用 @Scope("prototype") ,但是使用 SCOPE_PROTOTYPE 常量更加安全并且不易出错。
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Notepad notepad(){
    return new Notepad();
}
<bean id="notepad" class="com.myapp.Notepad" scope="prototype"/>

使用会话和请求作用域

@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION ,proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ShoppingCart {

}
?

我们将 value 设置成了 WebApplicationContext 中的 SCOPE_SESSION 常量(它的值是 session )。这会告诉 Spring 为 Web 应用中的每个会话创建一个 ShoppingCart 。这会创建多个 ShoppingCart bean 的实例,但是对于给定的会话只会创建一个实例,在当前会话相关的操作中,这个 bean 实际上相当于单例的。

现在,我们带着对这个作用域的理解,讨论一下 proxyMode 属性。如配置所示, proxyMode 属性被设置成了 ScopedProxyMode.INTERFACES ,这表明这个代理要实现 ShoppingCart 接口,并将调用委托给实现 bean 。如果 ShoppingCart 是接口而不是类的话,这是可以的(也是最为理想的代理模式)。但如果 ShoppingCart 是一个具体的类的话, Spring就没有办法创建基于接口的代理了。此时,它必须使用 CGLib 来生成基于类的代理。所以,如果 bean 类型是具体类的话,我们必须要将 proxyMode 属性设置为 ScopedProxyMode.TARGET_CLASS,以此来表明要以生成目标类扩展的方式创建代理。

在 XML 中声明作用域代理:

<bean id="cart" class="com.myapp.ShoppingCart" scope="session">
    <aop:scoped-proxy/>
</bean>

<aop:scoped-proxy> 是与 @Scope 注解的 proxyMode 属性功能相同的 Spring XML 配置元素。它会告诉 Spring 为 bean 创建一个作用域代理。默认情况下,它会使用 CGLib 创建目标类的代理。但是我们也可以将 proxy-target-class 属性设置为 false ,进而要求它生成基于接口的代理。

3.5 运行时注入值

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

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

app.properties

test.name = hahatest.intt = 1

RootConfig.java

@Configuration
@ComponentScan(basePackages = {"ch03.service"})
@PropertySource("classpath:app.properties")
public class RootConfig {
?
    @Bean
    public Root createRoot(){
        return new Root();
    }
?
    @Bean(name = "placeholderConfigurer")
    public static CustomizedPropertySourcesPlaceholderConfigurer placeholderConfigurer() {
?
        return new CustomizedPropertySourcesPlaceholderConfigurer();
    }
?
}

使用

  @Value("${test.name}")
  String name;

属性文件会加载到 Spring 的 Environment 中。

使用@Value注解是一种简便方式,当然也可以获取到spring容器中的Environment调用其方法来获取属性,例如env.getProperty("test.name")

除了属性相关的功能以外, Environment 还提供了一些方法来检查哪些 profile 处于激活状态:

  • String[] getActiveProfiles() :返回激活 profile 名称的数组;
  • String[] getDefaultProfiles() :返回默认 profile 名称的数组;
  • boolean acceptsProfiles(String... profiles) :如果 environment 支持给定 profile 的话,就返回 true

为了使用占位符${...},我们必须要配置一个 PropertyPlaceholderConfigurer bean 或PropertySourcesPlaceholderConfigurerbean 。从 Spring 3.1 开始,推荐使用 PropertySourcesPlaceholderConfigurer ,因为它能够基于 Spring Environment 及其属性源来解析占位符。

  @Bean(name = "placeholderConfigurer")
    public static CustomizedPropertySourcesPlaceholderConfigurer placeholderConfigurer() {
        return new CustomizedPropertySourcesPlaceholderConfigurer();
    }
? 

使用 Spring 表达式语言进行装配

SpEL 拥有很多特性,包括:

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

需要了解的第一件事情就是 SpEL 表达式要放到 “#{ ... }” 之中,这与属性占位符有些类似,属性占位符需要放到 “${ ... }” 之中。

T()表达式会将 java.lang.System 视为 Java 中对应的类型,因此可以调用其 static 修饰的 currentTimeMillis() 方法。

正则例子:

“[]” 运算符用来从集合或数组中按照索引获取元素。SpEL 还提供了查询运算符( .?[] ),它会用来对集合进行过滤,得到集合的一个子集。SpEL 还提供了另外两个查询运算符: “.^[]” 和 “.$[]” ,它们分别用来在集合中查询第一个匹配项和最后一个匹配项。 SpEL 还提供了投影运算符( .![] ),它会从集合的每个成员中选择特定的属性放到另外一个集合中。实际上,投影操作可以与其他任意的 SpEL 运算符一起使用。

@Value(value = "#{placeholderConfigurer.getProperty(‘test.name‘)}")
String name1;
@Value("#{T(java.lang.Integer).valueOf(placeholderConfigurer.getProperty(‘test.intt‘)) + 10}")
Integer intt;

原文地址:https://www.cnblogs.com/xzcode/p/9287315.html

时间: 2024-10-10 08:55:11

第3章 高级装配的相关文章

第三章 高级装配

Spring profile 条件化的bean声明 自动装配与歧义性 bean的作用域 Spring表达式语言 配置profile bean 在3.1版本中, Spring引入了bean profile的功能. 要使用profile, 你首先要将所有不同的bean定义整理到一个或多个profile之中, 在将应用部署 到每个环境时, 要确保对应的profile处于激活(active) 的状态. 在Java配置中, 可以使用@Profile注解指定某个bean属于哪一个profile. 例如, 在

第3章—高级装配—创建自定义的限定符

创建自定义的限定符 ? 我们可以为bean设置自己的限定符,而不是依赖于将bean ID作为限定符.在这里所需要做的就是在bean声明上添加@Qualifier注解来进行更加明确的区分.例如: Animal接口: package com.home.quar; public interface Animal { public void eat(); } Cat: package com.home.quar; import org.springframework.beans.factory.anno

Java 线程第三版 第六章 高级同步议题 读书笔记

多线程数据同步错误比较难检测,因为通常是与事件的特定发生顺序有关. 一.同步术语 Barrier(屏障) barrier是多个Thread的集合点:所有的Thread都应该到齐在这个barrier之后才能允许它们继续下去. Condition variable(条件变量) 实际上不是变量,而是与某个lock有关联的变量. Event variable(事件变量) 条件变量的另一个名称. Critical section(临界区) 临界区是synchronized方法或者block. Lock(锁

第9章 高级 SQL Server 配置

第9章  高级 SQL Server 配置 9.1  使用sp_config 9.1.1  执行查询 9.1.2  配置基本参数 9.1.3  配置高级参数 9.2  管理员专用连接 9.3  使用SQLCMD

java MySQL数据库编程 第四章 高级查询(二)

第四章 高级查询(二) (1)通过在子查询中使用EXISTS子句,可以对子查询中的行是否存在进行检查.子查询可以出现在表达式出现的如何位置 (2)子查询语句可以嵌套在SQL语句中任何表达式出现的位置. 一.EXISTS子查询 1.使用EXISTS语句判断该数据库对象是否存在: DROP TABLE IF EXISTS temp; 2. EXISTS作为WHERE语句的子查询: SELECT .....FROM 表名 WHERE EXISTS(子查询); 3. EXISTS关键字后面的参数是一个任

深入理解Magento – 第六章 – 高级Magento模型(转)

深入理解Magento 作者:Alan Storm 翻译:Hailong Zhang 第六章 – 高级Magento模型 我们讲过Magento有两种模型,简单模型和EAV(Entity Attribute Value)模型.上一章我们讲过所有的Magento模型都是继承自Mage_Core_Model_Abstract / Varien_Object.简单模型和EAV模型的区别在于资源模型(Model Resource).虽然所有的资源模型都最终继承“Mage_Core_Model_Resro

spring(3)高级装配

[0]README 0)本文部分文字描述转自:"Spring In Action(中/英文版)",旨在review  spring(3)高级装配 的相关知识: [1]环境与profile(考虑数据库配置) 1)使用嵌入式数据库 @Bean(destroyMethod="shutdown") public DataSource dataSource() { return new EmbeddedDatabaseBuilder() .addScript("cl

第七章 高级环境

第七章 高级环境 7.2 main函数 C程序总是从main函数开始执行的.main函数的原型是: int main(int argc, char *argv[]); 其中,argc是命令行参数的数目,argv是指向参数的各个指针所构成的数组. 当内核执行C程序的时候(使用一个exec函数),在调用main前先调用一个特殊的启动例程.可执行程序文件将此启动例程指定为程序的起始地址-- 这是由连接编辑器设置的,而连接编辑器则是由C编译器调用的.启动例程从内核取得命令行参数和环境变量值,然后为按上述

第六章 高级加密标准

第六章 高级加密标准 美国国家技术研究所NIST在2001年发布了高级加密标准AES,一个对称分组密码算法,取代DES称为广泛使用的标准. 与公钥密码RSA相比,AES以及大多数的对称密码的结构都很复杂. AES中所有的运算是在8位的字节上进行的. 一个域是一个结合,在集合内进行加减乘除运算的结果,也不会离开该域.例如\(Z_p=\{0,1,2,3,\dots,p-1\}\),其中\(p\)是一个素数,然后该域上的操作是取模. 1. AES的结构 明文分组长度为128位即16字节(对明文进行分组