Spring bean - 通过 xml 方式配置


分类: Java
评论: 0

Spring bean - 通过 xml 方式配置



分类: Java
评论: 0

XML 配置 bean

bean

bean 的命名规范

每个一个 bean 都有一个或多个的标识符,区别在于它们的作用范围不一样。

指定标识符可以通过:

若是定义了 id,那么 name 被视为 alias。

当然,若是没有给 bean 显式指定一个 id/name,那么 spring ioc 容器会自动生成一个唯一的标识符。

但是若是要通过 name 来引用 bean,则通过 ref 元素或者 Service Locator 那么必须提供一个 name。不用提供的 name 的原因与内部 bean 和 自动装配有关。

与 Java 的命名规范一样,id/name 的命名规范首字母小写的驼峰式命名。

同样的,上面描述到给 bean 定义别名。这在一个系统中很有用,比如说:有个公共的依赖,很多组件需要引用这个依赖,那么为了维护命名空间。

将公共的依赖定义多个 alias ,这些别名可能会加上不同组件含义,那么可以与其它命名区分开,不会造成冲突或者命名误解。

<alias name="from" alias="to" />

给 from 创建一个 alias 为:to。

值得注意的是,别名不仅限于一个,可以多个。

声明 bean 与对象属性赋值

spring ioc 容器通过元数据来管理一个或多个bean,这个元数据可以通过 xml 配置,来配置 bean 如何被 ioc 容器所管理。

在 bean 中可以定义哪些配置,其中可以归纳为几种:

一个正确的 bean 配置文件是位于一对 beans 标签中,如下:

<?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">
     <!-- 简单的 bean 必须要有的两个属性 -->
    <bean id="" class="***.***.***"></bean>
</beans>

beans 标签中那一大堆的网址,规范着当前配置文件中哪些标签是合法的。

这样通过 id,就可以从 ioc 容器获取到 bean 对象。

除了这样,还可以给 bean 对象属性赋值,通过一对 property 标签:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmls: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">

     <!-- 简单的 bean 必须要有的两个属性 -->
    <bean id="helloWorld" class="com.huasio.spring.helloWorld">
    	<property name="name" value="spring" />
    </bean>
</beans>

这样就给 bean 的 name 属性赋值了,需要注意的是:这样赋值需要 POJO 对象满足 java bean 的规范,有定义 setXxx() 方法,因为 spring 是通过 set 方法给对象赋值的,并且需要提供一个无参构造器。

引用赋值

给属性赋值的只能是字面量,什么是字面量?可以理解为,看到什么就是什么。如下:

那么,若是只能对基本数据类赋值,这就不满足编程需要的了,一般来说,对象的属性是引用数据类型是很多时候都会有的。那这种情况如果使用上面所说的,很明显没办法赋值。的确,字面的方式不能给引用数据类型赋值。

这里可以使用 bean 标签的 ref 属性来引用其它 bean 来给引用数据类型赋值。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmls: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">

    <bean id="helloWorld" class="com.huasio.spring.HelloWorld">
    	<property name="name" value="spring" />
    	<property name="student" ref="student">
    </bean>
    <bean id="student" class="com.huaiso.spring.Student">
    	<property name="name" value="spring" />
    	<property name="id" value="100001" />
    	<property name="age" value="24" />
    </bean>
</beans>

这样就通过 ref 引用的方式,来给 HelloWorld 类的 student 属性给赋值了一个 Student 对象。

除了这样方式,还有一个方式,那就是级联,属性通过 . 直接给引用数据属性赋值。如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmls: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">

    <bean id="helloWorld" class="com.huasio.spring.HelloWorld">
    	<property name="name" value="spring" />
    	<property name="student.name" ref="spring" />
    	<property name="student.id" ref="100001" />
    	<property name="student.age" ref="99" />
    </bean>
</beans>

上面将基础的定义、赋值两种方式给简单的描述了下。除了这些基础的属性之外,还可以定义 bean 对象的声明周期。

生命周期

一个 bean 在 spring ioc 中,是存在生命周期的,spring ioc 会在不同阶段调用对应的方法(如果有init、destroy)。

五个阶段分为:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmls: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">

    <bean id="helloWorld" class="com.huasio.spring.HelloWorld" init-method="init" destroy-method="destroy">
    	<!-- 这里省略赋值 -->
    </bean>
</beans>

example 打印了各自阶段的信息:

创建阶段

赋值阶段

初始化

hello spring

二月 09, 2020 7:33:19 下午 org.springframework.context.support.AbstractApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@6979e8cb: startup date [Sun Feb 09 19:33:18 CST 2020]; root of context hierarchy

销毁

bean 对象实例作用域

一般来讲,像直接在配置文件中声明 bean 对象,没有特别的配置属性,那么该 bean 对象在 ioc 容器只有一个实例,一个设计模式就是单例模式,也就是在项目的任何地方调用都是当前实例,作用于全局。

有些时候,可能遇到一种需求,比如说,不能使用单例模式,一般这种情况是数据不能残留,也就是每一次的使用都是不同状态。很典型的一个例子,就是 mybatis 的 SqlSession 实例,此实例每个线程都有一个,不是线程安全的。而 SqlSessionFactory 的实例是单例的,也就是整个项目运行期间不应该重新创建或者被销毁的。

作用域通过 bean 标签的 scope 属性配置:

<!-- <bean id="helloWorld" class="com.huasio.ssm.beans.HelloWorld" scope="prototype"> -->
<bean id="helloWorld" class="com.huasio.ssm.beans.HelloWorld" scope="singleton">
    <property name="name" value="spring"/>
</bean>

XML 方式自动注入

还是一样的需求,一个 POJO 中,属性通常会包含引用数据类型,这种类型通常需要在 xml 配置文件中引用另外 ref bean 来进行赋值,或者级联方式。但是,这两种不管是那种,都是需要手动配置。

那么 xml 配置文件方式有没有一种,让 spring ioc 容器自动注入依赖呢?刚好,spring xml 就有这种配置。

模式有四种:

模式说明
default默认模式,不用自动装配
byName按照属性名称在 spring ioc 容器中寻找匹配 id 的 bean 对象,若是没有则不赋值。
byType根据对象类型自动装配,在 spring ioc 容器中寻找匹配的 bean 对象,若是找到,则自动装配;若是找到多个匹配的类型,则抛出致命错误;若是没有找到,则没有赋值。
constructor类似 byType,因为其参数就是一个个引用类型构成

这几种模式中,前面两种在使用上面不怎么复杂,default就保持不用,byName 则根据属性与 id 匹配,那么也就是只有一个 bean 实例。

而后面两种其实雷同,可能有一个,可能有多个,也可能没有。按照面向对象的继承与多态,很有可能 spring ioc 中的 bean 实例有其父类、接口等。

一旦 ioc 容器匹配到多个 bean,那么程序不会正常运行。处于这种情况,可以将其结果赋值给一个数组、Conllection 或者 map 集合。

缺点与局限性:

针对于最后多个 bean 的解决方案:

对于 autowire-candidate 属性来讲,经对于 byType 有效。

除了上面的限制配置,顶级的 beans 也提供一些配置,其中就有:default-autowire、default-autowire-candidate。

对于 byType 自动装配的,除了 bean 本身的限制之外,还可以通过 default-autowire-candidate 来设置要匹配的 bean 包含哪些字符。

多个模式可以使用 逗号(,) 来分割。

<?xml version="1.0" encoding="UTF-8"?>

<!-- 这里设置了以 01 结尾的 bean id 被加载 -->
<beans default-autowire-candidates="*01" 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">

    <bean id="helloWorld" class="com.huasio.ssm.beans.HelloWorld" autowire="byType">
        <property name="name" value="spring"/>
    </bean>
    <bean id="student01" class="com.huasio.ssm.beans.Student">
        <property name="id" value="100001"/>
    </bean>
    <bean id="student02" class="com.huasio.ssm.beans.Student">
        <property name="id" value="100002"/>
    </bean>
</beans>

上面的 example 的 beans 这里设置了以 01 结尾的 bean id 被加载。

需要注意的是,设置了这些限制属性,并不影响 bean 本身使用自动装配。

内部 bean

见名思意,这种方式就是其它 bean 在对某个属性赋值的时候,而这个属性正好是引用数据类型的,并且这个 bean 只是使用一次或者只有这个属性需要用到,那么就可以考虑使用内部 bean。

很简单,定义的方式和其它 bean 都一样,区别在于不能被外部的 bean 所引用,以及在程序中 getBean 获取。

<?xml version="1.0" encoding="UTF-8"?>
<beans default-autowire-candidates="01" 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">

<!--    <context:component-scan base-package="com.huasio.ssm.beans"/>-->
<!--    <bean id="helloWorld" class="com.huasio.ssm.beans.HelloWorld" destroy-method="destroy" init-method="init">-->
<!--        <property name="name" value="spring"/>-->
<!--    </bean>-->
    <bean id="helloWorld" class="com.huasio.ssm.beans.HelloWorld">
        <property name="name" value="spring"/>
        <property name="student" ref="student01" />
        <!-- 内部 bean -->
        <bean id="student01" class="com.huasio.ssm.beans.Student">
        	<property name="id" value="100001"/>
    	</bean>
    </bean>

</beans>

注册现有 Objects

除了在 xml 文件中声明定义往 ioc 容器注册 bean,还支持在运行时将 现有对象注册为 bean 实例,当然这种方式官方并不支持。

而且 bean 的元数据和手动提供的 singleton bean 实例注册时间最好还是由 spring 一开始就注册,按照 spring 的手册描述,自动装配的 bean ,spring 还会在注册时会推理一些步骤,例如说在 ioc 找匹配的 bean 实例。

说归说,实现方法还是要讲一下:

@Test
public void testHello() {
    ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
    DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) ioc.getBeanFactory();
    List list = new ArrayList();
    list.add(hello);
    beanFactory.registerSingleton("list", list);
    ioc.close();
}

这样就注册一个 id 为 list 的 bean singleton。