Spring 核心(IOC)- 环境抽象

    技术2024-12-28  15

    目录

    环境抽象1. Profile操作2. Properties操作

    环境抽象

    环境接口是一个集成在容器中的抽象,它主要的两个方面为:Profiles 和 Properties。

    public interface Environment extends PropertyResolver { //活动的Profiles名称 String[] getActiveProfiles(); //默认Profiles名称 String[] getDefaultProfiles(); //返回一个或多个Profiles是否为正在活动 @Deprecated boolean acceptsProfiles(String... profiles); //返回该Profiles是否为正在活动 boolean acceptsProfiles(Profiles profiles); } public interface PropertyResolver { //返回是否包含此属性 boolean containsProperty(String key); //获取属性值 @Nullable String getProperty(String key); //获取属性值,如果没找到,则返回默认值 String getProperty(String key, String defaultValue); //获取指定类型的属性值 @Nullable <T> T getProperty(String key, Class<T> targetType); //获取指定类型的属性值,如果没找到,则返回默认值 <T> T getProperty(String key, Class<T> targetType, T defaultValue); //获取属性值,如果没找到,则抛出异常 String getRequiredProperty(String key) throws IllegalStateException; //获取指定类型的属性值,如果没找到,则抛出异常 <T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException; //返回已解析的字符串 String resolvePlaceholders(String text); //返回已解析的字符串,若无法解析,则抛出异常 String resolveRequiredPlaceholders(String text) throws IllegalArgumentException; }

    通过Environment接口和PropertyResolver父接口,了解到Environment接口本身实现Profiles 方面的功能,PropertyResolver父接口则实现Properties方面的功能。

    1. Profile操作

    Profile机制实现在不同环境下注册不同的Bean,如:

    在不同环境下配置不同的数据源为A客户和B客户定制不同的Bean实现仅在真正部署的性能环境下才注册的一些配置

    在以往,我们通常使用系统环境变量和带有${placeholder}令牌的XML<import/ >组合来根据系统环境变量值指定要导入的XML文件实现环境切换。然而,Profile机制才是提供解决这种问题的容器核心功能。

    使用@Profile注解来指定该组件(或配置类)的概要名称,此概要名称的环境被激活的时候才能注册到容器,否则该组件失效的。

    @Configuration @Profile("development") public class AConfig { @Bean public DataSource dataSource() { //... } } @Configuration @Profile("production") public class BConfig { @Bean public DataSource dataSource() { //... } }

    概要名称可以是一个简单的名称字符串,也可以是复杂的逻辑表达式,它可以使用&、|、!操作符。

    当要混合使用操作符时,你必须加上括号区分,如production & (us-east | eu-central)

    在配置类上使用@Profile时,在未激活此环境下,该配置类中的所有Bean方法和@Import注解都将失效。

    当然,@Profile还可以在方法上使用,如一个配置类中指定的Bean方法。

    @Configuration public class AConfig { @Profile("dev1") @Bean("dataSource") public DataSource standaloneDataSource() { //... } @Profile("dev2") @Bean("dataSource") public DataSource jndiDataSource() { //... } } //模拟测试 public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(); //手动设置环境 ac.getEnvironment().setActiveProfiles("dev1"); System.out.println(Arrays.toString(ac.getEnvironment().getActiveProfiles())); //注册配置类 ac.register(AConfig.class); //刷新容器 ac.refresh(); //查看该容器所注册的组件 System.out.println(Arrays.toString(ac.getBeanDefinitionNames())); } }

    当你想定义具有不同概要条件的同一类型Bean方法时,请使用不同的Java方法名称,同时用@Bean中的name属性指定相同Bean名称,参考上述例子。

    对于@Bean上的@Profile,当重载了相同Java名称的Bean方法情况下,你需要在所有重载的Bean方法上声明一致的@Profile(@Bean不指定Bean名称时会选择参数签名最长的那一个注册),若概要条件不一致,则会根据情况存在不确定性的Bean注册。所以,@Profile并不能用于选择具有特定参数签名的重载方法。

    //测试 @Configuration public class Aconfig { @Profile("dev1") @Bean public Adao adao(@Value("1") int a, @Value("2") int b){ System.out.println(3); return new Adao(); } @Profile("dev1") @Bean public Adao adao(){ System.out.println(2); return new Adao(); } @Profile("dev1") @Bean public Adao adao(@Value("1") int a,@Value("2") int b,@Value("3") int c){ System.out.println(1); return new Adao(); } }

    在XML配置中,profile是作为<beans>的属性进行概要配置的,可以在不同配置文件<beans>中使用,也可以嵌套<beans>使用。

    <beans profile="development" xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> ... </beans> <beans profile="production" xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> ... </beans>

    XML配置的形式仅支持!操作符,但可以通过嵌套<beans>的方式来实现&。

    而最直接激活Profile的方式就是通过容器上下文编程式的激活,上一节中就是采用这种方式。此外,还可以使用spring.profiles.active属性,它通过系统环境变量、JVM系统属性、web.xml的servlet上下文参数或者是作为JNDI中的一个条目来指定。

    在单页测试中,还可以通过@ActiveProfiles指定环境概要。

    Spring还提供一种默认概要的配置,若没有概要配置被激活,则采用默认的概要配置,反之则不采用。

    @Configuration @Profile("default") public class DefaultDataConfig { @Bean public DataSource dataSource() { //... } }

    通过default的概要名称来指定默认概要配置,当然,你也可以通过容器上下文中的setDefaultProfiles()方法来在自定义默认概要的名称。

    2. Properties操作

    Spring环境抽象提供了对属性源方面的操作。如你可以通过容器上下文获取的环境对象来搜索属性。

    ApplicationContext ac = new AnnotationConfigApplicationContext(); //获取环境对象 Environment env = ac.getEnvironment(); //搜索属性 boolean containsMyProperty = env.containsProperty("my-property"); System.out.println("Does my environment contain the 'my-property' property? " + containsMyProperty);

    Spring的StandardEnvironment实现类默认使用了JVM系统属性集和系统环境变量属性集,而StandardEnvironment是用于单体应用程序。通俗的讲,当你使用StandardEnvironment时,若存在一个my-property系统属性或my-property环境变量时,则env.containsProperty("my-property")返回true。

    StandardServletEnvironment默认使用其他属性源,包括servlet上下文参数和servlet配置等。

    这种搜索是分层的。默认情况下,系统属性是优先于环境变量的。例如,若两个位置都存在my-property属性,则系统属性的my-property会被采用,而环境变量中的my-property被覆盖。

    在公共的StandardServletEnvironment中,完整的层次结构如下,最高优先级从上到下:

    servlet配置参数(如DispatcherServlet上下文)servlet上下文参数(web.xml的context-param中)JNDI环境变量JVM系统属性JVM系统环境变量

    Spring中,关于属性源的整个机制是可配置的。你可以加入自定义配置源到当前环境的属性源集合中,如:

    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); //获取自定义属性源集合 MutablePropertySources sources = ctx.getEnvironment().getPropertySources(); //添加自定义属性源到最高优先级位置,当然也可以选择添加的优先级位置,通过其他方法 sources.addFirst(new MyPropertySource());

    MutablePropertySources实现类提供许多精确操作属性源集合的API。

    @PropertySource注解也是添加属性源至Spring环境的一种方式。

    //导入属性源 @PropertySource("my-properties.properties") @Configuration public class UserConfig { //${name:ww}指若容器环境中有name属性则注入name值,若无则注入默认值default @Bean public User user(@Value("${name:default}") String name){ return new User(name); } } @PropertySource("my-properties.properties") @Configuration public class UserConfig { //通过环境对象方式获取属性 @Autowired Environment env; @Bean public User user(){ return new User(env.getProperty("name")); } }

    在Java8之后,@PropertySource是可以重复的。但是所有@PropertySource必须定义在同一级别上,要么是在配置类上,要么是在元注解上。两者不能混用。

    Processed: 0.010, SQL: 9