360SDN.COM

Spring Framework 5 权威指南(18)

来源:程序员阮威  2017-09-11 13:22:15    评论:0点击:

这是一个Spring 5专题系列,其诞生的动机是:

1.介绍最新版本的Spring Framework;

2.介绍最系统化的Spring Framework;

3.介绍最佳实践的Spring Framework;

 1.环境抽象

Environment对象是容器对应用环境两个主要方面的模型的抽象:ProfilesProperties

10.1 Profile

Profile是被命名的一组bean定义的逻辑分组,允许容器在不同的环境下注册不同的beanEnvironment对象决定当前哪些profile是活跃的,哪些profile是默认活跃的。

例如,应用在开发环境需要访问下列数据源:

@Bean

public DataSource dataSource() {

        return newEmbeddedDatabaseBuilder()

               .setType(EmbeddedDatabaseType.HSQL)

               .addScript("my-schema.sql")

               .addScript("my-test-data.sql")

               .build();

}

应用在生产环境需要访问下列数据源:

@Bean(destroyMethod="")

public DataSource dataSource() throws Exception {

        Context ctx= new InitialContext();

        return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");

}

解决这种环境的切换,可以使用不同的环境变量来替换占位符:

<beans>

        <import resource="context-${env}.xml"/>

 

        <bean id="foo" class="com.example.MyFoo"/>

</beans>

Spring提供抽象的Profile方案。定义开发环境的profile如下:

<beans profile="development"

        xmlns="http://www.springframework.org/schema/beans"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xmlns:jdbc="http://www.springframework.org/schema/jdbc"

        xsi:schemaLocation="...">

 

        <jdbc:embedded-database id="dataSource">

                <jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>

                <jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>

        </jdbc:embedded-database>

</beans>

定义生产环境的profile如下:

<beans profile="production"

        xmlns="http://www.springframework.org/schema/beans"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xmlns:jee="http://www.springframework.org/schema/jee"

        xsi:schemaLocation="...">

 

        <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>

</beans>

也可以使用嵌套的<beans/>将二者合并在一个文件中:

<beans xmlns="http://www.springframework.org/schema/beans"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xmlns:jdbc="http://www.springframework.org/schema/jdbc"

        xmlns:jee="http://www.springframework.org/schema/jee"

        xsi:schemaLocation="...">

 

        <!-- other bean definitions -->

 

        <beans profile="development">

                <jdbc:embedded-database id="dataSource">

                       <jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>

                        <jdbc:scriptlocation="classpath:com/bank/config/sql/test-data.sql"/>

                </jdbc:embedded-database>

        </beans>

 

        <beans profile="production">

                <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>

        </beans>

</beans>

注意,定义profile属性的<beans>元素必须位于文件的最后。

等价的基于Java的配置元数据如下:

@Configuration

@Profile("development")

public class StandaloneDataConfig{

 

        @Bean

        publicDataSource dataSource() {

                return newEmbeddedDatabaseBuilder()

                       .setType(EmbeddedDatabaseType.HSQL)

                       .addScript("classpath:com/bank/config/sql/schema.sql")

                       .addScript("classpath:com/bank/config/sql/test-data.sql")

                        .build();

        }

}

@Configuration

@Profile("production")

public class JndiDataConfig {

 

        @Bean(destroyMethod="")

        publicDataSource dataSource() throws Exception {

               Context ctx = newInitialContext();

                return(DataSource) ctx.lookup("java:comp/env/jdbc/datasource");

        }

}

合并在一起如下:

@Configuration

public class AppConfig {

 

        @Bean("dataSource")

        @Profile("development")

        publicDataSource standaloneDataSource() {

                return newEmbeddedDatabaseBuilder()

                       .setType(EmbeddedDatabaseType.HSQL)

                       .addScript("classpath:com/bank/config/sql/schema.sql")

                       .addScript("classpath:com/bank/config/sql/test-data.sql")

                       .build();

        }

 

        @Bean("dataSource")

        @Profile("production")

        publicDataSource jndiDataSource() throws Exception {

               Context ctx = newInitialContext();

                return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");

        }

}

注意,@Profile注解支持非操作符,即@Profile({“p1”,”!p2”})

@Profile可以作为元注解来自定义组合注解,例如:

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Profile("production")

public @interface Production {

}

定义了profile以后,需要被激活才能注册和实例化相应的bean定义。

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();

ctx.getEnvironment().setActiveProfiles("development");

ctx.register(SomeConfig.class,StandaloneDataConfig.class, JndiDataConfig.class);

ctx.refresh();

另一种激活profile的方式是通过系统环境变量、JVM系统属性、servlet上下文参数等指定spring.profiles.active属性值。

可以同时激活多个profile,例如:

ctx.getEnvironment().setActiveProfiles("profile1", "profile2");

-Dspring.profiles.active="profile1,profile2"

默认profile表示默认被激活的profile

@Configuration

@Profile("default")

public class DefaultDataConfig{

 

        @Bean

        publicDataSource dataSource() {

                return newEmbeddedDatabaseBuilder()

                       .setType(EmbeddedDatabaseType.HSQL)

                       .addScript("classpath:com/bank/config/sql/schema.sql")

                       .build();

        }

}

默认profile的名字可以通过Environment.setDefaultProfiles()方法或者spring.profiles.default属性来指定。

@Profile注解是通过另一个更灵活的@Conditional注解实现的:

@Target(ElementType.TYPE, ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Conditional(ProfileCondition.class)

public @interface Profile {

 

        String[]value();

}

ProfileCondition的实现如下:

class ProfileCondition implements Condition{

 

    @Override

    public boolean matches(ConditionContextcontext, AnnotatedTypeMetadata metadata) {

        if(context.getEnvironment() != null) {

            // Read the@Profile annotation attributes

            MultiValueMap<String,Object> 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;

            }

        }

        returntrue;

    }

}

你可以实现自己的Condition,并直接使用@Conditional注解。

阅读原文

为您推荐

友情链接 |九搜汽车网 |手机ok生活信息网|ok生活信息网|ok微生活
 Powered by www.360SDN.COM   京ICP备11022651号-4 © 2012-2016 版权