Spring中资源和环境的配置

Spring中资源和环境的配置_第1张图片
摘要

配置文件的混用

  • 使用@ImportResource 在Java Config 类中导入XML配置文件
  • 在XML中引入Java Config

@Import 注解

  • 使用@Import来导入整合Java Config
@Configuration
public class Config {
    @Bean
    public OneBean oneBean() {
        return new OneBean();
    }
}

@Configuration
@Import(Config.class)
public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
        OneBean oneBean = context.getBean("oneBean", OneBean.class);
        System.out.println(oneBean);
    }
}
  • 根据ImportSelector接口selectImports方法的返回值来导入相关配置类
//自定义的注解,用来自动配置OneBean对象
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(OneBeanImportSelector.class)
public @interface EnableOneBean {
    boolean value() default true;
}
//OneBean对象的Java Config
@Configuration
public class Config {
    @Bean
    public OneBean oneBean() {
        return new OneBean();
    }
}
//主配置文件,注意,主配置文件中并没有导入OneBean对象的Java Config
@EnableOneBean
@Configuration
public class Demo {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
        OneBean oneBean = context.getBean("oneBean", OneBean.class);
        System.out.println(oneBean);
    }
}

//是否需要配置OneBean对象的逻辑
public class OneBeanImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //AnnotationMetadata对象可以获取注解上的信息,可以根据注解上的信息来判断是否需动态注入bean
        Map attributes = importingClassMetadata.getAnnotationAttributes(EnableOneBean.class.getName());
        if ((boolean) attributes.get("value")) {
            return new String[]{Config.class.getName()};
        }
        return null;
    }
}
  • 使用ImportBeanDefinitionRegistrar接口动态注册bean
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(OneBeanImportBeanDefinitionRegistrar.class)
public @interface EnableOneBean {
    boolean value() default true;
}
public class OneBeanImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //AnnotationMetadata 对象可以获取 注解上的信息,可以根据注解上的信息来判断是否需动态注入bean
        Map attributes = importingClassMetadata.getAnnotationAttributes(EnableOneBean.class.getName());
        if ((boolean) attributes.get("value")) {
            //BeanDefinitionRegistry 对象可以用来 动态的往Spring 容器中添加Bean
            //使用 BeanDefinitionBuilder 来构建 BeanDefinition
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(OneBean.class);
            builder.setScope(BeanDefinition.SCOPE_SINGLETON);
//            builder.addPropertyValue("name", "xxx");
            registry.registerBeanDefinition("oneBean", builder.getBeanDefinition());
        }
    }
}
@Configuration
@EnableOneBean
public class Config {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        OneBean oneBean = context.getBean("oneBean", OneBean.class);
    }
}

占位符的支持

  • XML



    
    
    

简单分析一下原理:
我们这里引入了命名空间,Spring会有相应的类根据去解析我们引入的命名空间,代码如下

public class ContextNamespaceHandler extends NamespaceHandlerSupport {

    @Override
    public void init() {
        registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
        ····
    }
}

也就是说,Spring解析到我们在XML中使用了property-placeholder,就会自动为我们创建一个PropertyPlaceholderBeanDefinitionParser对象。该类的代码如下

class PropertyPlaceholderBeanDefinitionParser extends AbstractPropertyLoadingBeanDefinitionParser {
    private static final String SYSTEM_PROPERTIES_MODE_ATTRIBUTE = "system-properties-mode";

    private static final String SYSTEM_PROPERTIES_MODE_DEFAULT = "ENVIRONMENT";

    @Override
    protected Class getBeanClass(Element element) {
        // As of Spring 3.1, the default value of system-properties-mode has changed from
        // 'FALLBACK' to 'ENVIRONMENT'. This latter value indicates that resolution of
        // placeholders against system properties is a function of the Environment and
        // its current set of PropertySources.
        if (SYSTEM_PROPERTIES_MODE_DEFAULT.equals(element.getAttribute(SYSTEM_PROPERTIES_MODE_ATTRIBUTE))) {
            return PropertySourcesPlaceholderConfigurer.class;
        }

        // The user has explicitly specified a value for system-properties-mode: revert to
        // PropertyPlaceholderConfigurer to ensure backward compatibility with 3.0 and earlier.
        return PropertyPlaceholderConfigurer.class;
    }
    ···

从代码的注释中,我们可以知道,在Spring3.1之后,Spring使用PropertySourcesPlaceholderConfigurer类来支持Spring的参数占位符填充。所以,XML的另一种配置的方式为:


    



    
    
    

  • Java Config
@Configuration
@PropertySource("db.properties") // 使用该注解来引入外部资源文件
public class Config {

    /**
     * 使用PropertySourcesPlaceholderConfigurer 来解析占位符
     * 

* 注意: 要确保该Bean在使用占位符之前就已经被初始化了,使用static修饰,保存该Bean初始化的优先级 */ @Bean public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurerP() { return new PropertySourcesPlaceholderConfigurer(); } @Value("${db.username}") // 可以通过 @Value 来引入占位符 private String username; @Value("${db.password}") private String password; @Value("${db.url}") private String url; }

profile

注意: Spring 并不是在构建的时候去根据环境选择是否要创建Bean,而是在等到运行时期在确定,所以能够适用于所有的环境,没必要重新构建项目。

配置profile

  • XML

    
        
    

  • Java Config

@Profile注解用于指定某个Bean属于哪一个Profile,该注解也可以直接在配置类上使用,表明该配置类中的Bean都属于某一个Profile。

@Configuration
public class Config {

    @Profile("dev")
    @Bean
    public OneBean devOneBean() {
        return new OneBean("devBean");
    }

    @Profile("test")
    @Bean
    public OneBean testOneBean() {
        return new OneBean("testBean");
    }
}

激活Profile

  1. 如果设置了spring.profiles.active属性,那么就更他的值来确定激活哪个profile。
  2. 如果没有设置,就会根据spring.profiles.default属性的值来激活profile。
  3. 如果前面两者都没有确定的话,那就只会构建没有profile的Bean。
  4. 有多种方式来设置这两个属性的值:
  • JVM的启动参数
  • 作为DispatcherServlet的初始化参数
  • 作为Web应用的上下文
  • 作为环境变量
  • 在测试类中,使用@ActiveProfile

Spring的环境Environment接口

Environment 接口主要就是两个作用:

  • 获取环境中的属性值
  • 获取profile的激活状态

@Conditional注解和Condition接口

  • @Conditional可以根据满足某一特定的条件来创建一个特别的bean
  • 可以通过实现Condition接口的matches方法来构造判断条件
public class ExistOneBean implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 如果Spring 的环境中存在 OneBean 对象, 就返回true
        return context.getBeanFactory().getBeansOfType(OneBean.class).size() > 0;
    }
}
@Configuration
public class Config {

    @Bean
    public OneBean oneBean() {
        return new OneBean();
    }

    @Bean
    @Conditional(ExistOneBean.class) // 如果当前环境中存在OneBean,才实例化该Bean
    public LocalDateTime dateTime() {
        return LocalDateTime.now();
    }
}

@Profile的实现原理

在Spring4中,@Profile也使用@Conditional注解来实现。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
    /**
     * The set of profiles for which the annotated component should be registered.
     */
    String[] value();
}
class ProfileCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        if (context.getEnvironment() != null) {
            MultiValueMap attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
            if (attrs != null) {
                for (Object value : attrs.get("value")) {
                    if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
                        return true;
                    }
                }
                return false;
            }
        }
        return true;
    }
}

你可能感兴趣的:(Spring中资源和环境的配置)