Spring笔记——Spring高级配置

1、环境与profile

  在开发软件的时候,有一个很大的挑战就是将应用软件从一个环境迁移到另一个环境,开发阶段中,某些环境相关做法可能并不适合迁移到生产环境,甚至即便迁移过去也无法正常运行。为此Spring可根据环境(在运行时)去决定该创建那个bean和不创建那个bean。这样同一个部署单元(可能是war文件)能够运用于所有的环境,没有必要进行重新构建。

  例如:开发环境下的DataSource 用EmbeddedDatabaseBuilder 创建,生产环境下的DataSource用JndiObjectFactoryBean创建更加合理。

1.1、配置profile bean

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

  • 通过Java配置类配置——@Profile
@Configuration
public class DataSourceConfig {
   @Bean(destroyMethod="shutdown")
   @Profile("dev") // 为dev profile装配的bean
   public DataSource embeddedDataSource() {
        return new ...
   }
   @Bean
   @Profile("prod") // 为prod profile装配的bean
   public DataSource jndiDataSource() {
        return new ...
    }   
} 
  • 通过XML配置profile
<beans ...>
    <beans profile="dev">
        <bean>...</bean>
    </beans>
    <beans profile="qa">
        <bean>...</bean>
    </beans>
    <beans profile="prod">
        <bean>...</bean>
    </beans>
</beans>

1.2、激活profile

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

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

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

  例如:Web应用的web.xml文件中设置默认的profile

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    version="2.5">
    <display-name>ssm-spring</display-name>

    <context-param><!-- 为上下文设置默认profile -->
        <param-name>spring.profiles.default</param-name>
        <param-value>dev</param-value>
    </context-param>
    
    <!-- springmvc前端控制器 -->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframwork.web.servlet.DispatcherServlet</servlet-class>
        <init-param><!-- 为Servlet设置默认profile -->
            <param-name>spring.profiles.default</param-name>
            <param-value>dev</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

2、条件化的Bean

  【条件】

  • 希望一个或多个bean只有在应用的类路径下包含特定的库时才创建;
  • 希望某个bean只有当另外某个特定的bean也声明了之后才会创建 ;
  • 要求只有某个特定的环境变量设置之后,才会创建某个bean

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

@Configuration
@ComponentScan
public class BeanConfig {

    @Bean
    @Conditional(MagicExistsCondition.class) // 设置创建bean的条件
    public MagicBean magicBean() {
        return new MagicBean();
    }
}

public class MagicExistsCondition implements Condition {

    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment ev = context.getEnvironment(); 
        return ev.containsProperty("magic"); // 检查magic属性是否存在
    }

}

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

  当在Spring上下文中存在多个bean能够匹配所需的结果时,@autowired自动装配将产生歧义,Spring将抛出NoUniqueBeanDefinitionException异常。

  解决办法:

  • 标示首选的bean
@Component
@Primary
public class IceCrean implements Dessert {...}
<bean id="iceCream" class="..." primary="true" />
  • 限定自动装配的bean——使用@Qualifier

  (1)、使用@Qualifier("iceCream")限定bean的范围。@Qualifier注解所设置的参数就是想要注入的beanID;

  (2)、使用自定义的限定符注解

@Component
@Qualifier("cold")
public class Poosicle implements Dessert {...}

@Bean
@Qualifier("cold")
public Dessert iceCream() {
  return new IceCream();  
}

4、bean的作用域

  在默认情况下, Spring应用上下文中所有bean都是作为以单例(singleton)的形式创建的。 也就是说,不管给定的一个bean被注入到其他bean多少次,每次所注入的都是同一个实例。
  Spring定义了多种作用域:

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

  4.1、使用@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE )设置bean的作用域;

  4.2、使用<bean id="xxx" class="xxxx" scope="" />设置bean的作用域。

5、运行时值注入

   运行时注入的两种方式:

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

5.1、注入外部的值

  (1)、通过Java配置类注入

@Configuration
@PropertySource("classpath:com/zhux/ssm/app.properties") // 声明属性源
public class ExpressiveConfig {

    @Autowired
    Environment env;
    
    @Bean
    public MagicBean magicBean() {
        return new MagicBean(env.getProperty("magic.title"), env.getProperty("magic.arttist")); // 检索属性值
    }
}

  (2)、通过XML注入

<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  <property name="driverClassName" value="${jdbc.driver}" />
  <property name="url" value="${jdbc.url}" />
  <property name="username" value="${jdbc.username}" />
  <property name="password" value="${jdbc.password}" />
</bean>

  (3)、使用@Value注解——如果我们依赖于组件扫描和自动装配来创建和初始化应用组件的话,那么就没有指定占位符的配置文件或类了 。此时我们可以使用@Value注解。为了使用占位符,我们必须要配置一个PropertyPlaceholderConfigurer beanPropertySourcesPlaceholderConfigurer bean

public class MagicBean {
  public MagicBean(@Value("${magic.title}") title, @Value("${magic.artist}") artist) {     ...   }


// Java配置类中
@Bean
public static PropertySourcesPlaceholderConfigurer p
laceholderConfigurer {
  return new PropertySourcesPlaceholderConfigurer();
}

 5.2、使用Spring表达式语言进行装配——SpEL

原文地址:https://www.cnblogs.com/zhuxiong/p/7653506.html