紧接着上文,我们来学习其他标签。
Spring 版本:4.3.14。
5. context
命名空间
看完了beans
命名空间下的标签,接下来,我们来看下context
命名空间下各种标签的使用。
第一步,当然是引入context
命名空间,引入方式上文已经讲过,先引入命名空间:
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/>
标签提供了一种优雅的读取参数配置的方式。比如说在开发的过程中,我们通常需要相应的properties文件,来配置数据库相关地址,MQ相关地址,分布式环境地址端口等相关参数,而这些参数在不同的环境又是不同的,而通过context:property-placeholder
则可以在程序中动态的读取配置参数。
<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;
下面我们来简单看下该参数的各项元素:
5.1.1 location元素
- location元素,要读取资源的resource,同样可选的配置也有好多种,相对路径,绝对路径,
classpath:
,classpath*:
,file:
,http:
等,如果要读取多个文件,可以使用逗号进行分割;- 注意:如果配置了多个文件,而文件中有相同的属性的话,那么后一个文件中定义的属性将会覆盖掉前面文件中定义的属性,也就是说最后文件中的属性优先级更高。
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文件中解析相应的配置参数,当然也可以通过其他方式进行解析,然后我们通过
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。这时候如果我们去掉该配置项的话,再打印下:
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越小,优先级越高;- 配置多个
context-property-placeholder
之后,如果对应的properties文件中有相同的属性,后者并不会覆盖掉前者,上面的例子我们也看到了,后加载的student.properties中的name属性并没有覆盖掉score.properties中的name属性;也就是说,对于同名的属性,只会取第一次加载的值,不会覆盖。- 在按顺序加载
context-property-placeholder
对应的文件的时候,比如先加载score.properties的时候,此时Spring会扫描整个容器中的bean,如果发现有无法解析的占位符,则会抛出异常,所以这种情况下,需要添加ignore-unresolvable="true"
,来忽略掉无法解析的占位符。这样解析score.properties
所忽略掉的占位符,在解析student.properties
的时候就可以解析成功了。- 也就是说,只需要将最后一个
context-property-placeholder
配置ignore-unresolvable="false"
,前面其他的都配置为true就可以了。- 由于
context-property-placeholder
的全局性,所以最优的方式应该是,我们在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还会去我们的系统属性,环境变量等配置中读取,而实现这种行为的就是system-properties-mode
属性。- 或者另一种说法,比如我们需要给我们的bean赋值系统属性(
System.getProperty ()
),或者环境变量(System.getenv()
)中的值,那么我们就可以通过该配置来实现我们的功能。
其实,就是控制如何解决占位符与系统属性的关系。 该属性配置目前共有4个可选项,但这些可选项加载的顺序和另一个配置local-override
是相关的,我们先看下local-override=false
的情况下:
ENVIRONMENT
,默认配置,表示加载的时候先从System.getProperty
,然后System.getenv
,如果加载到就返回,加载不到就接着查找properties-ref
,然后再查找locations
,如果查找到,覆盖掉properties-ref
的值。OVERRIDE
,查找顺序是:System.getProperty -> System.getenv -> properties-ref -> locations
,对于系统属性和环境变量,查找到就返回,查找不到再以此查找。注意,对于后两者,会先加载properties-ref
,然后再查询location
,如果查到,覆盖掉前面的值。OVERRIDE
元素查找时以系统属性及环境变量优先;FALLBACK
,查找的顺序是:properties-ref -> location -> System.getProperty -> System.getenv
,对于前两者而言,同样是先加载properties-ref
,再加载location
,然后后者覆盖掉前者,然后查找到直接返回,查找不到以此往下查找。也就是查找的时候以本地属性优先;NEVER
,查找顺序是:properties-ref -> location
,同样也是,查找到后者覆盖掉前者直接返回,但不查找系统属性及环境变量。
其实对于这几种加载方式,除了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
,在环境变量中查找不到,才会查找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中配置的数据库连接,如果一不小心多加了个空格,可能就会连接失败,我们可以通过这个属性来避免这个问题。
- 该属性是布尔类型,支持
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 这个标签是用于识别或者说开启我们的注解的,因为现在的项目已经很少有人还在XML中配置各种bean了,通过使用注解,我们可以更方便的注册和管理我们的bean。
而通过配置该属性,可以显示的向Spring容器注入AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor 以及RequiredAnnotationBeanPostProcessor 这4个BeanPostProcessor。
在传统方式中,如果我们要使用@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是唯一的,而如果不注意的话,比如说项目很大, 多人开发的时候有可能会出现同名的bean。这种情况下,我们可以修改bean的命名策咯,将bean的名称改为类的全路径即可。
-
name-generator
默认的命名策咯是AnnotationBeanNameGenerator
,它继承了BeanNameGenerator
接口,实现了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
。- 如果自定义的话,我们需要实现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在容器启动时就会创建A,而session的Bean在用户访问时才会创建B,那么当A中要注入B时,有可能B还未创建,这个时候就会出问题,那么这时候就需要使用代理了。B如果是个接口,就用interfaces代理,是个类则用targetClass代理。更多scope-resolver
与scoped-proxy
相关可查看官网文档: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 下可以有许多个子标签,每个子标签有两个参数可选:过滤器的类型type
和 type对应的值expression
,我们通过这两个参数来定义一组扫描策咯。
- context:include-filter, 表示查找的是包含的目标类;
- context:exclude-filter, 表示查找的时候要排除在外的目标类;、
其中type属性(我们针对include-filter来学习):
- annotation,注解类型,过滤器扫描使用指定注解所标注的那些类,通过expression属性指定要扫描的注解;
- assignable,这个是扫描指定接口的实现,或者指定类的继承类;
- aspectj,扫描与指定的
AspectJ
表达式所匹配的那些类;- regex,扫描指定的正则表达式所匹配的类;
- custom,使用我们自定义的过滤器,该类实现了
org.springframework.core.type.filter.TypeFilter
接口。
<!-- 一般情况下,Spring MVC的配置文件中经常这么配置,只扫描对应的Controller,其他的交给Spring来配置:-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<!-- 比如说我们要自动注册所有实现`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里的内容进行排除的。
5.3.9 context:component-scan 与 context:annotation-config 区别
到这里,context:component-scan
标签学习完了,我们再来总结下它与context:annotation-config
标签不同的地方:
- <context:annotation-config> 是用于激活那些已经在spring容器里注册过的bean上的注解(无论是通过xml的方式还是通过package sanning的方式)。
- <context:component-scan>除了具有<context:annotation-config>的功能之外,还可以在指定的package下扫描以及注册javabean;
- 具体的不同可参考StackOverFlow,上面说的已经是非详细了:Difference between <context:annotation-config> vs <context:component-scan>
,如果英文不太好,也可以参考别人翻译的:<context:annotation-config> 与 <context:component-scan>的区别
5.4 <context:load-time-weaver>
标签
该标签用于开启Spring的LTW(Load Time Weaver)功能,LTW,就是所谓的类加载期的切面织入,是ApsectJ切面织入的一种方式,它通过JVM代理在类加载期替换字节码达到织入切面的目的。
- 在Java语言中,织入切面的实现方式大概有如下三种:编译期织入,类加载期织入,运行期织入。编译期织入是指在Java编译期,采用特殊的编译器,将切面织入到Java类中;而类加载期织入则指通过特殊的类加载器,在类字节码加载到JVM时,织入切面;运行期织入则是采用CGLib工具或JDK动态代理进行切面的织入。
- AspectJ 提供了两种织入切面方式,一种是编译期织入,而另一种就是类加载期织入,也就是我们所说的LTW;
该属性有两个参数:
-
aspectj-weaving,有三个可选项,
on
,off
,autodetect
,设置为on的时候就是开启LoadTimeWeaver;如果设置为autodetect
,则Spring将会在classpath中查找AspectJ所需要的META-INF/aop.xml
文件,查找到则开启,这部分源码在:LoadTimeWeaverBeanDefinitionParser#isAspectJWeavingEnabled中; - weaver-class 就是我们的切面类地址;
有关LTW的更多内容可参考:
Spring-framework-reference-aop.html#aop-aj-ltw
Spring AspectJ LTW
Spring之LoadTimeWeaver——一个需求引发的思考
5.5 <context:mbean-server>
和<context:mbean-export>
标签
这两个标签都是用于Spring与JMX的集成所用。这里大概说下JMX。
JMX(Java Management Extensions),Java管理扩展,是Java提供的一种用于管理和监控程序的规范。比如说我们线上运行的环境,我们想监控下每天访问量,UV,PV是多少,又或者说在业务高峰的期间,想修改线上的配置进行限流,而这种就可以通过JMX来实现,也就是说可以让开发者和管理者可以获取程序运行的状态以及动态的修改程序的相关配置。比如说我们平时使用的jvisualvm,JBoss,Druid等都是实现了JMX规范。
更多有关JMX的可参考官方文档:spring-framework-reference-jmx.html
https://www.zhihu.com/question/36688387
通过 <context:mbean-export> 可以将我们的bean导出到JMX,该标签有如下几个参数:
- registration,将一个MBean注册到MBeanServer时,我们可以控制它的行为。针对已经有一个MBean注册到同一个ObjectName下时,该属性有3个可选值:failOnExisting,ignoreExisting, replaceExisting;
- default-domain,TODO:等学习到的时候再来补充;
- server,TODO:等学习到的时候再来补充;
<context:mbean-server>标签,有两个参数:
- 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/>
然后,调用:
Test4 test4 = new Test4();
System.out.println(test4.getTest1());
不过这里可能需要其他的jar包,这里需要注意下。
5.7 <context:property-override>标签
该标签和context:property-placeholder
标签有点像,都是用于读取外部配置properties中的参数的值,但功能还有些不太一样,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