目录
- 1. Bean 的配置方式
- 2. 依赖注入的方式
- 1)构造器注入
- 2)属性注入
- 3. 注入属性值或依赖对象
- 1)字面值
- 2)其他 Bean 对象
- 3)集合对象
- 4)内部 Bean
- 5)null 值和级联属性
- 6)使用 p 命名空间
- 7)Bean 的继承
- 8)引用外部文件属性值
- 4. 自动装配
- 5. Bean 之间的关系
- 1)Bean 的继承关系
- 2)Bean 的依赖关系
- 6. Bean 的作用域
- 7. SpEL
- 1)字面量 (也可以不需要用到 SpEL)
- 2)引用 Bean、属性和方法
- 3)比较运算符, if/else, and/or/not, 正则表达式
- 8. 静态工厂方法和实例工厂方法创建 Bean
- 1) 静态工厂方法
- 2) 实例工厂方法
- 3) FactoryBean接口
1. Bean 的配置方式
配置方式有两种:
- 基于 XML 文件配置:在 Spring 配置文件中,使用特定的标签元素来声明 Bean 及它们的依赖关系
- 基于注解的方式:通过在类、方法、属性等上面使用注解的方式来管理配置 Bean
2. 依赖注入的方式
为对象注入属性值或依赖对象的主要方式有三种,分别是:构造器注入、属性注入和接口方法注入(比较不常用)。
1)构造器注入
使用构造器注入 Bean 的属性值或依赖的对象,Bean 在实例化后就可以使用:
<!-- 1. 按照顺序 -->
<bean id="user" class="com.sonata.spring.helloworld.User">
<!-- 要求: 在 Bean 中必须有对应的构造器. -->
<constructor-arg value="Mike"></constructor-arg>
</bean>
<!-- 2. 按照索引匹配入参 -->
<bean id="user" class="com.sonata.spring.helloworld.User">
<constructor-arg value="Mike" index="0"></constructor-arg>
<constructor-arg value="18" index="1"></constructor-arg>
<constructor-arg value="male" index="2"></constructor-arg>
</bean>
<!-- 3. 按照类型匹配入参 -->
<bean id="user" class="com.sonata.spring.helloworld.User">
<constructor-arg value="Mike" type="java.lang.String"></constructor-arg>
<constructor-arg value="18" type="java.lang.Integer"></constructor-arg>
<constructor-arg value="male" type="java.lang.String"></constructor-arg>
</bean>
<!-- 若一个 bean 有多个构造器, 如何通过构造器来为 bean 的属性赋值 -->
<!-- 可以根据 index 和 type 进行更加精确的定位 -->
<bean id="user" class="com.sonata.spring.helloworld.User">
<constructor-arg value="KUGA" index="1"></constructor-arg>
<constructor-arg value="ChangAnFord" index="0"></constructor-arg>
<constructor-arg value="250000" type="float"></constructor-arg>
</bean>
优点:
- 一注入即就绪状态,可以马上用
缺点:
- 当依赖对象多的时候,参数列表长;
- 因为是通过反射来注入的,当多个同类型参数存在比较难处理;
- 针对不同的需求,可能需要引入多个构造方法,不方便维护。
2)属性注入
实际使用中比较常用的注入方式,使用 setter 方法进行赋值,所以对象中必须有对应的属性 set 方法:
<!-- 配置一个 bean -->
<bean id="user" class="com.sonata.spring.helloworld.User">
<!-- 为属性赋值 -->
<property name="name" value="Jason"></property>
</bean>
优点:
- setter 方法命名比较随意,描述性比构造方法好;
- setter 方法可以被继承,可以设置默认值;
3. 注入属性值或依赖对象
上面已经讲了注入属性或依赖对象的主要方式,但是在注入不同类型的值或对象的时候,写法有些区别,可以分为字面值(或者说是魔法值),其他 Bean 对象,内部 Bean,null 值,集合,级联属性。
1)字面值
字面值, 可以用字符串表示的值, 这种类型的值可以使用 value 属性或者 <value>
元素标签进行注入:
<bean id="user" class="com.sonata.spring.helloworld.User">
<constructor-arg value="Mike" index="0"></constructor-arg>
<constructor-arg>
<value>18</value>
</constructor-arg>
</bean>
若字面值中包含特殊字符,如 <
, >
等, 可以使用 <![CDATA[字面值]]>
把字面值包裹起来:
<bean id="user" class="com.sonata.spring.helloworld.User">
<!-- 这里为姓名注入值: Jack<>son -->
<constructor-arg>
<value><![CDATA[Jack<>son]]></value>
</constructor-arg>
</bean>
2)其他 Bean 对象
Java 程序通常是由很多对象组成的,有些对象中又会包含对其他对象的引用,所以 Bean 之间需要相互访问,在配置文件中,可以通过<ref>
元素或 ref 属性为 Bean 的属性或构造器进行注入:
<bean id="car" class="com.sonata.spring.helloworld.Car">
<!-- 通过 ref 属性值指定当前属性指向哪一个 bean! -->
<property name="dao" ref="dao5"></property>
</bean>
<bean id="user" class="com.sonata.spring.helloworld.User">
<!-- 通过 ref 属性值指定当前属性指向哪一个 bean 的 ID -->
<property name="car" ref="car"></property>
</bean>
3)集合对象
当 Bean 对象中的属性包含集合属性时, 如 List, Set, Map 等, 可以使用 Spring 提供的特定标签进行配置:
<!-- 装配集合属性 -->
<bean id="user" class="com.sonata.spring.helloworld.User">
<property name="userName" value="Jack"></property>
<property name="cars">
<!-- 使用 list 元素来装配集合属性 -->
<list>
<ref bean="car"/>
<ref bean="car2"/>
</list>
</property>
</bean>
- set 集合使用
<set>
标签 - 数组的定义和 List 一样, 也是使用
<list>
- java.util.Properties 对象, 可以使用
<props>
标签定义, 其内包含多个<prop>
作为子标签, 每个<prop>
标签需要定义 key 属性
util 空间下的标签
为了复用 list 配置对象, 可以使用 util schema
来定义一个带有 ID 的集合对象, 为其他 Bean 对象提供引用:
<!-- 声明集合类型的 bean -->
<util:list id="cars">
<ref bean="car"/>
<ref bean="car2"/>
</util:list>
<bean id="user" class="com.sonata.spring.helloworld.User">
<property name="userName" value="Jack"></property>
<!-- 引用外部声明的 list -->
<property name="cars" ref="cars"></property>
</bean>
4)内部 Bean
内部 Bean,有点类似内部类,就是在 Bean 或构造器内部赋值的时候直接声明一个 Bean 对象,这个对象可以不需要定义 id
属性。如果有 Bean 对象仅仅只是给某个对象的特定属性使用时, 可以将它声明为内部 Bean,直接放在<property>
或<constructor-arg>
元素中:
<!-- 声明使用内部 bean -->
<bean id="user" class="com.sonata.spring.helloworld.User">
<property name="car">
<!-- 内部 bean, 类似于匿名内部类对象. 不能被外部的 bean 来引用, 也没有必要设置 id 属性 -->
<bean class="com.sonata.spring.helloworld.Car">
<property name="color" value="black"></property>
</bean>
</property>
</bean>
5)null 值和级联属性
- null 值:可以使用专用的
<null/>
元素标签为 Bean 的字符串或其它对象类型的属性注入 null 值 - 级联属性:和 Struts、Hiberante 等框架一样,Spring 支持级联属性的配置
<!-- 设置 null 值 -->
<bean id="user" class="com.sonata.spring.helloworld.User">
<property name="car"><null/></property>
</bean>
<!-- 设置级联属性 -->
<bean id="user" class="com.sonata.spring.helloworld.User">
<property name="car" ref="car"></property>
<property name="car.color" value="black"></property>
</bean>
6)使用 p 命名空间
为了简化 XML 文件的配置,Spring 2.5 版本引入了 p 命名空间,可以直接作为<bean>
标签的属性来配置:
<bean id="user" class="com.sonata.spring.helloworld.User"
p:cars-ref="cars" p:userName="Tim Cook"></bean>
7)Bean 的继承
<!-- 使用 parent 来完成继承 -->
<bean id="user4" parent="user" p:userName="Bob"></bean>
8)引用外部文件属性值
比如在配置数据库连接的时候, 经常使用db.properties
文件来保存连接参数:
jdbc.user=root
jdbc.password=123456
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql:///sonata
如果我们需要在 Spring 配置文件中引用的话, 在 Spring2.0 的时候, 需要先配置 PropertyPlaceholderConfigurer
类, 同时引入配置文件:
<!-- 使用 parent 来完成继承 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:db.properties"></property>
</bean>
在Spring2.5
之后, 可以通过简化的元素:
<!-- 导入外部的资源文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
</bean>
4. 自动装配
IoC 容器可以自动装配 Bean,只需通过在<bean>
标签的autowire
属性指定自动装配的模式, 这样在配置 Bean 的时候就不需要再手动指定属性值是什么:
public class Dao {
private DataSource dataSource;
//getter, setter...
}
public class Service {
private Dao dao;
//getter, setter...
}
<!-- 自动装配: 只需要声明 bean, 然后把 bean 之间的关系交给 IOC 容器来完成 -->
<!--
byType: 根据类型进行自动装配. 但要求 IOC 容器中只有一个类型对应的 bean, 若有多个则无法完成自动装配.
byName: 若属性名和某一个 bean 的 id 名一致, 即可完成自动装配. 若没有 id 一致的, 则无法完成自动装配
-->
<!-- 在使用 XML 配置时, 自动转配用的不多. 但在基于 注解 的配置时, 自动装配使用的较多. -->
<bean id="dao" class="com.spring.ref.Dao">
<property name="dataSource" value="C3P0"></property>
</bean>
<!-- 默认情况下 bean 是单例的! -->
<!-- 但有的时候, bean 就不能使单例的. 可以通过 scope 属性来指定 bean 的作用域 -->
<!--
prototype: 原型的. 每次调用 getBean 方法都会返回一个新的 bean. 且在第一次调用 getBean 方法时才创建实例
singleton: 单例的. 每次调用 getBean 方法都会返回同一个 bean. 且在 IOC 容器初始化时即创建 bean 的实例. 默认值
-->
<bean id="service" class="com.spring.ref.Service" autowire="byName"></bean>
autowire 属性要么根据类型自动装配, 要么根据名称自动装配, 不能两者兼而有之
5. Bean 之间的关系
1)Bean 的继承关系
- Spring 允许继承 bean 的配置, 被继承的 bean 称为父 bean. 继承这个父 Bean 的 Bean 称为子 Bean
- 子 Bean 从父 Bean 中继承配置, 包括 Bean 的属性配置
- 子 Bean 也可以覆盖从父 Bean 继承过来的配置
- 父 Bean 可以作为配置模板, 也可以作为 Bean 实例. 若只想把父 Bean 作为模板, 可以设置
<bean>
的 abstract 属性为 true, 这样 Spring 将不会实例化这个 Bean - 并不是
<bean>
元素里的所有属性都会被继承. 比如: autowire, abstract 等. - 也可以忽略父 Bean 的 class 属性, 让子 Bean 指定自己的类, 而共享相同的属性配置. 但此时 abstract 必须设为 true
2)Bean 的依赖关系
- Spring 允许用户通过 depends-on 属性设定 Bean 前置依赖的 Bean,前置依赖的 Bean 会在本 Bean 实例化之前创建好
- 如果前置依赖于多个 Bean,则可以通过逗号,空格或的方式配置 Bean 的名称
6. Bean 的作用域
- singleton: 在 SpringIOC 容器中仅存在一个 Bean 实例, Bean 以单例的方式存在
- prototype: 每次调用 getBean() 时都会返回一个新的实例
- request: 每次 HTTP 请求都会创建一个新的 Bean, 该作用域仅适用于 WebApplicationContext 环境
- session: 同一个 HTTP Session 共享一个 Bean, 不同的 HTTP Session 使用不同的 Bean。该作用域仅适用于 WebApplicationContext 环境
7. SpEL
支持运行时查询和操作对象图的强大的表达式语言;语法类似于 EL:SpEL 使用 #{…} 作为定界符,所有在大框号中的字符都将被认为是 SpEL;SpEL 为 bean 的属性进行动态赋值提供便利。
通过 SpEL 可以实现:
- 通过 bean 的 id 对 bean 进行引用
- 调用方法以及引用对象中的属性
- 计算表达式的值
- 正则表达式的匹配
1)字面量 (也可以不需要用到 SpEL)
- 整数:
<property name="count" value="#{5}"/>
- 小数:
<property name="frequency" value="#{89.7}"/>
- 科学计数法:
<property name="capacity" value="#{1e4}"/>
- String 可以使用单引号或者双引号作为字符串的定界符号:
<property name=「name」value="#{'Chuck'}"/>
或<property name='name' value='#{"Chuck"}'/>
- Boolean:
<property name="enabled" value="#{false}"/>
<bean ...>
<property name="name" value="Jack"></property>
</bean>
2)引用 Bean、属性和方法
<bean id="car" class="com.spring.beans.spel.Car">
<bean id="address" class="com.spring.beans.spel.Address">
<bean id="person" class="com.spring.beans.spel.Person">
<!-- 使用 SpEL 引用其他的 Bean -->
<property name="car" value="#{car}"></property>
<!-- 使用 SpEL 引用其他的 Bean 的属性 -->
<property name="city" value="#{address.city}"></property>
<!-- 使用运算符 -->
<property name="info" value="#{car.price > 100 ? 'Y' : 'N'}"></property>
</bean>
3)比较运算符, if/else, and/or/not, 正则表达式
8. 静态工厂方法和实例工厂方法创建 Bean
1) 静态工厂方法
调用静态工厂方法创建 Bean是将对象创建的过程封装到静态方法中. 当客户端需要对象时, 只需要简单地调用静态方法, 而不同关心创建对象的细节。要声明通过静态方法创建的 Bean, 需要在 Bean 的 class 属性里指定拥有该工厂的方法的类, 同时在 factory-method 属性里指定工厂方法的名称. 最后, 使用 <constrctor-arg>
元素为该方法传递方法参数:
public class StaticCarFactory {
private static Map<String, Car> cars = new HashMap<>();
static {
cars.put("audi", new Car("audi", 10000));
cars.put("ford", new Car("ford", 20000));
}
public static Car getCar(String name) {
return cars.get(name);
}
}
Bean 文件配置:
<!-- 如果工厂方法需要传入参数, 则使用 constrctor-arg 来配置参数 -->
<bean id="car" class="com.sonata.spring.factory.StaticCarFactory"
factory-method="getCar">
<constrctor-arg value="audi"/>
</bean>
2) 实例工厂方法
先创建工厂实例, 然后再调用工厂实例的工厂方法创建 Bean:
/**
* 创建实例化的工厂
*/
public class InstanceCarFactory {
private Map<String, Car> cars = new HashMap<>();
public InstanceCarFactory {
cars.put("audi", new Car("audi", 10000));
cars.put("ford", new Car("ford", 20000));
}
public Car getCar(String name) {
return cars.get(name);
}
}
Bean 文件配置:
<!-- 先配置工厂, 进行实例 -->
<bean id="factory" class="com.sonata.spring.factory.InstanceCarFactory "></bean>
<!-- 如果工厂方法需要传入参数, 则使用 constrctor-arg 来配置参数 -->
<bean id="car" factory-bean="factory" factory-method="getCar">
<constrctor-arg value="audi"/>
</bean>
3) FactoryBean接口
工厂 Bean 跟普通 Bean 不同, 其返回的对象不是指定类的一个实例, 其返回的是该工厂 Bean 的 getObject 方法所返回的对象 :
/**
* 自定义的 FactoryBean 需要实现 Spring 的 FactoryBean 接口
*/
public class CarFactoryBean implements FactoryBean<Car> {
private String brand;
//getter(), setter()
@Override
public Car getObject() throws Exception {
return new Car("BMW", 5000);
}
@Override
public Class<?> getObjectType() {
return Car.class;
}
@Override
public boolean isSingleton() {
return return;
}
}
Bean 文件配置:
<bean id="car" class="com.sonata.spring.factory.CarFactoryBean">
<property name="brand" value="BMW"/>
</bean>