Spring中資源和環(huán)境的配置

摘要

配置文件的混用

  • 使用@ImportResource 在Java Config 類中導(dǎo)入XML配置文件
  • 在XML中引入Java Config
<bean class="com.me.xml.Config"/>

@Import 注解

  • 使用@Import來導(dǎo)入整合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);
    }
}
  • 根據(jù)ImportSelector接口selectImports方法的返回值來導(dǎo)入相關(guān)配置類
//自定義的注解桐猬,用來自動(dòng)配置OneBean對(duì)象
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(OneBeanImportSelector.class)
public @interface EnableOneBean {
    boolean value() default true;
}
//OneBean對(duì)象的Java Config
@Configuration
public class Config {
    @Bean
    public OneBean oneBean() {
        return new OneBean();
    }
}
//主配置文件办铡,注意愈捅,主配置文件中并沒有導(dǎo)入OneBean對(duì)象的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對(duì)象的邏輯
public class OneBeanImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //AnnotationMetadata對(duì)象可以獲取注解上的信息,可以根據(jù)注解上的信息來判斷是否需動(dòng)態(tài)注入bean
        Map<String, Object> attributes = importingClassMetadata.getAnnotationAttributes(EnableOneBean.class.getName());
        if ((boolean) attributes.get("value")) {
            return new String[]{Config.class.getName()};
        }
        return null;
    }
}
  • 使用ImportBeanDefinitionRegistrar接口動(dòng)態(tài)注冊(cè)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 對(duì)象可以獲取 注解上的信息,可以根據(jù)注解上的信息來判斷是否需動(dòng)態(tài)注入bean
        Map<String, Object> attributes = importingClassMetadata.getAnnotationAttributes(EnableOneBean.class.getName());
        if ((boolean) attributes.get("value")) {
            //BeanDefinitionRegistry 對(duì)象可以用來 動(dòng)態(tài)的往Spring 容器中添加Bean
            //使用 BeanDefinitionBuilder 來構(gòu)建 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
<context:property-placeholder location="db.properties"/>

<bean id="dataSource" class="com.me.xml.DataSourceProperties">
    <property name="url" value="${db.url}"/>
    <property name="username" value="${db.username}"/>
    <property name="password" value="${db.password}"/>
</bean>

簡(jiǎn)單分析一下原理:
我們這里引入了命名空間,Spring會(huì)有相應(yīng)的類根據(jù)去解析我們引入的命名空間麦射,代碼如下

public class ContextNamespaceHandler extends NamespaceHandlerSupport {

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

也就是說磷醋,Spring解析到我們?cè)赬ML中使用了property-placeholder,就會(huì)自動(dòng)為我們創(chuàng)建一個(gè)PropertyPlaceholderBeanDefinitionParser對(duì)象。該類的代碼如下

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的參數(shù)占位符填充苍蔬。所以诱建,XML的另一種配置的方式為:

<bean id="propertySourcesPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
    <property name="location" value="db.properties"/>
</bean>

<bean id="dataSource" class="com.me.xml.DataSourceProperties">
    <property name="url" value="${db.url}"/>
    <property name="username" value="${db.username}"/>
    <property name="password" value="${db.password}"/>
</bean>
  • Java Config
@Configuration
@PropertySource("db.properties") // 使用該注解來引入外部資源文件
public class Config {

    /**
     * 使用PropertySourcesPlaceholderConfigurer 來解析占位符
     * <p>
     * 注意: 要確保該Bean在使用占位符之前就已經(jīng)被初始化了,使用static修飾,保存該Bean初始化的優(yōu)先級(jí)
     */
    @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 并不是在構(gòu)建的時(shí)候去根據(jù)環(huán)境選擇是否要?jiǎng)?chuàng)建Bean,而是在等到運(yùn)行時(shí)期在確定碟绑,所以能夠適用于所有的環(huán)境俺猿,沒必要重新構(gòu)建項(xiàng)目。

配置profile

  • XML
<beans profile="dev">
    <bean id="devBean" class="com.me.xml.OneBean">
        <constructor-arg value="dev"/>
    </bean>
</beans>
  • Java Config

@Profile注解用于指定某個(gè)Bean屬于哪一個(gè)Profile格仲,該注解也可以直接在配置類上使用押袍,表明該配置類中的Bean都屬于某一個(gè)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. 如果設(shè)置了spring.profiles.active屬性凯肋,那么就更他的值來確定激活哪個(gè)profile谊惭。
  2. 如果沒有設(shè)置,就會(huì)根據(jù)spring.profiles.default屬性的值來激活profile。
  3. 如果前面兩者都沒有確定的話圈盔,那就只會(huì)構(gòu)建沒有profile的Bean豹芯。
  4. 有多種方式來設(shè)置這兩個(gè)屬性的值:
  • JVM的啟動(dòng)參數(shù)
  • 作為DispatcherServlet的初始化參數(shù)
  • 作為Web應(yīng)用的上下文
  • 作為環(huán)境變量
  • 在測(cè)試類中,使用@ActiveProfile

Spring的環(huán)境Environment接口

Environment 接口主要就是兩個(gè)作用:

  • 獲取環(huán)境中的屬性值
  • 獲取profile的激活狀態(tài)

@Conditional注解和Condition接口

  • @Conditional可以根據(jù)滿足某一特定的條件來創(chuàng)建一個(gè)特別的bean
  • 可以通過實(shí)現(xiàn)Condition接口的matches方法來構(gòu)造判斷條件
public class ExistOneBean implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 如果Spring 的環(huán)境中存在 OneBean 對(duì)象, 就返回true
        return context.getBeanFactory().getBeansOfType(OneBean.class).size() > 0;
    }
}
@Configuration
public class Config {

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

    @Bean
    @Conditional(ExistOneBean.class) // 如果當(dāng)前環(huán)境中存在OneBean驱敲,才實(shí)例化該Bean
    public LocalDateTime dateTime() {
        return LocalDateTime.now();
    }
}

@Profile的實(shí)現(xiàn)原理

在Spring4中铁蹈,@Profile也使用@Conditional注解來實(shí)現(xiàn)。

@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<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;
            }
        }
        return true;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末众眨,一起剝皮案震驚了整個(gè)濱河市木缝,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌围辙,老刑警劉巖我碟,帶你破解...
    沈念sama閱讀 216,997評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異姚建,居然都是意外死亡矫俺,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門掸冤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來厘托,“玉大人,你說我怎么就攤上這事稿湿∏ζィ” “怎么了?”我有些...
    開封第一講書人閱讀 163,359評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵饺藤,是天一觀的道長(zhǎng)包斑。 經(jīng)常有香客問我,道長(zhǎng)涕俗,這世上最難降的妖魔是什么罗丰? 我笑而不...
    開封第一講書人閱讀 58,309評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮再姑,結(jié)果婚禮上萌抵,老公的妹妹穿的比我還像新娘。我一直安慰自己元镀,他們只是感情好绍填,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評(píng)論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著栖疑,像睡著了一般讨永。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蔽挠,一...
    開封第一講書人閱讀 51,258評(píng)論 1 300
  • 那天住闯,我揣著相機(jī)與錄音瓜浸,去河邊找鬼。 笑死比原,一個(gè)胖子當(dāng)著我的面吹牛插佛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播量窘,決...
    沈念sama閱讀 40,122評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼雇寇,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了蚌铜?” 一聲冷哼從身側(cè)響起锨侯,我...
    開封第一講書人閱讀 38,970評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎冬殃,沒想到半個(gè)月后囚痴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,403評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡审葬,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評(píng)論 3 334
  • 正文 我和宋清朗相戀三年深滚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涣觉。...
    茶點(diǎn)故事閱讀 39,769評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡痴荐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出官册,到底是詐尸還是另有隱情生兆,我是刑警寧澤,帶...
    沈念sama閱讀 35,464評(píng)論 5 344
  • 正文 年R本政府宣布膝宁,位于F島的核電站鸦难,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏昆汹。R本人自食惡果不足惜明刷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望满粗。 院中可真熱鬧,春花似錦愚争、人聲如沸映皆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽捅彻。三九已至,卻和暖如春鞍陨,著一層夾襖步出監(jiān)牢的瞬間步淹,已是汗流浹背从隆。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留缭裆,地道東北人键闺。 一個(gè)月前我還...
    沈念sama閱讀 47,831評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像澈驼,于是被迫代替她去往敵國(guó)和親辛燥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評(píng)論 2 354