[spring]applicationContext.xml-context標簽

緊接著上文淮野,我們來學習其他標簽捧书。

Spring 版本:4.3.14吹泡。

5. context命名空間

看完了beans命名空間下的標簽,接下來经瓷,我們來看下context命名空間下各種標簽的使用爆哑。
第一步,當然是引入context命名空間舆吮,引入方式上文已經(jīng)講過揭朝,先引入命名空間:

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

再進入對應的schema文件:

http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"

最后看下整體配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
        default-autowire="byType">
        ......
</beans>

引入命名空間之后队贱,我們就可以看下具體的標簽了。

5.1 <context:property-placeholder/>標簽

??Spring通過使用<context:property-placeholder/>標簽提供了一種優(yōu)雅的讀取參數(shù)配置的方式潭袱。比如說在開發(fā)的過程中柱嫌,我們通常需要相應的properties文件,來配置數(shù)據(jù)庫相關地址屯换,MQ相關地址编丘,分布式環(huán)境地址端口等相關參數(shù),而這些參數(shù)在不同的環(huán)境又是不同的彤悔,而通過context:property-placeholder則可以在程序中動態(tài)的讀取配置參數(shù)嘉抓。

<context:property-placeholder location="/resource/student.properties" />

在配置文件中,可以使用占位符進行解析:

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${jdbc.driver}"/>
</bean>

在程序中晕窑,我們可以通過@Value注解來解析:

@Value("${jdbc.address}")
private String jdbcAddress;

下面我們來簡單看下該參數(shù)的各項元素:

5.1.1 location元素
  1. location元素抑片,要讀取資源的resource,同樣可選的配置也有好多種杨赤,相對路徑敞斋,絕對路徑,classpath:疾牲,classpath*:渺尘,file:http:等说敏,如果要讀取多個文件鸥跟,可以使用逗號進行分割;
  2. 注意:如果配置了多個文件盔沫,而文件中有相同的屬性的話医咨,那么后一個文件中定義的屬性將會覆蓋掉前面文件中定義的屬性,也就是說最后文件中的屬性優(yōu)先級更高架诞。
5.1.2 file-encoding元素

文件編碼拟淮,通常情況下我們一般不需要配置;

5.1.3 ignore-resource-not-found元素

是否忽略對應的屬性文件找不到的問題谴忧。該元素有truefalse兩個選項很泊,默認情況下是false。true的時候表示忽略沾谓,false的時候表示不忽略委造,如果配置為false,并且在指定的位置沒有找到文件均驶,那么在運行的時候將會拋出一個異常昏兆。

5.1.4 ignore-unresolvable元素

是否忽略解析不了或替換不了的屬性。同樣該元素也是有truefalse兩個選項妇穴,默認情況下是false爬虱。false的時候表示不忽略隶债,如果解析不了相應的屬性,則將會拋出一個異常跑筝;

5.1.5 properties-ref元素

??其他properties配置死讹,我們可以從我們的properties文件中解析相應的配置參數(shù),當然也可以通過其他方式進行解析曲梗,然后我們通過properties-ref元素進行引入即可回俐。比如JDK中我們可以通過java.util.Properties來讀取相應的properties文件,這時候就可以通過該元素進行引入稀并,我么你來簡單看下:

<!-- 比如我們引用通過java.util.Properties進行配置的屬性 -->
<context:property-placeholder location="" properties-ref="properties"/>
<bean id="properties" class="java.util.Properties">
    <constructor-arg>
        <props>
            <prop key="test1">name1</prop>
            <prop key="test2">name2</prop>
        </props>
    </constructor-arg>
</bean>

<bean id="student" class="entity.Student">
    <property name="name" value="${test1}"/>
</bean>

然后測試打印下Student的name屬性仅颇,可以看到正常打印出相應的name值;

我們再來看下通過util:properties標簽引入的配置碘举,當然記得要先引入util命名空間(有關util命名空間的問題我們后面會學習到)忘瓦。

<context:property-placeholder location="" properties-ref="properties"/>
<util:properties id="properties">
    <prop key="key1">value1_local</prop>
    <prop key="key4">value4_local</prop>
    <prop key="key5">value5_local</prop>
</util:properties>

<bean id="student" class="entity.Student">
    <property name="name" value="${key1}"/>
</bean>

所以,該元素就是為了引入其他與properties相關的配置引颈。

5.1.6 local-override

本地是否覆蓋模式配置耕皮,元素有truefalse可選,默認情況下是false蝙场。如果配置為true凌停,那么properties-ref所引入的屬性配置將覆蓋掉location所加載的配置;

我們簡單測試下售滤,還拿上文的例子來說,我們添加一個student.properties文件完箩,在里面定義一個key1的變量:

key1=test

然后配置local-override="false"赐俗,然后看一下打印的Student中的name值:

Student student = ac.getBean("student", Student.class);
System.out.println(student.getName());

output:

test

然后,我們將local-override配置為true弊知,然后再看一下打印的值:

value1_local
5.1.7 null:value元素

這個值比較有意思阻逮,表示默認為null的設置。也就是說秩彤,如果配置的某一個屬性沒有值或空字符串叔扼,我們可以將它默認解析為null

比如漫雷,我們修改我們的student.properties文件:

key1=
key2=test

然后瓜富,修改我們的配置文件,添加上null:value元素:

<context:property-placeholder location="/resource/student.properties" null-value=""/>

<bean id="student" class="entity.Student">
    <property name="name" value="${key1}"/>
    <property name="id" value="${key2}"/>
 </bean>

這時候珊拼,我們測試一下我們配置的這兩個值:

Student student = ac.getBean("student", Student.class);
System.out.println(student.getName());
System.out.println(student.getId());

output:

null
test

可以看到食呻,name屬性解析為了null。這時候如果我們?nèi)サ粼撆渲庙椀脑捙煜郑俅蛴∠拢?/p>


test

可以看到仅胞,是按照空字符串進行解析的,而這種情況有的時候并不是我們所需要的剑辫。

我們可以配置null:value的值干旧,來確定將什么值來作為null來處理,如果我們不配置null:value的值妹蔽,將默認按照空字符串來解析椎眯。

我們來簡單測試下,先修改下student.properties文件:

key1=test1
key2=test2

然后設置null:value的值胳岂,將值是test2的時候默認做為null來解析编整,再次打印結果:

test1
null

可以看到,我們通過設置null:value的值乳丰,成功的改變了我們的默認為null的策咯掌测。當然,我們也可以通過Spring EL表達式來完成默認空值的操作产园。

5.1.8 order 元素

如果我們配置了多個context-property-placeholder汞斧,通過配置order可以指定查找的順序。

先簡單看一個例子什燕,先配置下properties文件:

# score.properties
score1=score1
score2=score2
name=score
# student.properties
stu1=stu1
stu2=stu2
name=student

然后看下配置文件:

<context:property-placeholder location="/resource/score.properties" order="1" ignore-unresolvable="true"/>
<context:property-placeholder location="/resource/student.properties" order="2"/>

<bean id="student" class="entity.Student">
    <property name="name" value="${name}"/>
    <property name="id" value="${score2}"/>
    <property name="age" value="${stu1}"/>
    <property name="math" value="${stu2}"/>
</bean>

打印測試:

Student student = ac.getBean("student", Student.class);
System.out.println(student.getName());
System.out.println(student.getId());
System.out.println(student.getAge());
System.out.println(student.getMath());

output:

score
score2
stu1
stu2

看完了這個例子粘勒,我們來了解下該屬性及相關的東西。

  1. 我們可以配置多個context-property-placeholder屎即,默認情況下加載順序是在Spring中的XML的配置順序庙睡,當然我們可以通過配置order元素指定加載的順序,order越小技俐,優(yōu)先級越高埃撵;
  2. 配置多個context-property-placeholder之后,如果對應的properties文件中有相同的屬性虽另,后者并不會覆蓋掉前者暂刘,上面的例子我們也看到了,后加載的student.properties中的name屬性并沒有覆蓋掉score.properties中的name屬性捂刺;也就是說谣拣,對于同名的屬性,只會取第一次加載的值族展,不會覆蓋森缠。
  3. 在按順序加載context-property-placeholder對應的文件的時候,比如先加載score.properties的時候仪缸,此時Spring會掃描整個容器中的bean贵涵,如果發(fā)現(xiàn)有無法解析的占位符,則會拋出異常,所以這種情況下宾茂,需要添加ignore-unresolvable="true"瓷马,來忽略掉無法解析的占位符。這樣解析score.properties所忽略掉的占位符跨晴,在解析student.properties的時候就可以解析成功了欧聘。
  4. 也就是說,只需要將最后一個context-property-placeholder配置ignore-unresolvable="false"端盆,前面其他的都配置為true就可以了怀骤。
  5. 由于context-property-placeholder的全局性,所以最優(yōu)的方式應該是焕妙,我們在Spring的配置入口處applicationContext.xml處配置一個全局的context-property-placeholder蒋伦,包含所有的properties文件,然后再使用import導入各個模塊的配置文件即可焚鹊。

最終應該是如下配置痕届,applicationContext.xml 配置:

/** 加載所有的properties文件,這里就會有屬性的覆蓋情況了 */
<context:property-placeholder location="/resource/score.properties, /resource/student.properties"/>

/** 導入所有的application-*文件 */
<import resource="applicationContext-*.xml"/>

applicationContext-student.xml 中只需要配置相應的bean即可如下:

<bean id="student" class="entity.Student">
    <property name="name" value="${name}"/>
    <property name="id" value="${score2}"/>
    <property name="age" value="${stu1}"/>
    <property name="math" value="${stu2}"/>
</bean>

而相應的 applicationContext-score.xml也是類似寺旺。

5.1.9 system-properties-mode 元素
  1. 配置了location之后爷抓,Spring會去相應的路徑下加載配置文件,默認情況下阻塑,如果無法在指定的屬性文件中找到對應的屬性蓝撇,Spring還會去我們的系統(tǒng)屬性,環(huán)境變量等配置中讀取陈莽,而實現(xiàn)這種行為的就是system-properties-mode屬性渤昌。
  2. 或者另一種說法,比如我們需要給我們的bean賦值系統(tǒng)屬性(System.getProperty ())走搁,或者環(huán)境變量(System.getenv())中的值独柑,那么我們就可以通過該配置來實現(xiàn)我們的功能。

其實私植,就是控制如何解決占位符與系統(tǒng)屬性的關系忌栅。 該屬性配置目前共有4個可選項,但這些可選項加載的順序和另一個配置local-override是相關的曲稼,我們先看下local-override=false的情況下:

  1. ENVIRONMENT索绪,默認配置,表示加載的時候先從System.getProperty贫悄,然后System.getenv瑞驱,如果加載到就返回,加載不到就接著查找properties-ref窄坦,然后再查找locations唤反,如果查找到凳寺,覆蓋掉properties-ref的值。
  2. OVERRIDE彤侍,查找順序是:System.getProperty -> System.getenv -> properties-ref -> locations肠缨,對于系統(tǒng)屬性和環(huán)境變量,查找到就返回拥刻,查找不到再以此查找怜瞒。注意父泳,對于后兩者般哼,會先加載properties-ref,然后再查詢location惠窄,如果查到蒸眠,覆蓋掉前面的值。OVERRIDE元素查找時以系統(tǒng)屬性及環(huán)境變量優(yōu)先杆融;
  3. FALLBACK楞卡,查找的順序是:properties-ref -> location -> System.getProperty -> System.getenv,對于前兩者而言脾歇,同樣是先加載properties-ref蒋腮,再加載location,然后后者覆蓋掉前者藕各,然后查找到直接返回池摧,查找不到以此往下查找。也就是查找的時候以本地屬性優(yōu)先激况;
  4. NEVER作彤,查找順序是:properties-ref -> location,同樣也是乌逐,查找到后者覆蓋掉前者直接返回竭讳,但不查找系統(tǒng)屬性及環(huán)境變量。

其實對于這幾種加載方式浙踢,除了ENVIRONMENT绢慢,其他三種我們看一下源代碼就很清晰了(代碼位于PropertyPlaceholderConfigurer):

protected String resolvePlaceholder(String placeholder, Properties props, int systemPropertiesMode) {
    String propVal = null;
    // 對于OVERRIDE模式下
    if (systemPropertiesMode == SYSTEM_PROPERTIES_MODE_OVERRIDE) {
        propVal = resolveSystemProperty(placeholder);
    }
    if (propVal == null) {
        propVal = resolvePlaceholder(placeholder, props);
    }
    // 對于FALLBACK模式下
    if (propVal == null && systemPropertiesMode == SYSTEM_PROPERTIES_MODE_FALLBACK) {
        propVal = resolveSystemProperty(placeholder);
    }
    return propVal;
}

而針對ENVIRONMENT類型,可能源碼的話就有點不太一樣了洛波。該模式下胰舆,會先加載environmentProperties,其中這里又包括systemPropertiessystemEnvironment奋岁,在環(huán)境變量中查找不到思瘟,才會查找localProperties。其實這種方式和OVERRIDE類似闻伶,該部分的源碼在PropertySourcesPropertyResolver下的getProperty下滨攻。

而如果設置了local-override=true的情況下:

  • NEVER查找,順序:location -> properties-ref,后者會覆蓋掉前者的值光绕;
  • FALLBACK女嘲,順序:location -> properties-ref -> System.getProperty -> System.getenv,同樣诞帐,對前兩者而言欣尼,后者覆蓋前者,查找到直接返回停蕉;
  • OVERRIDE愕鼓,順序就location 和properties-ref反過來即可;
  • ENVIRONMENT慧起,順序是location -> properties-ref -> System.getProperty -> System.getenv菇晃,和FALLBACK類似,前兩者而言蚓挤,查到返回磺送;

針對location與properties-ref的加載順序,直接查看源碼(位于PropertiesLoaderSupport):

protected Properties mergeProperties() throws IOException {
    Properties result = new Properties();
    // 如果local-override是true灿意,也加載location
    if (this.localOverride) {
        // Load properties from file upfront, to let local properties override.
        loadProperties(result);
    }
    // 否則估灿,先加載properties-ref
    if (this.localProperties != null) {
        for (Properties localProp : this.localProperties) {
            CollectionUtils.mergePropertiesIntoMap(localProp, result);
        }
    }

    if (!this.localOverride) {
        // Load properties from file afterwards, to let those properties override.
        loadProperties(result);
    }

    return result;
}

再多說一點,由于ENVIROMENT的流程相對復雜缤剧,并且由于這個類中有許多日志打印馅袁,所以建議學習這塊的時候可以通過日志來查看這個流程。日志jar包是apache.commons.logging鞭执,這個就不多說了司顿。

5.1.10 *value-separator 元素

分隔符,用來分割占位符變量和默認值兄纺,默認分隔符是冒號:大溜,意思是當找不到某個變量的時候使用默認值來替換,比如我們設置下:

value-separator="#"
<bean id="student" class="entity.Student">
  <property name="name" value="${stu#first}"/>
</bean>

我們故意不配置變量stu估脆,然后運行下程序钦奋,程序將會打印出first

5.1.11 trim-values 元素
  1. 顧名思義疙赠,解析屬性的時候付材,自動忽略掉開始和結尾處的空格,特別是制表符圃阳。這個有點用厌衔,比如說我們在properties中配置的數(shù)據(jù)庫連接,如果一不小心多加了個空格捍岳,可能就會連接失敗富寿,我們可以通過這個屬性來避免這個問題睬隶。
  2. 該屬性是布爾類型,支持truefalse兩種類型页徐,true的時候苏潜,表示自動過濾空格,默認情況下是false類型变勇。

比如我們在student.properties中添加:

users=student        // 這里多加了幾個空格`

然后解析的時候恤左,我們不添加該屬性的時候進行測試:

System.out.println(student.getName() + ", size:" + student.getName().length());

打印結果:

student        , size:15

然后添加該屬性后:

student, size:7
5.2 <context:annotation-config/>標簽

??context:annotation-config 這個標簽是用于識別或者說開啟我們的注解的,因為現(xiàn)在的項目已經(jīng)很少有人還在XML中配置各種bean了搀绣,通過使用注解飞袋,我們可以更方便的注冊和管理我們的bean。

而通過配置該屬性豌熄,可以顯示的向Spring容器注入AutowiredAnnotationBeanPostProcessor授嘀、CommonAnnotationBeanPostProcessor物咳、PersistenceAnnotationBeanPostProcessor 以及RequiredAnnotationBeanPostProcessor 這4個BeanPostProcessor锣险。

在傳統(tǒng)方式中,如果我們要使用@Autowired注解览闰,那么需要在配置文件中單獨配置:

<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>

而如果要使用@Resource 芯肤、@PostConstruct、@PreDestroy這些注解压鉴,則需要配置:

<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>

而如果要使用@PersistenceContext注解崖咨,則需要配置:

<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>

而如果要使用@Required注解,則需要配置:

<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>

試想可知油吭,如果要使用上述注解击蹲,那需要配置太多東西了,而為了簡化我們的配置婉宰,Spring給我們提供了context:annotation-config注解來簡化我們的操作歌豺。我們我們要使用以上的注解,則只需要配置:

<context:annotation-config/>

而該屬性支持的注解大概有以下幾個:Autowired心包,Value类咧,JSR-330的InjectResource蟹腾,PostConstruct痕惋,PreDestroy,JAX-WS的WebServiceRef娃殖,Required值戳,JPA的PersistenceContext以及PersistenceUnit等常用的。

不過接下來我們要學習的context:component-scan注解炉爆,是在<context:annotation-config/>基礎之上擴展了更多的功能堕虹,所以目前我們的項目中一般都是使用context:component-scan注解來完成相應的掃描注入工作柿隙,因為該屬性不但能完成context:annotation-config的所有功能,還擴展了許多新的功能鲫凶。

5.3 <context:component-scan>標簽

該元素會自動掃描并注入相應包下所有相關的注解所修飾的對象禀崖。

默認情況下,Spring提供的@Component螟炫,@Repository波附,@Service@Controller昼钻,@RestController掸屡,@ControllerAdvice@Configuration這些注解都將會掃描并自動注入bean然评。并且仅财,該標簽還隱式的包含了context:annotation-config標簽所對應的注解類型,所以一旦使用了該標簽碗淌,通常就不需要再使用context:annotation-config標簽盏求。

5.3.1 base-package 元素

該元素是要掃描并注入的包目錄,可以使用通配符亿眠。如果是多個包碎罚,可以使用逗號,分號纳像,空格荆烈,制表符,換行符來進行分割竟趾。

5.3.2 use-default-filters 元素

是否要采用默認的過濾器憔购,有 true 和 false 兩個選項,默認是 true岔帽。因為默認情況下會掃描@Component玫鸟。@Repository@Service山卦,@Controller所修飾的類鞋邑;配置為false的情況下,我們可以指定要掃描的注解類型账蓉。

5.3.3 annotation-config 元素

nnotation-config枚碗,是否掃描隱式的context:annotation-config所對應的注解類型,同樣有 true 和 false 兩個選項铸本,默認是 true肮雨,也就是默認情況下會掃描autowire這些注解。

5.3.4 name-generator 元素

Spring bean的命名策咯箱玷,在Spring中bean的id是唯一的怨规,而如果不注意的話陌宿,比如說項目很大, 多人開發(fā)的時候有可能會出現(xiàn)同名的bean波丰。這種情況下壳坪,我們可以修改bean的命名策咯钉稍,將bean的名稱改為類的全路徑即可借嗽。

  • name-generator默認的命名策咯是AnnotationBeanNameGenerator硬猫,它繼承了BeanNameGenerator接口蒿辙,實現(xiàn)了generateBeanName接口,該命名策咯的命名是類名送矩,并且第一個字母小寫登颓,當然我們也可以自定義我們的bean名稱名段。
  • 另一個命名策咯是DefaultBeanNameGenerator先馆,它是為了防止bean的id重復发框,它的生成策咯是:類名稱+分隔符+序號。比如一個類的路徑是object.Test1煤墙,那么生成的id是object.Test1#0梅惯。不過該命名策咯不支持自定義id,否則會拋出異常番捂,這里我們可以注意下个唧。
  • 當然,我們也可以重新定義一個類來繼承基礎的BeanNameGenerator设预,然后重寫它的generateBeanName方法,這里我們直接選擇繼承AnnotationBeanNameGenerator犁河,然后覆蓋掉它的處理默認命名的 buildDefaultBeanName 方法鳖枕;

AnnotationBeanNameGenerator中處理命名策咯的方法:

// 處理默認命名的方法
protected String buildDefaultBeanName(BeanDefinition definition) {
    String shortClassName = ClassUtils.getShortName(definition.getBeanClassName());
    return Introspector.decapitalize(shortClassName);
}

DefaultBeanNameGenerator中處理命名策咯的方法:

public static String generateBeanName(
        BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)
        throws BeanDefinitionStoreException {

    String generatedBeanName = definition.getBeanClassName();
    if (generatedBeanName == null) {
        if (definition.getParentName() != null) {
            generatedBeanName = definition.getParentName() + "$child";
        }
        else if (definition.getFactoryBeanName() != null) {
            generatedBeanName = definition.getFactoryBeanName() + "$created";
        }
    }
    if (!StringUtils.hasText(generatedBeanName)) {
        throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " +
                "'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
    }

    String id = generatedBeanName;
    if (isInnerBean) {
        // Inner bean: generate identity hashcode suffix.
        id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
    }
    else {
        // Top-level bean: use plain class name.
        // Increase counter until the id is unique.
        int counter = -1;
        while (counter == -1 || registry.containsBeanDefinition(id)) {
            counter++;
            id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + counter;
        }
    }
    return id;
}

然后我們來看下我們的類:

public class MyAnnotationBeanNameGenerator extends AnnotationBeanNameGenerator{
    protected String buildDefaultBeanName(BeanDefinition definition) {
        return definition.getBeanClassName();
    }
}

對應的配置:

<context:component-scan base-package="entity,object" name-generator="expend.MyAnnotationBeanNameGenerator"/>

測試:

// 通過類的全路徑作為bean的名稱
Test3 test3 = ac.getBean("object.Test3", Test3.class);
5.3.5 scope-resolver 元素

??用于處理Spring的bean的scope,也就是作用域桨螺。默認情況下宾符,Spring管理的作用域都是singleton模式的,當然如果需要灭翔,我們可以通過@Scope注解來為每個bean指定具體的scope魏烫。而在掃描的時候如果不想通過注解的方式來指定作用域的話,那么就可以考慮對自動注冊的bean自定義作用域肝箱。

<context:component-scan base-package="entity,object" scope-resolver="expend.MyScopeResolver">
5.3.6 scoped-proxy 元素

??scope代理链嘀,有三個選項值:no(默認值)萌狂,interfaces(接口代理),targetClass(類代理)怀泊,該屬性一般用于非單例的情況下茫藏。比如當你把一個session或者request的Bean注入到singleton的Bean中時,因為singleton的Bean在容器啟動時就會創(chuàng)建A霹琼,而session的Bean在用戶訪問時才會創(chuàng)建B刷允,那么當A中要注入B時,有可能B還未創(chuàng)建碧囊,這個時候就會出問題树灶,那么這時候就需要使用代理了。B如果是個接口糯而,就用interfaces代理天通,是個類則用targetClass代理。更多scope-resolverscoped-proxy相關可查看官網(wǎng)文檔:beans-factory-scopes-other-injection
beans-scanning-scope-resolver

5.3.7 resource-pattern 元素

??過濾我們要掃描的包下的類文件熄驼,默認是 */*.class像寒,掃描所有的類,不過該屬性的功能有限瓜贾,更多的時候推薦使用子標簽 include-filterexclude-filter來更細粒度的過濾類文件诺祸。

<context:component-scan base-package="object" resource-pattern="/zhang/*.class"/>

這樣的話,將只掃描object.zhang包下的所有類文件祭芦。

5.3.8 context:include-filter 和 context:exclude-filter元素

context:component-scan目錄下的這兩個子標簽筷笨,用于更細粒度的在掃描的時候過濾和排除相關的類。一個 context:component-scan 下可以有許多個子標簽龟劲,每個子標簽有兩個參數(shù)可選:過濾器的類型 type 和 type對應的值 expression胃夏,我們通過這兩個參數(shù)來定義一組掃描策咯。

  • context:include-filter昌跌, 表示查找的是包含的目標類仰禀;
  • context:exclude-filter, 表示查找的時候要排除在外的目標類蚕愤;答恶、

其中type屬性(我們針對include-filter來學習):

  1. annotation,注解類型萍诱,過濾器掃描使用指定注解所標注的那些類悬嗓,通過expression屬性指定要掃描的注解;
  2. assignable砂沛,這個是掃描指定接口的實現(xiàn)烫扼,或者指定類的繼承類;
  3. aspectj碍庵,掃描與指定的AspectJ表達式所匹配的那些類映企;
  4. regex悟狱,掃描指定的正則表達式所匹配的類;
  5. custom堰氓,使用我們自定義的過濾器挤渐,該類實現(xiàn)了org.springframework.core.type.filter.TypeFilter接口。
<!-- 一般情況下双絮,Spring MVC的配置文件中經(jīng)常這么配置浴麻,只掃描對應的Controller,其他的交給Spring來配置:-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<!-- 比如說我們要自動注冊所有實現(xiàn)`Instrument`接口的類 -->
<context:exclude-filter type="assignable" expression="com.springinaction.springidol.Instrument"/>

<context:exclude-filter type="aspectj" expression="org.example..*Service+"/>
<context:exclude-filter type="regex" expression="org\.example\.Default.*"/>

不過需要注意的是囤攀,使用 include-filter 去篩選要掃描的目標類软免,要先配置use-default-filters="false",要不然會采用默認的過濾器覆蓋掉我們所定義的焚挠;并且膏萧,exclude-filter是針對include-filter里的內(nèi)容進行排除的。

5.3.9 context:component-scan 與 context:annotation-config 區(qū)別

到這里蝌衔,context:component-scan標簽學習完了榛泛,我們再來總結下它與context:annotation-config標簽不同的地方:

  1. <context:annotation-config> 是用于激活那些已經(jīng)在spring容器里注冊過的bean上的注解(無論是通過xml的方式還是通過package sanning的方式)。
  2. <context:component-scan>除了具有<context:annotation-config>的功能之外噩斟,還可以在指定的package下掃描以及注冊javabean曹锨;
  3. 具體的不同可參考StackOverFlow,上面說的已經(jīng)是非詳細了:Difference between <context:annotation-config> vs <context:component-scan>
    剃允,如果英文不太好沛简,也可以參考別人翻譯的:<context:annotation-config> 與 <context:component-scan>的區(qū)別
5.4 <context:load-time-weaver> 標簽

該標簽用于開啟Spring的LTW(Load Time Weaver)功能,LTW硅急,就是所謂的類加載期的切面織入覆享,是ApsectJ切面織入的一種方式,它通過JVM代理在類加載期替換字節(jié)碼達到織入切面的目的营袜。

  1. 在Java語言中,織入切面的實現(xiàn)方式大概有如下三種:編譯期織入丑罪,類加載期織入荚板,運行期織入。編譯期織入是指在Java編譯期吩屹,采用特殊的編譯器跪另,將切面織入到Java類中;而類加載期織入則指通過特殊的類加載器煤搜,在類字節(jié)碼加載到JVM時免绿,織入切面;運行期織入則是采用CGLib工具或JDK動態(tài)代理進行切面的織入擦盾。
  2. AspectJ 提供了兩種織入切面方式嘲驾,一種是編譯期織入淌哟,而另一種就是類加載期織入,也就是我們所說的LTW辽故;

該屬性有兩個參數(shù):

  • aspectj-weaving徒仓,有三個可選項,on誊垢,off掉弛,autodetect,設置為on的時候就是開啟LoadTimeWeaver喂走;如果設置為autodetect殃饿,則Spring將會在classpath中查找AspectJ所需要的META-INF/aop.xml文件,查找到則開啟芋肠,這部分源碼在:LoadTimeWeaverBeanDefinitionParser#isAspectJWeavingEnabled中乎芳;
  • weaver-class 就是我們的切面類地址;

有關LTW的更多內(nèi)容可參考:
Spring-framework-reference-aop.html#aop-aj-ltw
Spring AspectJ LTW
Spring之LoadTimeWeaver——一個需求引發(fā)的思考

5.5 <context:mbean-server><context:mbean-export>標簽

這兩個標簽都是用于Spring與JMX的集成所用业栅。這里大概說下JMX。

JMX(Java Management Extensions),Java管理擴展,是Java提供的一種用于管理和監(jiān)控程序的規(guī)范姆坚。比如說我們線上運行的環(huán)境兔辅,我們想監(jiān)控下每天訪問量懂昂,UV沸柔,PV是多少,又或者說在業(yè)務高峰的期間,想修改線上的配置進行限流,而這種就可以通過JMX來實現(xiàn)寺枉,也就是說可以讓開發(fā)者和管理者可以獲取程序運行的狀態(tài)以及動態(tài)的修改程序的相關配置。比如說我們平時使用的jvisualvm,JBoss,Druid等都是實現(xiàn)了JMX規(guī)范。

更多有關JMX的可參考官方文檔:spring-framework-reference-jmx.html
https://www.zhihu.com/question/36688387

通過 <context:mbean-export> 可以將我們的bean導出到JMX,該標簽有如下幾個參數(shù):

  1. registration啤握,將一個MBean注冊到MBeanServer時授段,我們可以控制它的行為缘薛。針對已經(jīng)有一個MBean注冊到同一個ObjectName下時,該屬性有3個可選值:failOnExisting,ignoreExisting, replaceExisting敦间;
  2. default-domain昔汉,TODO:等學習到的時候再來補充靶病;
  3. server,TODO:等學習到的時候再來補充口予;

<context:mbean-server>標簽娄周,有兩個參數(shù):

  1. server,TODO:等學習到的時候再來補充沪停;
  2. agent-id煤辨,TODO:等學習到的時候再來補充裳涛;
5.6 <context:spring-configured/>標簽

一般情況下,我們可以將依賴注入到Spring的bean中众辨,但也可以將依賴注入到Spring未實例化的對象中端三。比如說,某一個類并不是又Spring進行實例化鹃彻,但其中引用了Spring實例化的對象郊闯。一般情況下我們使用@Configurable注解標記不用于Spring實例化的對象,不過這種情況一般都是用于AspectJ的使用場景中蛛株。

@Configurable
public class Test4 {
    @Autowired
    private Test1 test1;
    
    get,set方法略
}

然后在Spring的配置文件中配置:

<context:spring-configured/>

然后团赁,調(diào)用:

Test4 test4 = new Test4();
System.out.println(test4.getTest1());

不過這里可能需要其他的jar包,這里需要注意下泳挥。

5.7 <context:property-override>標簽

該標簽和context:property-placeholder標簽有點像然痊,都是用于讀取外部配置properties中的參數(shù)的值,但功能還有些不太一樣屉符,context:property-override是為XML中的bean的屬性指定最終的結果剧浸,這其中會覆蓋掉先前context:property-placeholder讀取的值,并且property-override標簽所加載的properties文件中的key有固定的格式:bean的名稱.bean的屬性矗钟,也就是 beanName.property=value唆香;

我們簡單來看一個例子:

public class Student {
    private String id;
    private String name;
}
<bean id="student" class="entity.Student">
    <property name="name" value="${name}"/>
    <property name="id" value="${id}"/>
</bean>

兩個properties文件的值分別為:

# student.properties
id=id
name=name     
# score.properties
# 指定bean的名稱是student,bean的屬性是name
student.name=score
<context:property-placeholder location="resource/student.properties" ignore-unresolvable="true"/>
<context:property-override location="/resource/score.properties"/>

測試:

Student student = ac.getBean("student", Student.class);
System.out.println(student.getName());
System.out.println(student.getId());

最終打印結果:

score
id

可以看到吨艇,最終property-override所加載的properties的值覆蓋掉了 property-placeholder 加載的值躬它。

5.8 總結

到這里,context命名空間下所有的配置項我們基本都學習過了东涡,不過常用的還是我們最上面說的這幾項冯吓。下面這幾項等我們用到的時候再來學習也可以。


本文參考自:
《Spring in Action》
官方文檔:https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring-framework-reference/html/beans.html#beans-scanning-filters

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疮跑,一起剝皮案震驚了整個濱河市组贺,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌祖娘,老刑警劉巖失尖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異渐苏,居然都是意外死亡掀潮,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門琼富,熙熙樓的掌柜王于貴愁眉苦臉地迎上來仪吧,“玉大人,你說我怎么就攤上這事鞠眉∫厣蹋” “怎么了摄咆?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長人断。 經(jīng)常有香客問我,道長朝蜘,這世上最難降的妖魔是什么恶迈? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮谱醇,結果婚禮上暇仲,老公的妹妹穿的比我還像新娘。我一直安慰自己副渴,他們只是感情好奈附,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著煮剧,像睡著了一般斥滤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上勉盅,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天佑颇,我揣著相機與錄音,去河邊找鬼草娜。 笑死挑胸,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的宰闰。 我是一名探鬼主播茬贵,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼移袍!你這毒婦竟也來了解藻?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤咐容,失蹤者是張志新(化名)和其女友劉穎舆逃,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體戳粒,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡路狮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蔚约。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奄妨。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖苹祟,靈堂內(nèi)的尸體忽然破棺而出砸抛,到底是詐尸還是另有隱情评雌,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布直焙,位于F島的核電站景东,受9級特大地震影響,放射性物質發(fā)生泄漏奔誓。R本人自食惡果不足惜斤吐,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望厨喂。 院中可真熱鬧和措,春花似錦、人聲如沸蜕煌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽斜纪。三九已至贫母,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間傀广,已是汗流浹背颁独。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留伪冰,地道東北人誓酒。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像贮聂,于是被迫代替她去往敵國和親靠柑。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 文/無量水2017.6.23 愛你不是兩三天吓懈,愛你已經(jīng)很多年歼冰。 記得還是上小學時候,一個寒假剛剛結束耻警,新學...
    諸葛恩德閱讀 372評論 0 1
  • 昨天手機叮咚作響,收到一些人的平安夜祝福短信温兼,被淹沒在會計分錄中的我秸滴,才恍然想起來,圣誕節(jié)要到了募判。 走出房間荡含,跟媽...
    牛肉蘑菇醬閱讀 435評論 0 1
  • [TOC] 內(nèi)容 時間:2016/08/10 耗時30分鐘 截圖 總結
    上山老人閱讀 166評論 0 0
  • 廣州終于變冷啦 不過天氣預報說廣州將再一次入冬失敗 南國嘛 emm還是有點喪 不過會慢慢變好吧 我現(xiàn)在的想法和對待...
    野榆木閱讀 207評論 0 0