IOC - 配置源(xml,注解)

1. 配置源

1.1 如何理解配置源

配置源,简单理解,就是配置的来源。在前面的超多例子中,都是使用 xml 配置文件或者注解配置类来驱动 IOC 容器,那么对于 IOC 容器而言,xml 配置文件或者注解配置类就可以称之为配置源。

解释这个概念倒是不难,不过我想请小伙伴们思考一个问题:我们自己写好的配置源给了 Spring 之后,Spring 是如何驱动起整个应用上下文的呢?

这个问题倒是不难回答,Spring 拿到配置源后肯定是先加载再解析最后注册那些定义好的 bean 到 IOC 容器,这个过程基本也就算结束了。下面咱来简单捋一捋这个过程中配置源的解析部分。

1.2 配置源的解析思路

咱现在已经学过的配置源就是 xml 配置文件,以及注解配置类两种,咱分别来看。

1.2.1 xml配置文件

仔细端倪一会之前写过的 xml 配置文件:

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.linkedbear.spring.basic_di.c_value_spel.bean"/>

    <context:property-placeholder location="classpath:basic_di/value/red.properties"/>

    <bean id="person" class="com.linkedbear.spring.basic_di.a_quickstart_set.bean.Person">
        <property name="name" value="test-person-byset"/>
        <property name="age" value="18"/>
    </bean>
</beans>

这个 xml 中包含几个部分:

  • xml 头信息

  • component-scan 声明包扫描

  • property-placeholder 引入外部 properties 文件

  • <bean> 注册 bean 并属性赋值

再思考一个问题:为什么在 xml 中能写这些标签呢?很简单,xml 头上的那些约束声明的可以写呗。

那 xml 头上的约束又是从哪来的呢?当然是 SpringFramework 中的 jar 包自带的呗。

思考到这里就可以了,要是真的问 jar 包是哪里来的,可能不出 10 个问题,我们就要开始思考人生了。。。

回到正题上,xml 中定义的这些信息,如果给这些信息一个定义,那它们就可以这样解释:

  • xml 中包含 1 条组件扫描的声明

  • 包含一条 properties 资源文件引入的声明

  • 包含一个 bean 的注册

于是这个 xml 可以用如下的一种抽象语言描述:

beans.xml {
    context: [component-scan, property-placeholder]
    beans: [person]
}

这里面不会描述具体的组件扫描路径等等,只会记录这个 xml 中声明了哪些标签

1.2.2 注解配置类

跟上面一样,咱先找一个之前写过的注解配置类:

@Configuration
@ComponentScan("com.linkedbear.spring.bean.b_scope.bean")
public class BeanScopeConfiguration {
    
    @Bean
    public Child child1(Toy toy) {
        Child child = new Child();
        child.setToy(toy);
        return child;
    }
    
    @Bean
    public Child child2(Toy toy) {
        Child child = new Child();
        child.setToy(toy);
        return child;
    }
}

根据上面的抽象思维,这个注解配置类也可以进行如下转换:

BeanScopeConfiguration.java: {
    annotations: [ComponentScan]
    beans: [child1, child2]
}

与上面一样,只会记录配置类中的配置结构而已,任何配置信息都不会体现在这里面。

2. 元信息

突然跳到元信息的章节了,是不是有点猝不及防?先缓缓神,想想上面为什么会叨叨这个解析思路呢?

也或者,可能会有一些小伙伴产生一种感觉:这种解析思路像是把整个文件中配置的所有定义都抽取出来了,形成了一个类似于配置定义信息的东西。

好,如果真的有这个感觉,请一直保持住;如果没有感觉到,那也没有关系,咱来解释一下这里头的重要概念:元信息

2.1 如何理解元信息

元信息,又可以理解为元定义,简单的说,它就是定义的定义

干说太抽象,直接上例子吧,方便理解:

  • 张三,男,18岁

    • 它的元信息就是它的属性们:Person {name, age, sex}

  • 咪咪,美国短毛,黑白毛,主人是张三

    • 它的元信息可以抽取为:Cat {name, type, color, master}

写到这里是不是突然产生一种感觉:这不就是对象和类吗???是的,所以我们可以这样说:类中包含对象的元信息

想一个小问题,类有元信息吗?当然有,**Class** 这个类里面就包含一个类的所有定义(属性、方法、继承实现、注解等),所以我们可以说:**Class** 中包含类的元信息

再举一个小伙伴们都比较熟悉的吧:数据库表与表结构信息,这也是非常典型的信息与元信息:数据库表结构描述了数据库表的整体表属性,以及表字段的属性。

2.2 SpringFramework中的配置元信息

理解了元信息的概念,接下来就可以来看 SpringFramework 中的元信息了。不过在 SpringFramework 中涉及到的元信息更多,咱先从最熟悉的开始看。

2.2.1 Bean的定义元信息

跟上面的 Class 描述类相似,SpringFramework 中定义的 Bean 也会封装为一个个的 Bean 的元信息,也就是 **BeanDefinition** 。它包含了一个 Bean 所需要的几乎所有维度的定义:

  • Bean 的全限定名 className

  • Bean 的作用域 scope

  • Bean 是否延迟加载 lazy

  • Bean 的工厂 Bean 名称 factoryBean

  • Bean 的构造方法参数列表 constructorArgumentValues

  • Bean 的属性值 propertyValues

  • ......

可以发现,通过这些定义,基本上一个 Bean 的所有特征、属性就全部都描述出来了。

有关 BeanDefinition 的内容,咱们放到后面专门开 2 章来讲解,这里先一带而过了。

2.2.2 IOC容器的配置元信息

IOC 容器的配置元信息分为 beans 和 context 两部分,分别展开来看。

2.2.2.1 beans的配置元信息

IOC 容器本身也是有元信息的,只不过这些元信息咱基本没怎么接触。以 xml 配置文件为例,如果你仔细注意一下整个配置文件的最顶层标签,会发现 <beans> 其实是有属性的:

只不过这些属性我们几乎不怎么用,都是用它默认的值了。

这个时候可能会有小伙伴一脸楞逼了,我去这都哪来的,我咋不知道呢,还有默认值呢?好吧没有关系,咱下面列出来这些属性的含义和默认值。

配置元信息
含义 / 作用
默认值

profile

基于环境的配置

""

default-autowire

默认的自动注入模式(不需要声明 @Autowired 等注解即可注入组件)

default(no)

default-autowire-candidates

满足指定属性名规则的属性才会被自动注入

""

default-init-method

全局 bean 的初始化方法

""

default-destroy-method

全局 bean 的销毁方法

""

default-lazy-init

全局 bean 是否延迟加载

default(false)

default-merge

继承父 bean 时直接合并父 bean 的属性值

default(false)

注:默认值中提到的 default 是在没有声明时继承父配置的默认值( <beans> 标签是可以嵌套使用的),如果都没有声明,则配置的默认值是括号内的值。

2.2.2.2 context的配置元信息

除此之外,还有一部分 IOC 容器的配置源信息来自于 spring-context 包的 context 前缀标签中(如之前写过的 <component-scan /> 标签)。

同样的,咱也把这些配置元信息都列出来,小伙伴们对里面几个相对常见的配置有一个了解即可。

配置元信息
含义 / 作用

<context:annotation-config/>

开启注解驱动

<context:component-scan/>

开启组件扫描

<context:property-placeholder/>

引入外部的资源文件( properties xml yml 等)

<context:property-override/>

指定配置源会覆盖全局配置(可用于配置覆盖)

<context:spring-configured/>

可以对没有注册到 IOC 容器的 bean 实现依赖注入

<context:load-time-weaver/>

与 AOP 相关(放到 AOP 章节介绍)

<context:mbean-server/>

暴露应用运行状态监控(与 JMX 管理监控有关)

<context:mbean-export/>

注册 MBean 到 JMX 实现运行状态监控(与 JMX 管理监控有关)

相对比较常用的标签是前三个,小伙伴们只需要对前三个比较了解即可。

到这里,针对 IOC 容器的基本配置也就都列举完毕了,需要了解的不多,而且大多都接触过了,小伙伴们注意巩固前面的知识点即可。

2.3 beans的其他配置元信息

前面介绍 <beans> 的配置元信息中,只是介绍了 <beans> 标签的属性,在 beans 的命名空间里还有两个常用的标签,而且也都比较简单:

配置元信息
含义 / 作用
使用方式举例

<alias />

给指定的 bean 指定别名

<alias name="person" alias="zhangsan"/>

<import />

导入外部现有的 xml 配置文件

<import resource="classpath:basic_dl/quickstart-byname.xml"/>

使用方式也比较简单,小伙伴们可以自行试探着借助 IDEA 写一写。

2.4 properties等配置元信息

上一章我们反复研究的 properties 、xml 、yml 文件,它们的作用都是为了将具体的配置抽取为一个可任意修改的配置文件,防止在程序代码中出现硬编码配置,导致修改配置还需要重新编译的麻烦。这种将配置内容抽取为配置文件的动作,我们称之为 “配置外部化”,抽取出来的配置文件又被成为 “外部化配置文件” 。而加载这些外部化配置文件的方式,要么通过上面的 <context:property-placeholder/> ,要么通过 @PropertySource 注解,它们最终都会被封装为一个一个的 PropertySource 对象( properties 文件被封装为 PropertiesPropertySource )了,而这个 PropertySource 对象内部就持有了这些外部化配置文件的所有内容。

最后更新于