spring中高级装配

Posted ssc在路上

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring中高级装配相关的知识,希望对你有一定的参考价值。

  大概看了一下第三章的内容,我从项目中仔细寻找,始终没有发现哪里有这种配置,但是看完觉得spring还有这么牛B的功能啊,spring的厉害之处,这种设计程序的思想,很让我感慨。。。

一、环境与profile

(1)配置profile bean

面对这样的需求:想出一种方法来配置DataSource,使其在每种环境下都会选择最为合适的配置,你会如何做呢?看看spring所提供的解决方案。spring中引入了bean profile的功能。在Java配置中,可以使用@Profile 注解指定某个bean属于哪一个profile。

1)在Java类中配置profile bean

package com.myapp;
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;

//@Profile注解应用在累计别上。它会告诉spring这个配置类中的bean只有在dev profile 激活时才会创建,没有激活,则不会创建
@Configuration
@Profile("dev")
public class DevelopmentProfileConfig{
   return new 
   EmbeddDatabaseBuilder().setType(EmbeddedDatebaseType.H2).
   addScript("classpath:schema.sql").addScript("classpath:test-
   data.sql").build();
}

package com.myapp;
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;

//@Profile注解应用在累计别上。它会告诉spring这个配置类中的bean只有在prod profile 激活时才会创建,没有激活,则不会创建
@Configuration
@Profile("prod")
public class ProductiontProfileConfig{
   JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
   jndiObjectFactoryBean.setJndiName("jdbc/myDS");
   jndiObjectFactoryBean.setResourceRef(true);
   jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
   return (DataSource) jndiObjectFactoryBean.getObject();
}

//可以将这两个bean的声明放置在同一个配置类中
package com.myapp;
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 DateSourceConfig{

 //为dev profile装配bean
 public DataSource embeddedDataSource(){
    return new 
   EmbeddDatabaseBuilder().setType(EmbeddedDatebaseType.H2).
   addScript("classpath:schema.sql").addScript("classpath:test-
   data.sql").build();
 }
 
 //为prod profile 装配bean
 public DataSource jbdiDataSource(){
    JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
   jndiObjectFactoryBean.setJndiName("jdbc/myDS");
   jndiObjectFactoryBean.setResourceRef(true);
   jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
   return (DataSource) jndiObjectFactoryBean.getObject();
 }
}

2)在XML中配置profile bean

//xml中配置的实际效果和在Java类中配置是一样的

//dev profile 的bean
<beans profile="dev">
    <jdbc:embedded-database id="dataSource">
        <jdbc:script location="classpath:schema.sql" />
        <jdbc:script location="classpath:test-data.sql" />
    </jdbc:embedded-database>
</beans>

//qa profile 的bean
<beans profile="qa">
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destory-method="close" p:url="jdbc:h2:tcp://deserver/~/test" p:driverClassName="org.h2.Driver" p:username="sa" p:password="password" p:initialSize="20" p:maxActive="30">
    </bean>
</beans>

//prod profile 的bean
<beans>
    <jee:jndi-lookup id="dataSource" jndi-name="jdbc/myDataSource" resource-ref="true" proxy-interfase="javax.sql.DataSource" />
</beans>

2)激活 profile

profile bean 配置OK了,如何来激活配置的bean呢?依赖两个独立的属性:spring.profiles.active 和 spring.profiles.default。设置了spring.profiles.active属性的话,那么它的值就会来确定哪个profile是激活的,如果设置了spring.profiles.default属性,那么spring 会查找spring.profiles.default的值,如果两个属性都设置的话,肯定会优先spring.profiles.active属性对应的profile bean 装配

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

1))作为DispatcherServlet的初始化参数

2))作为web应用的上下文参数

3))作为JNDI条目

4))作为环境变量

5))作为JVM的系统属性

6))在集成测试上,使用@ActiveProfiles注解设置

web.xml 中设置默认的profile

//为上下文设置默认的profile
<context-param>
    <param-name>spring.profile.default</param-name>
    <param-value>dev</param-value>
</context-param>

//为Servlet设置默认的profile 可以列出多个profile名称,并以逗号分隔来实现
<servlet>
    <servlet-name>app-servlet</servlet-name>
    <servlet-class>
        org.springframework.web.servlet.servlet.DispatcherServlet
    </servlet-class>
    <init-param>
        <param-name>spring.profiles.default</param-name>
        <param-value>dev</param-value>
    </init-param>
</servlet>

二、条件化的bean

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

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

//@Conditional 将会通过Condition 接口进行条件对比
public interface Condition{
  public boolean matches(ConditionContext ctcx,AnnotatedTypeMetadata metadata);  
}

//在Contidion 中检查是否存在magic属性,满足这个条件,matches()方法返回true,则所有的@Conditional 注解上引用MagicExistsCondition的bean都会被创建
public class MagicExistsCondition implements Condition{
  public boolean matches(ConditionContext context,AnnotatedTypeMetadata metadata){
    Environment env = context.getEnvironment();
    return env.containsProperty("magic");                       
  }  
}

注:这里讲解了两个比较特殊的接口,一个是ConditionContext,一个是AnnotatedTypeMetadata,做一个说明:

public interface ConditionContext{
  //检查bean的定义  
  BeanDefinitonRegistry getRegistry();  
  //检查bean是否存在,甚至探查bean的属性
  ConfigurableListableBeanFactory getBeanFactory();
  //检查环境变量是否存在以及它的值是什么
  Environment getEnvironment();
  //返回ResourceLoader所加载的资源
  ResourceLoader getResourceLoader();
  //返回ClassLoader加载并检查类是否存在
  ClassLoader getClassLoader();              
}

//检查带有@Bean注解的方法上还有什么其他的注解
public interface AnnotatedTypeMetadata{
  boolean isAnnotated(String annotationType);
  Map<String,Object> getAnnotationAttributes(String annotationType);      
  Map<String,Object> getAnnotationAttributes(String annotationType,boolean classValueAsString);
  MultiValueMap<String,Object> getAnnotationAttributes(String annotationType);
   MultiValueMap<String,Object> getAnnotationAttributes(String annotationType,boolean classValueAsString);
}

注:spring4 中对@Profile注解进行了重构,使其基于@Conditional 和 Condition实现,@Profile在spring4 中的实现:

/*
@Profile本身也使用了@Conditional注解,并且引用了ProfileCondition作为Condition实现,ProfileCondition在做出决策的过程中,考虑到了ConditionContext和AnnotatedTypeMetadata中多个因素
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
@Document
@Conditional(ProfileCondition.class)
public @interface Profile{
  String[] value();  
}

 

以上是关于spring中高级装配的主要内容,如果未能解决你的问题,请参考以下文章

spring学习总结——高级装配学习一(处理自动装配的歧义性)

spring—高级装配

Spring框架学习:@Qualifier实现高级装配

Spring实战Spring高级装配中的bean profile

Spring高级装配 条件化的bean

第三章 高级装配