緊接著上文淮野,我們來學習其他標簽捧书。
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元素
- location元素抑片,要讀取資源的resource,同樣可選的配置也有好多種杨赤,相對路徑敞斋,絕對路徑,
classpath:
疾牲,classpath*:
渺尘,file:
,http:
等说敏,如果要讀取多個文件鸥跟,可以使用逗號進行分割;- 注意:如果配置了多個文件盔沫,而文件中有相同的屬性的話医咨,那么后一個文件中定義的屬性將會覆蓋掉前面文件中定義的屬性,也就是說最后文件中的屬性優(yōu)先級更高架诞。
5.1.2 file-encoding元素
文件編碼拟淮,通常情況下我們一般不需要配置;
5.1.3 ignore-resource-not-found元素
是否忽略對應的屬性文件找不到的問題谴忧。該元素有true
和false
兩個選項很泊,默認情況下是false。true的時候表示忽略沾谓,false的時候表示不忽略委造,如果配置為false,并且在指定的位置沒有找到文件均驶,那么在運行的時候將會拋出一個異常昏兆。
5.1.4 ignore-unresolvable元素
是否忽略解析不了或替換不了的屬性。同樣該元素也是有true
和false
兩個選項妇穴,默認情況下是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
本地是否覆蓋模式配置耕皮,元素有
true
和false
可選,默認情況下是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
看完了這個例子粘勒,我們來了解下該屬性及相關的東西。
- 我們可以配置多個
context-property-placeholder
屎即,默認情況下加載順序是在Spring中的XML的配置順序庙睡,當然我們可以通過配置order
元素指定加載的順序,order越小技俐,優(yōu)先級越高埃撵;- 配置多個
context-property-placeholder
之后,如果對應的properties文件中有相同的屬性虽另,后者并不會覆蓋掉前者暂刘,上面的例子我們也看到了,后加載的student.properties中的name屬性并沒有覆蓋掉score.properties中的name屬性捂刺;也就是說谣拣,對于同名的屬性,只會取第一次加載的值族展,不會覆蓋森缠。- 在按順序加載
context-property-placeholder
對應的文件的時候,比如先加載score.properties的時候仪缸,此時Spring會掃描整個容器中的bean贵涵,如果發(fā)現(xiàn)有無法解析的占位符,則會拋出異常,所以這種情況下宾茂,需要添加ignore-unresolvable="true"
瓷马,來忽略掉無法解析的占位符。這樣解析score.properties
所忽略掉的占位符跨晴,在解析student.properties
的時候就可以解析成功了欧聘。- 也就是說,只需要將最后一個
context-property-placeholder
配置ignore-unresolvable="false"
端盆,前面其他的都配置為true就可以了怀骤。- 由于
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 元素
- 配置了
location
之后爷抓,Spring會去相應的路徑下加載配置文件,默認情況下阻塑,如果無法在指定的屬性文件中找到對應的屬性蓝撇,Spring還會去我們的系統(tǒng)屬性,環(huán)境變量等配置中讀取陈莽,而實現(xiàn)這種行為的就是system-properties-mode
屬性渤昌。- 或者另一種說法,比如我們需要給我們的bean賦值系統(tǒng)屬性(
System.getProperty ()
)走搁,或者環(huán)境變量(System.getenv()
)中的值独柑,那么我們就可以通過該配置來實現(xiàn)我們的功能。
其實私植,就是控制如何解決占位符與系統(tǒng)屬性的關系忌栅。 該屬性配置目前共有4個可選項,但這些可選項加載的順序和另一個配置local-override
是相關的曲稼,我們先看下local-override=false
的情況下:
ENVIRONMENT
索绪,默認配置,表示加載的時候先從System.getProperty
贫悄,然后System.getenv
瑞驱,如果加載到就返回,加載不到就接著查找properties-ref
窄坦,然后再查找locations
唤反,如果查找到凳寺,覆蓋掉properties-ref
的值。OVERRIDE
彤侍,查找順序是:System.getProperty -> System.getenv -> properties-ref -> locations
肠缨,對于系統(tǒng)屬性和環(huán)境變量,查找到就返回拥刻,查找不到再以此查找怜瞒。注意父泳,對于后兩者般哼,會先加載properties-ref
,然后再查詢location
惠窄,如果查到蒸眠,覆蓋掉前面的值。OVERRIDE
元素查找時以系統(tǒng)屬性及環(huán)境變量優(yōu)先杆融;FALLBACK
楞卡,查找的順序是:properties-ref -> location -> System.getProperty -> System.getenv
,對于前兩者而言脾歇,同樣是先加載properties-ref
蒋腮,再加載location
,然后后者覆蓋掉前者藕各,然后查找到直接返回池摧,查找不到以此往下查找。也就是查找的時候以本地屬性優(yōu)先激况;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
,其中這里又包括systemProperties
和systemEnvironment
奋岁,在環(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 元素
- 顧名思義疙赠,解析屬性的時候付材,自動忽略掉開始和結尾處的空格,特別是制表符圃阳。這個有點用厌衔,比如說我們在properties中配置的數(shù)據(jù)庫連接,如果一不小心多加了個空格捍岳,可能就會連接失敗富寿,我們可以通過這個屬性來避免這個問題睬隶。
- 該屬性是布爾類型,支持
true
和false
兩種類型页徐,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的Inject
,Resource
蟹腾,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自定義作用域肝箱。
- 默認情況下的作用域解析類是:
AnnotationScopeMetadataResolver
哄褒,另外Spring還提供了一個解析類:Jsr330ScopeMetadataResolver
。- 如果自定義的話煌张,我們需要實現(xiàn)ScopeMetadataResolver接口呐赡,并且要提供一個默認的無參構造方法,然后使用全路徑進行配置下就可以了骏融。我們可以參考StackOverflow上一個問題:https://stackoverflow.com/questions/5380235/is-it-possible-to-replace-spring-scoperequest-with-jsr-330-scope-variant
<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-resolver
與scoped-proxy
相關可查看官網(wǎng)文檔:beans-factory-scopes-other-injection
beans-scanning-scope-resolver
5.3.7 resource-pattern 元素
??過濾我們要掃描的包下的類文件熄驼,默認是 */*.class
像寒,掃描所有的類,不過該屬性的功能有限瓜贾,更多的時候推薦使用子標簽 include-filter
和 exclude-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來學習):
- annotation,注解類型萍诱,過濾器掃描使用指定注解所標注的那些類悬嗓,通過expression屬性指定要掃描的注解;
- assignable砂沛,這個是掃描指定接口的實現(xiàn)烫扼,或者指定類的繼承類;
- aspectj碍庵,掃描與指定的
AspectJ
表達式所匹配的那些類映企;- regex悟狱,掃描指定的正則表達式所匹配的類;
- 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
標簽不同的地方:
- <context:annotation-config> 是用于激活那些已經(jīng)在spring容器里注冊過的bean上的注解(無論是通過xml的方式還是通過package sanning的方式)。
- <context:component-scan>除了具有<context:annotation-config>的功能之外噩斟,還可以在指定的package下掃描以及注冊javabean曹锨;
- 具體的不同可參考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é)碼達到織入切面的目的营袜。
- 在Java語言中,織入切面的實現(xiàn)方式大概有如下三種:編譯期織入丑罪,類加載期織入荚板,運行期織入。編譯期織入是指在Java編譯期吩屹,采用特殊的編譯器跪另,將切面織入到Java類中;而類加載期織入則指通過特殊的類加載器煤搜,在類字節(jié)碼加載到JVM時免绿,織入切面;運行期織入則是采用CGLib工具或JDK動態(tài)代理進行切面的織入擦盾。
- 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ù):
- registration啤握,將一個MBean注冊到MBeanServer時授段,我們可以控制它的行為缘薛。針對已經(jīng)有一個MBean注冊到同一個ObjectName下時,該屬性有3個可選值:failOnExisting,ignoreExisting, replaceExisting敦间;
- default-domain昔汉,TODO:等學習到的時候再來補充靶病;
- server,TODO:等學習到的時候再來補充口予;
<context:mbean-server>標簽娄周,有兩個參數(shù):
- server,TODO:等學習到的時候再來補充沪停;
- 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