XML 配置 bean
bean
bean 的命名规范
每个一个 bean 都有一个或多个的标识符,区别在于它们的作用范围不一样。
指定标识符可以通过:
- id
- 在 ioc 容器中唯一。
- name
- 可以多个。
若是定义了 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 对象的全类名
- 这个类名符合规范的
- bean 的配置行为
- 表明 bean 对象如何在容器的运行(生命周期,范围等)
- 引用其它 bean
- 内部 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 方法给对象赋值的,并且需要提供一个无参构造器。
引用赋值
给属性赋值的只能是字面量,什么是字面量?可以理解为,看到什么就是什么。如下:
- 可以使用字符串表示的值,可以通过 value 属性或 value 子节点的方式指定
- 基本数据类型及其封装类、String 等类型都可以采取字面值注入的方式
- 若字面值中包含特殊字符,可以使用把字面值包裹起来
那么,若是只能对基本数据类赋值,这就不满足编程需要的了,一般来说,对象的属性是引用数据类型是很多时候都会有的。那这种情况如果使用上面所说的,很明显没办法赋值。的确,字面的方式不能给引用数据类型赋值。
这里可以使用 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)。
五个阶段分为:
- 创建对象阶段
- 这个是调用对象的构造器
- 赋值阶段
- 调用对象的 setXxx() 方法
- 初始化阶段
- 调用 bean 的 init-method 属性定义的方法
- 存活阶段
- 通俗来讲是使用对象的阶段,例如说调用对象功能等等
- 销毁阶段
- 关闭 ioc 容器的时候,会调用 destroy-method 属性定义的方法
<?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 属性配置:
- prototype
- 原型
- singleton
- 单例,默认的状态
<!-- <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 集合。
缺点与局限性:
- property 和 constructor 的 arg 显式的依赖始终覆盖自动装配。不能自动装配有些简单的属性,例如说:Classs、Strings
- 自动装配有时候会出现与期待的不一致,如上面的后面两种。这种情况,对象之间的关系不明确,不知道到底加载的是哪个类。
- 可能适用于生成文档的工具提供信息。
- 容器的若是匹配到多个符合的 bean,构造参数匹配
- 要是赋值给数组、Conllection、Maps 没有问题
- 但是要是依赖单一的值,那么 spring 不会自动解决这种情况,那么就会抛出 exception 异常。
针对于最后多个 bean 的解决方案:
- 使用显式赋值
- 将不希望被自动装配 bean 的设置 autowire-candidate 为 false
- 将希望被优先自动装配的 bean 的 primary 属性设置为 true
- 使用注解的方式实现更细粒度的自动装配
对于 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。
Q.E.D.
Comments | 0 条评论