[spring]applicationContext.xml-context标签

紧接着上文,我们来学习其他标签。

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元素
  1. location元素,要读取资源的resource,同样可选的配置也有好多种,相对路径,绝对路径,classpath:classpath*:file:http:等,如果要读取多个文件,可以使用逗号进行分割;
  2. 注意:如果配置了多个文件,而文件中有相同的属性的话,那么后一个文件中定义的属性将会覆盖掉前面文件中定义的属性,也就是说最后文件中的属性优先级更高。
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文件中解析相应的配置参数,当然也可以通过其他方式进行解析,然后我们通过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。这时候如果我们去掉该配置项的话,再打印下:


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越小,优先级越高;
  2. 配置多个context-property-placeholder之后,如果对应的properties文件中有相同的属性,后者并不会覆盖掉前者,上面的例子我们也看到了,后加载的student.properties中的name属性并没有覆盖掉score.properties中的name属性;也就是说,对于同名的属性,只会取第一次加载的值,不会覆盖。
  3. 在按顺序加载context-property-placeholder对应的文件的时候,比如先加载score.properties的时候,此时Spring会扫描整个容器中的bean,如果发现有无法解析的占位符,则会抛出异常,所以这种情况下,需要添加ignore-unresolvable="true",来忽略掉无法解析的占位符。这样解析score.properties所忽略掉的占位符,在解析student.properties的时候就可以解析成功了。
  4. 也就是说,只需要将最后一个context-property-placeholder配置ignore-unresolvable="false",前面其他的都配置为true就可以了。
  5. 由于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 元素
  1. 配置了location之后,Spring会去相应的路径下加载配置文件,默认情况下,如果无法在指定的属性文件中找到对应的属性,Spring还会去我们的系统属性,环境变量等配置中读取,而实现这种行为的就是system-properties-mode属性。
  2. 或者另一种说法,比如我们需要给我们的bean赋值系统属性(System.getProperty ()),或者环境变量(System.getenv())中的值,那么我们就可以通过该配置来实现我们的功能。

其实,就是控制如何解决占位符与系统属性的关系。 该属性配置目前共有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,对于系统属性和环境变量,查找到就返回,查找不到再以此查找。注意,对于后两者,会先加载properties-ref,然后再查询location,如果查到,覆盖掉前面的值。OVERRIDE元素查找时以系统属性及环境变量优先;
  3. FALLBACK,查找的顺序是:properties-ref -> location -> System.getProperty -> System.getenv,对于前两者而言,同样是先加载properties-ref,再加载location,然后后者覆盖掉前者,然后查找到直接返回,查找不到以此往下查找。也就是查找的时候以本地属性优先;
  4. 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,其中这里又包括systemPropertiessystemEnvironment,在环境变量中查找不到,才会查找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中配置的数据库连接,如果一不小心多加了个空格,可能就会连接失败,我们可以通过这个属性来避免这个问题。
  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 这个标签是用于识别或者说开启我们的注解的,因为现在的项目已经很少有人还在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/>

而该属性支持的注解大概有以下几个:AutowiredValue,JSR-330的InjectResourcePostConstructPreDestroy,JAX-WS的WebServiceRefRequired,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自定义作用域。

<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-resolverscoped-proxy相关可查看官网文档: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 下可以有许多个子标签,每个子标签有两个参数可选:过滤器的类型 type 和 type对应的值 expression,我们通过这两个参数来定义一组扫描策咯。

  • context:include-filter, 表示查找的是包含的目标类;
  • context:exclude-filter, 表示查找的时候要排除在外的目标类;、

其中type属性(我们针对include-filter来学习):

  1. annotation,注解类型,过滤器扫描使用指定注解所标注的那些类,通过expression属性指定要扫描的注解;
  2. assignable,这个是扫描指定接口的实现,或者指定类的继承类;
  3. aspectj,扫描与指定的AspectJ表达式所匹配的那些类;
  4. regex,扫描指定的正则表达式所匹配的类;
  5. 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标签不同的地方:

  1. <context:annotation-config> 是用于激活那些已经在spring容器里注册过的bean上的注解(无论是通过xml的方式还是通过package sanning的方式)。
  2. <context:component-scan>除了具有<context:annotation-config>的功能之外,还可以在指定的package下扫描以及注册javabean;
  3. 具体的不同可参考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代理在类加载期替换字节码达到织入切面的目的。

  1. 在Java语言中,织入切面的实现方式大概有如下三种:编译期织入,类加载期织入,运行期织入。编译期织入是指在Java编译期,采用特殊的编译器,将切面织入到Java类中;而类加载期织入则指通过特殊的类加载器,在类字节码加载到JVM时,织入切面;运行期织入则是采用CGLib工具或JDK动态代理进行切面的织入。
  2. AspectJ 提供了两种织入切面方式,一种是编译期织入,而另一种就是类加载期织入,也就是我们所说的LTW;

该属性有两个参数:

  • aspectj-weaving,有三个可选项,onoffautodetect,设置为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,该标签有如下几个参数:

  1. registration,将一个MBean注册到MBeanServer时,我们可以控制它的行为。针对已经有一个MBean注册到同一个ObjectName下时,该属性有3个可选值:failOnExisting,ignoreExisting, replaceExisting;
  2. default-domain,TODO:等学习到的时候再来补充;
  3. server,TODO:等学习到的时候再来补充;

<context:mbean-server>标签,有两个参数:

  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/>

然后,调用:

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • 文/无量水2017.6.23 爱你不是两三天,爱你已经很多年。 记得还是上小学时候,一个寒假刚刚结束,新学...
    诸葛恩德阅读 346评论 0 1
  • 昨天手机叮咚作响,收到一些人的平安夜祝福短信,被淹没在会计分录中的我,才恍然想起来,圣诞节要到了。 走出房间,跟妈...
    牛肉蘑菇酱阅读 426评论 0 1
  • [TOC] 内容 时间:2016/08/10 耗时30分钟 截图 总结
    上山老人阅读 162评论 0 0
  • 广州终于变冷啦 不过天气预报说广州将再一次入冬失败 南国嘛 emm还是有点丧 不过会慢慢变好吧 我现在的想法和对待...
    野榆木阅读 198评论 0 0