4.1 Spring 配置概述
4.1.1 Spring 容器高层视图
-
Spring容器成功启动的必备条件
- Spring 框架的类包都已经放到应用程序的类路径下
- 应用程序为 Spring 提供完备的 Bean 配置信息
- Bean 的类都已经放到应用程序的类路径下
-
Bean 在Spring中加载的过程
- 1、Bean 配置信息定义了 Bean 的实现及依赖关系
- 2、Spring 容器根据各种形式的 Bean 配置信息在容器内部建立 Bean 定义注册表
- 3、然后根据注册表加载、实例化 Bean,并建立 Bean 和 Bean 的依赖关系
- 4、最后将这些准备就绪到 Bean 放到 Bean 缓存池中,以供外层的应用程序进行调用
4.1.2 基于 XML 的配置
- xml 文件头中声明的内容
- 默认命名空间: 它没有空间名,用于Spring Bean 的定义
- xsi命名空间: 这个命名空间用于为每个文档中命名空间指定相应的 Schema 样式文件,是标准组织定义的标准命名空间
- aop命名空间: 这个命名空间是Spring 配置 AOP 的命名空间,是用户自定义的命名空间。
4.2 Bean 基本配置
4.2.1 装配一个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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<bean id="car" class="com.baobaotao.simple.Car" />
</beans>
4.2.2 Bean的命名
- 配置一个 Bean 时,需要指定一个 id 或 name 属性作为 Bean 的名称
id 和 name 都可以指定多个名字,之间用逗号、分号或者空格进行分割
<bean id="car" class="com.baobaotao.simple.Car" />
<bean name="#car1,123,$car" class="com.baobaotao.simple.Car" />
<bean class="com.baobaotao.simple.Car" />
- 通过getBean(“car”)、getBean("#car1")、getBean(“123”)、getBean("$car") 、getBean(“com.baobaotao.simple.Car”)获取 Bean
注意: - Spring配置文件不允许出现两个相同 id 的 < bean >;却可以出现两个相同 name 的 Bean;通过 getBean(beanName) 获取最后声明的 Bean,原因是后面的 Bean 覆盖前面 Bean。 故,尽量使用 id 而非 name 命名的 Bean。
- 若没有 id 、name,则默认全路径为 Bean 命名。
4.3 依赖注入
4.3.1 属性注入
package com.baobaotao.ditype;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Car {
private int maxSpeed;
private String brand;
private double price;
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<bean id="car" class="com.baobaotao.ditype.Car">
<property name="maxSpeed"><value>200</value></property>
<property name="brand"><value>红旗CA72</value></property>
<property name="price"><value>20000.00</value></property>
</bean>
</beans>
package com.baobaotao.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.baobaotao.ditype.Car;
public class ApplicationBeansTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Car car = (Car) context.getBean("car");
System.err.println(car);
}
}
注意:成员变量的命名规则 驼峰命名、全小写、首字母不可大写
4.3.2 构造函数注入
package com.baobaotao.ditype;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Car {
private int maxSpeed;
private String brand;
private double price;
private String iDcode;
//测试1
public Car(String brand, double price) {
this.brand = brand;
this.price = price;
}
//测试2
public Car(String brand, String corp, double price) {
this.brand = brand;
this.corp = corp;
this.price = price;
}
//测试3
public Car(String brand, String corp, int maxSpeed) {
this.brand = brand;
this.corp = corp;
this.maxSpeed = maxSpeed;
}
}
package com.baobaotao.ditype;
public class Office {
}
package com.baobaotao.ditype;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Boss {
private String name;
private Car car;
private Office office;
//测试4
public Boss(String name, Car car, Office office) {
this.name = name;
this.car = car;
this.office = office;
}
}
<!-- 测试1 -->
<bean id="car" class="com.baobaotao.ditype.Car">
<constructor-arg type="double">
<value>20000</value>
</constructor-arg>
<constructor-arg type="String">
<value>红旗CA72</value>
</constructor-arg>
</bean>
<!-- 测试2 -->
<bean id="car" class="com.baobaotao.ditype.Car">
<constructor-arg index="0" value="红旗CA72"/>
<constructor-arg index="1" value="中国一汽"/>
<constructor-arg index="2" value="20000"/>
</bean>
<!-- 测试3 -->
<bean id="car" class="com.baobaotao.ditype.Car">
<constructor-arg index="0" type="String" value="红旗CA72"/>
<constructor-arg index="1" type="String" value="中国一汽"/>
<constructor-arg index="2" type="double" value="20000"/>
</bean>
<!-- 测试4 -->
<bean id="boss" class="com.baobaotao.ditype.Boss">
<constructor-arg>
<value>John</value>
</constructor-arg>
<constructor-arg>
<ref bean="car"/>
</constructor-arg>
<constructor-arg>
<ref bean="office"/>
</constructor-arg>
</bean>
<bean id="car" class="com.baobaotao.ditype.Car">
<constructor-arg index="0" type="String" value="红旗CA72"/>
<constructor-arg index="1" type="String" value="中国一汽"/>
<constructor-arg index="2" type="double" value="20000"/>
</bean>
<bean id="office" class="com.baobaotao.ditype.Office"/>
- 循环依赖问题
package com.baobaotao.ditype;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Car {
private String brand;
private Boss boss;
public Car() {}
public Car(String beand, Boss boss) {
this.brand = beand;
this.boss = boss;
}
}
package com.baobaotao.ditype;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Boss {
private String name;
private Car car;
public Boss(String name, Car car) {
this.name = name;
this.car = car;
}
}
<bean id="car" class="com.baobaotao.ditype.Car">
<constructor-arg index="0" value="红旗CA72"/>
<constructor-arg index="1" ref="boss"/>
</bean>
<bean id="boss" class="com.baobaotao.ditype.Boss">
<constructor-arg index="0" value="John"/>
<constructor-arg index="1" ref="car"/>
</bean>
该种方法无法启动 Spring Ioc容器,因为循环依赖;解决方法:将构造函数注入方式调整为属性注入方式
4.3.3 工厂方法注入(大可不适用)
package com.baobaotao.ditype;
public class CarFactory {
//非静态工厂方法
public Car createhongQiCar() {
Car car = new Car();
car.setBrand("红旗CA72");
return car;
}
//静态工厂方法
public static Car createhongQiCarStatic() {
Car car = new Car();
car.setBrand("红旗CA72");
return car;
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Car car = (Car) context.getBean("car5");
System.err.println(car);
}
}
<!-- 动态工厂类配置 -->
<!-- @1工厂类Bean -->
<bean id="carFactory" class="com.baobaotao.ditype.CarFactory" />
<!-- factory-bean 指定@1处的工厂类Bean:factory-method 指定工厂类Bean 创建该 Bean 的工厂方法 -->
<bean id="car5" factory-bean="carFactory" factory-method="createhongQiCar"/>
<!-- 静态工厂类配置 -->
<bean id="car6" class="com.baobaotao.ditype.CarFactory" factory-method="createhongQiCarStatic"/>
4.3.4 选择注入方式的考量
- 构造函数可以保证一些重要的属性在Bean实例化时就设置好,避免了因为一些重要属性没有提供,导致一个无用Bean实例的情况;
- 不需要为每个属性提供Setter方法,减少了类的方法个数
- 可以更好的封装变量,不需要为每个属性指定Setter方法,避免外部错误的调用。更多的开发者可能更倾向于使用属性注入方式,他们反对构造函数注入的理由是:
- 如果一个类的属性众多,构造函数的签名将变成一个庞然大物,可读性很差;
- 灵活性不强,在有些属性是可选的情况下,如果通过构造函数注入,也需要为可选的参数提供一个null值
- 如果有多个构造函数,需要考虑配置文件和具体构造函数匹配歧义的问题,配置上相对复杂
- 构造函数不利于类的继承和扩展,因为子类需要引用到父类复杂的构造函数
4.4 注入参数详解
4.4.1 字面值
所谓"字面值"一般是指可用字符串表示的值,这些值可以通过< value >元素标签进行注入。
XMl特殊处理标签
- XML的5个特殊字符(&、<、>、"、’、。)
- 处理方法
- 以 <![CDATA[ ]]> 使XML解析器将标签中的字符串当作普通的文本对待
- 使用XML转移序列特殊的符
特殊符号 | 转义序列 |
---|---|
< | & lt; |
> | & gt; |
& | & amp; |
" | & quot; |
’ | & apos; |
注意:一般情况下,XML解析器会忽略内部字符串的前后空格,但Spring却不忽略元素标签内部字符串的前后空格。
<bean id="car" class="com.baobaotao.ditype.Car">
<property name="maxSpeed">
<value>200</value>
</property>
<property name="brand">
<!-- <value><![CDATA[红旗&CA72]]></value> -->
<value>红旗&CA72</value>
</property>
</bean>
4.4.2 引用其他 Bean
- < ref> 元素可以通过一下三个属性引用容器中其他Bean
- bean: 通过该属性可以引用同一容器或父容器的 Bean,这是最常见的形式
- local: 通过改属性只能引用统一配置文件中定义的 Bean,它可以利用XML解析器自动检验引用的合法性,以便在开发编写配置时能够及时发现并纠正配置的错误
- parent: 引用父容器中的 Bean,如< ref parnet=“car”>的配置说明car 的 Band 是父容器的中的Bean
beans1.xml
<!-- 在父容器中定义的car -->
<bean id="car" class="com.baobaotao.ditype.Car">
<property name="brand" value="红旗CA72"/>
<property name="maxSpeed" value="200"/>
<property name="price" value="20000.00"/>
</bean>
beans2.xml
<!-- 该Bean 和父容器的 car Bean 具有相同的id -->
<bean id="car" class="com.baobaotao.ditype.Car">
<property name="brand" value="吉利CT5"/>
<property name="maxSpeed" value="100"/>
<property name="price" value="20000.00"/>
</bean>
<bean id="boss" class="com.baobaotao.ditype.Boss">
<property name="car">
<!-- <ref parent="car"/> 引用父容器中的car,而非同xml中定义的 Bean,如果采用< ref bean="car"/> 将应用本容器中的car -->
<!-- <ref parent="car"/> -->
<ref bean="car"/>
</property>
</bean>
package com.baobaotao.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.baobaotao.ditype.Boss;
import com.baobaotao.ditype.Car;
import com.baobaotao.ditype.CarFactory;
public class ApplicationBeansTest {
public static void main(String[] args) {
//父容器
ClassPathXmlApplicationContext pFactory = new ClassPathXmlApplicationContext(new String[] {"beans1.xml"});
//指定pFactory为该容器的父容器
ApplicationContext factory = new ClassPathXmlApplicationContext(new String[] {"beans2.xml"}, pFactory);
Boss boss = (Boss) factory.getBean("boss");
System.err.println(boss);
}
}
4.4.3 内部 Bean
- 如果 car Bean 只被 boss Bean 引用而不被容器中任何其他的 Bean引用,则可以将 car 以内部的方式注入到 Boss 中
<bean id="boss" class="com.baobaotao.ditype.Boss">
<property name="car">
<bean class="com.baobaotao.ditype.Car">
<property name="maxSpeed" value="200"></property>
<property name="price" value="2000.0"></property>
</bean>
</property>
</bean>
- 内部 Bean 即使提供了 id、name、scope 属性,也会被忽略。即不能被其他 Bean 引用,只能在声明除为外部Bean提供实例注入
4.4.4 null值
- 对 car 的 brand 属性注入一个 null 值
<bean id="car" class="com.baobaotao.ditype.Car">
<property name="brand">
<null/>
</property>
</bean>
4.4.5 级联属性
package com.baobaotao.ditype;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Boss {
...
private Car car = new Car();
}
<bean id="boss" class="com.baobaotao.ditype.Boss">
<property name="car.brand" value="吉利CT50"></property>
</bean>
- Spring 没有对级联属性的层级数进行限制,只要配置的 Bean拥有对应于级联属性的类结构,就可以配置任意层级的级联属性;定义了具有三级结构的级联属性。
4.4.6 集合类型属性
java.util 包中的集合类是最常用的数据结构类型,主要包括 List、Set、Map、Properties,Spring 为这些集合类型属性提供了专门的配置元素标签
- List、Set、Map、Properties 示例
package com.baobaotao.ditype;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Boss {
private List<String> favorites = new ArrayList<String>();
private Map<String, String> jobs = new HashMap<String, String>();
private Set<String> hashSet = new HashSet<String>();
private Properties mails = new Properties();
}
<bean id="boss" class="com.baobaotao.ditype.Boss">
<property name="favorites">
<list>
<value>看报</value>
<value>赛车</value>
<value>高尔夫</value>
</list>
</property>
<property name="jobs">
<map>
<entry>
<key><value>AM</value></key>
<value>会见客户</value>
</entry>
<entry>
<key><value>PM</value></key>
<value>公司内部会议</value>
</entry>
</map>
</property>
<property name="hashSet">
<set>
<value>牛</value>
<value>娜</value>
</set>
</property>
<property name="mails">
<props>
<prop key="jobMail">john-office@baobaotao.com</prop>
<prop key="lifeMail">john-life@baobaotao.com</prop>
</props>
</property>
</bean>
- 集合合并
<bean id="parentBoss" abstract="true" class="com.baobaotao.ditype.Boss">
<property name="favorites">
<list>
<value>看报</value>
<value>赛车</value>
<value>高尔夫</value>
</list>
</property>
</bean>
<bean id="chiIdBoss" parent="parentBoss">
<property name="favorites">
<list merge="true">
<value>爬山</value>
<value>游泳</value>
</list>
</property>
</bean>
通过 util 命名空间配置集合类型的 Bean
- 如果你希望配置一个集合类型的 Bean,而非一个集合类型的属性,则可以通过 util 命名空间进行配置。首先需要在 Spring 配置文件头中引入 util 命名空间的声明
<?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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd">
</beans>
<util:list id="favoriteList1" list-class="java.util.LinkedList">
<value>dream1</value>
<value>dream1</value>
<value>dream1</value>
</util:list>
<util:set id="favoriteSet1" set-class="java.util.HashSet">
<value>set1</value>
<value>set2</value>
</util:set>
<util:map id="favoriteMap1" map-class="java.util.HashMap">
<entry>
<key><value>key1</value></key>
<value>value1</value>
</entry>
<entry>
<key><value>key2</value></key>
<value>value2</value>
</entry>
</util:map>
4.4.7 简化配置方式
-
字面值属性
- 字面值属性 < property name=“maxSpeed” value=“200”>
- 构造函数参数 < constructor-arg type=“java.lang.String” value=“红旗CA72”/>
- 集合元素 < map>< entry key=“AM” value=“会见客户”/>
-
引用对象属性
- 字面值属性 < property name=“car” ref=“car”>
- 构造函数参数 < constructor-arg ref=“car”/>
- 集合元素 < map>< entry key-ref=“keyBean” value-ref=“valueBean”/>
-
使用 p 命名空间
<?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:util="http://www.springframework.org/schema/util"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<bean id="car" class="com.baobaotao.ditype.Car"
p:brand="红旗&CaA72"
p:maxSpeed="200"
p:price="20000.0"/>
</beans>
4.4.8 自动装配
< bean>元素提供了一个指定自动装配类型的属性:autowire="<自动装配类型>" Spring 提供了4种自动装配类型
自动装配类型 | 说明 |
---|---|
byName | 根据名称进行自动匹配。假设 Boss 有一个名为 car 的属性,如果容器中刚好有一个名为 car 的 Bean ,Spring 就会自动将其装配给 Boss 的 car 属性 |
byType | 根据类型进行自动匹配。假设 Boss 有一个 Car 类型的属性,如果容器中刚好有一个 Car 类型的 Bean ,Spring 就会自动将其装配给 Boss 的 car 属性 |
constructor | 于 byType 类似,只不过它是针对构造函数注入而言的,如果Boss有一个构造函数,构造函数包含一个 Car 类型的入参,如果容器中有一个 Car 类型的 Bean,则Spring 将自动把这个 Bean 作为 Boss 构造函数的入参,如果容器中没有找到和构造函数入参匹配类型的Bean,Spring 将抛出异常 |
autodetect | 根据 Bean 的自省机制决定采用 byType 还是 constructor 进行自动装配:如果Bean提供了默认的构造函数,则采用 byType;否则采用constructor |
4.5 方法注入
4.5.1 lookup 方法注入
package com.baobaotao.injectfun;
import com.baobaotao.ditype.Car;
public interface MagicBoss {
Car getCar();
}
<bean id="car" class="com.baobaotao.ditype.Car"
p:brand="红旗&CaA72"
p:maxSpeed="200"
p:price="20000.0"
scope="prototype"/>
<bean id="magicBoss" class="com.baobaotao.injectfun.MagicBoss">
<lookup-method name="getCar" bean="car"/>
</bean>
package com.baobaotao.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.baobaotao.ditype.Boss;
import com.baobaotao.ditype.Car;
import com.baobaotao.ditype.CarFactory;
import com.baobaotao.injectfun.MagicBoss;
public class ApplicationBeansTest {
public static void main(String[] args) {
ApplicationContext factory = new ClassPathXmlApplicationContext("beans.xml");
MagicBoss car = (MagicBoss) factory.getBean("magicBoss");
System.err.println(car.getCar());
}
}
4.5.1 方法替换
- 调用方法 使 Boss2 中的 方法 reimplement() 方法,替换 Boos1 中的 getCar() 方法
package com.baobaotao.injectfun;
import com.baobaotao.ditype.Car;
public class Boss1 {
public Car getCar() {
Car car = new Car();
car.setBrand("宝马Z4");
return car;
}
}
package com.baobaotao.injectfun;
import java.lang.reflect.Method;
import org.springframework.beans.factory.support.MethodReplacer;
import com.baobaotao.ditype.Car;
public class Boss2 implements MethodReplacer{
public Object reimplement(Object arg0, Method arg1, Object[] arg2) {
Car car = new Car();
car.setBrand("美洲豹");
return car;
}
}
<bean id="boss1" class="com.baobaotao.injectfun.Boss1">
<replaced-method name="getCar" replacer="boss2"/>
</bean>
<bean id="boss2" class="com.baobaotao.injectfun.Boss2"></bean>
4.6 < Bean >之间的关系
- 不但可以通过 < ref > 引用另一个 Bean,建立起 Bean 和 Bean 之间的依赖关系,相似的,< bean >元素标签之间也可以建立类似的关系,完成一些特殊的功能
4.6.1 继承
- 如果有多个类拥有相同的方法和属性,则我们可以引入一个父类,在父类中定义这些共同的方法和属性,以消除重复的代码。
<!-- 定义为抽象 bean -->
<bean id="abstractCar" class="com.baobaotao.ditype.Car"
p:brand="红旗CaA72"
p:maxSpeed="200"
p:price="20000.0"
p:color="黑色"
scope="prototype"/>
<!-- 继承于 abstractCar -->
<bean id="car3" p:color="红色" parent="abstractCar"/>
<bean id="car4" p:color="白色" parent="abstractCar"/>
4.6.2 依赖
- 一般情况下,可以使用< ref >元素标签建立对其它 Bean 的依赖关系, Spring 负责管理这些 Bean 的关系,当实例化一个 Bean 时, Spring 保证该 Bean 所依赖的其他 Bean 已经初始化。
package com.baobaotao.entity;
public class SystemSettings {
public static int SESSION_TIMEOUT = 30;
public static int REFRESH_CYCLE = 60;
}
package com.baobaotao.entity;
public class SysInit {
public SysInit() {
SystemSettings.SESSION_TIMEOUT = 10;
SystemSettings.REFRESH_CYCLE = 100;
}
}
package com.baobaotao.entity;
import java.util.Timer;
import java.util.TimerTask;
public class CacheManager {
public void cacheManager() {
System.err.println( SystemSettings.REFRESH_CYCLE);
}
}
- Spring 运行用户通过 depends-no 属性指定 Bean 前置依赖的 Bean,签之依赖的 Bean 会在本 Bean 实例化之前传值。如果前置依赖于多个Beans,则可以通过逗号,空格或分号的方式创建 Bean 的名称
<bean id="manager" class="com.baobaotao.entity.CacheManager" depends-on="sysInit" />
<bean id="sysInit" class="com.baobaotao.entity.SysInit" />
4.6.3 引用
- Spring 提供了一个< idref >元素标签,通过该标签引用另一个< bean > 的名字。
<bean id="car" class="com.baobaotao.ditype.Car"
p:color="红色"/>
<bean id="boss" class="com.baobaotao.ditype.Boss" p:car-ref="car" />
4.7 整合多个配置文件
-
方法一:一个 XML 配置文件可以通过 < import> 组合多个外部的配置文件,resource 属性支持 Spring 标准的资源路径
-
beans1.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:util="http://www.springframework.org/schema/util"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<bean id="car1" class="com.baobaotao.ditype.Car"
p:brand="红旗CA72"
p:color="红色"
p:maxSpeed="200"
p:price="20000.0" />
</beans>
- beans2.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:util="http://www.springframework.org/schema/util"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<import resource="classpath:beans1.xml"/>
<bean id="car2" class="com.baobaotao.ditype.Car"
p:brand="红旗CA72"
p:color="红色"
p:maxSpeed="200"
p:price="20000.0"/>
<bean id="boss1" class="com.baobaotao.ditype.Boss"
p:car-ref="car1"/>
<bean id="boss2" class="com.baobaotao.ditype.Boss"
p:car-ref="car2"/>
</beans>
方法二:通过 new String[]{“beans1.xml”,“beans1.xml”}
package com.baobaotao.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.baobaotao.ditype.Boss;
import com.baobaotao.ditype.Car;
public class ApplicationBeansTest {
public static void main(String[] args) {
ApplicationContext factory = new ClassPathXmlApplicationContext(new String[] {"beans1.xml", "beans2.xml"});
Car car1 = (Car) factory.getBean("car1");
Boss boss2 = (Boss) factory.getBean("boss2");
System.err.println(car1);
System.err.println(boss2);
}
}
4.8 Bean 作用域
类 别 | 说 明 |
---|---|
singleton | 在 Spring IoC 容器中仅存在一个 Bean 实例, Bean 以单实例的方式存在 |
prototype | 每次从容器中调用 Bean 时,都返回一个新的实例,即每次调用getBean()时,相当于执行 new XxxBean() 的操作 |
request | 每次 HTTP 请求都会创建一个新的 Bean。该作用域仅适用于 WebApplicationContext环境 |
session | 同一个 HTTP Session 共享一个 |
globalSession | 在 Spring IoC 容器中仅存在一个 Bean 实例, Bean 以单实例的方式存在 |
4.8.1 singleton 作用域
无状态或者状态不可变的类适合使用单实例模式;
- @1 处的 car Bean 声明为 singleton (因为默认是 singleton,所以可以显示不指定),在容器中有 3 个其他的 Bean 引用了 car Bean,如 @2、@3、@3所示。在容器内部 boss1、boss2、boss3 的 car 属性都指向同一个Bean
- 任何通过容器的 getBean(“car”) 方法返回的实例也指向同一个 Bean.
<bean id="car" class="com.baobaotao.ditype.Car"
p:color="红色" scope="singleton"/> @1
<bean id="boss1" class="com.baobaotao.ditype.Boss" p:car-ref="car" /> @2
<bean id="boss2" class="com.baobaotao.ditype.Boss" p:car-ref="car" /> @3
<bean id="boss3" class="com.baobaotao.ditype.Boss" p:car-ref="car" /> @4
- Spring 的 ApplicationContext 容器在启动时,自动实例化所有singleton 的 Bean 并缓存于容器中。好出有二
- 提前实例化,及早发现一些潜在的配置问题
- Bean 以缓存的方式保存,当使用到该 Bean 时就无需再实例化,加快了效率。
- 如果用户不希望再容器启动时提前实例化 singleton 的 Bean,可以通过lazy-init 属性控制
- 当该 Bean 被其他需要提前实例化的 Bean 引用到,Spring 也将忽略延迟实例化的设置
<bean id="boss1" class="com.baobaotao.ditype.Boss" p:car-ref="car" lazy-init="true"/>
4.8.2 prototype 作用域
- boss1、boss2、boss3 所引用的都是一个新的 car 实例,每次通过容器的 getBean(“car”) 方法返回的也是新的Car 实例
- 默认Spring 容器启动时不实例化 prototype 的 Bean。此外,Spring 容器将 prototype 的 Bean 交给调用者后,就不再管理它的生命周期
<bean id="car" class="com.baobaotao.ditype.Car"
p:color="红色" scope="prototype"/> @1
<bean id="boss1" class="com.baobaotao.ditype.Boss" p:car-ref="car" /> @2
<bean id="boss2" class="com.baobaotao.ditype.Boss" p:car-ref="car" /> @3
<bean id="boss3" class="com.baobaotao.ditype.Boss" p:car-ref="car" /> @4
4.8.3 Web 应用环境相关的 Bean 作用域
-
使用 Spring 的 WebApplicationContext,则还可以使用另外 3 种 Bean 的作用域:request、session和 globalSession。当然必须再 Web 容器中进行额外的配置。
-
Web 容器中进行配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
...
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
</web-app>
- 再整合 Spring 容器时使用 ContextLoaderListener,它实现了 ServletContextListener 接听接口,ServletContextListener 只负责接听 Web 容器启动和关闭的事件。
- RequestContextListener 实现 ServletReqeustListener 监听器接口,该监听器监听 HTTP 请求事件, Web 服务器接收的每一次请求都会通知该监听器。
- Spring 容器启动和关闭操作由 Web 容器的启动和关闭事件触发,但如果 Spring 容器中的 Bean 需要 request、sessios 和 globalSession 作用域的支持,Spring容器本身就必须获取 Web 容器的 HTTP 请求事件,以 HTTP 请求的事件“驱动” Bean作用域的控制逻辑。
request 作用域
- request 作用域的 Bean 对应一个 HTTP 请求和生命周期
<bean id="car" class="com.baobaotao.ditype.Car" scope="request"/>
- 每次 HTTP 请求调用到 car Bean 时,Spring 容器创建一个新的 car Bean,请求处理完毕后,销毁这个 Bean。
session作用域
<bean id="car" class="com.baobaotao.ditype.Car" scope="session"/>
- car Bean 的作用域横跨整个 HTTP Session, Session 中所有 HTTP 请求都共享一个 car Bean,当 HTTP Session 结束后,实例才被销毁。
globalSession 作用域
<bean id="car" class="com.baobaotao.ditype.Car" scope="globalSession "/>
- globalSession 作用域类似于 session 作用域,不过仅再 Portlet 的 Web 应用中使用。Portlet 规范定义了全局 Session 的概念,它被组成 portlet Web 应用的所有的子 Portet 共享。如果不在 Portet Web 应用环境下,globalSession 自然就等价于 session 作用域了。
4.8.4 作用域依赖问题
<?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:util="http://www.springframework.org/schema/util"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"<!-- 声明 aop Schema -->
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<bean name="car" class="com.baobaotao.ditype.Car" scope="request">
<aop:scoped-proxy/><!-- 创建代理 -->
</bean>
<bean id="boss" class="com.baobaotao.ditype.Boss">
<property name="car" ref="car"></property><!-- 引用 web 相关作用域的 car Bean -->
</bean>
</beans>
- car Bean 是 request 作用域,他被 singleton 作用域的 boss Bean 引用。 为了 boss能够从适当作用域中获取 car Bean 的引用,需要使用 Spring AOP 的配置标签,则需要再文档声明头中定义 aop 命名空间。
- 当 boss Bean 再 Web 环境下,调用 car Bean 时, Spring AOP 将启用动态dialing智能判断 boss Bean 位于那个HTTP请求线程中,并从对应的HTTP请求线程域中获取对应的 car Bean
- boss Bean 的作用域时 singleton,再Spring 容器中始终只有有个实例,而 car Bean 的作用域为request,所以每个调用到 car Bean 的那些HTTP请求都会创建一个car Bean。Spring 通过动态代理技术,能够让 boss Bean 应用到对应HTTP 请求的 car Bean。
4.9 FactoryBean
-
Spring 通过反射机制利用< Bean > 的class 属性指定实现类实例化 Bean。
-
从Spring 3.0 开始,FactoryBean 开始支持泛型,即接口声明改为 FactoryBean< T>的形式;该接口中定义了以下3个接口方法
- T getObject() 返回由FactoryBean 创建的 Bean 实例, 如果 isSingleton() 返回 true,则该实例会放到Spring 容器的单实例缓存池中。
- boolean isSingleton() 确定由 FactoryBean 创建的 Bean的作用域时 singleton 还是prototype;
- Class<?> getObjectType() 返回 FactoryBean 创建 Bean 的类型。
package com.baobaotao.fb;
import org.springframework.beans.factory.FactoryBean;
import com.baobaotao.ditype.Car;
public class CarFactoryBean implements FactoryBean<Car>{
private String carInfo;
public String getCarInfo() {
return carInfo;
}
//@1 接收逗号分割的属性设置信息
public void setCarInfo(String carInfo) {
this.carInfo = carInfo;
}
//@2 实例化 Car Bean
@Override
public Car getObject() throws Exception {
Car car = new Car();
String[] infos = carInfo.split(",");
car.setBrand(infos[0]);
car.setMaxSpeed(Integer.valueOf(infos[1]));
car.setPrice(Double.valueOf(infos[2]));
return car;
}
//@3 返回Car 的类型
@Override
public Class<Car> getObjectType() {
return Car.class;
}
public boolean isSingleton() {
return false;
}
}
<bean name="car" class="com.baobaotao.fb.CarFactoryBean"
p:carInfo="红旗CA72,200,20000.00"/>
package com.baobaotao.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.baobaotao.ditype.Car;
import com.baobaotao.fb.CarFactoryBean;
public class ApplicationBeansTest {
public static void main(String[] args) throws Exception {
ApplicationContext factory = new ClassPathXmlApplicationContext("beans.xml");
//该方法返回 Car Bean对象 getBean("car')
//Car cfb = (Car) factory.getBean("car");
//若希望获取CarFactoryBean 自身的实例 则需再 beanName 前加 ‘&’ getBean("&car')
CarFactoryBean cfb = (CarFactoryBean) factory.getBean("&car");
System.err.println(cfb.getCarInfo());
Car car = cfb.getObject();
System.err.println(car);
System.err.println(cfb.getObjectType());
System.err.println(cfb.isSingleton());
}
}
4.10 基于注解的配置
4.10.1 使用注解定义 Bean
- @Component:
- @Repository:用于对DAO 实现类进行标注
- @Service:用于对Service 实现类进行标注
- @Contoller:用于对Controller 实现类进行标注
4.10.2 使用注解配置信息启动Spring 容器
<?xml version="1.0" encoding="UTF-8"?>
<!-- @1 声明context 命名空间 -->
<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-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- @2 扫描类包以应用注解定义的 Bean -->
<context:component-scan base-package="com.baobaotao.ditype"/>
</beans>
- 在@1 处声明context 命名空间,在@2 处即可通过context 命名空间的 component-scan 的base-package 属性指定一个扫描的基类包,Spring 容器将会扫描这个基类包里的所有类,并从类的注解信息中获取 Bean 的定义信息。
- 如果仅希望扫描特定的类而非基包下的所有类,那么可以使用 resource-pattern 属性过滤出特定的类,如:
<context:component-scan base-package="com.baobaotao" resource-pattern="anno/*.class"/>
- 这里我们将基包设置为 com.baobaotao,默认情况下 resource-pattern 属性的值为 “**/.class”,即基类包里的所有类。这里我们设置为 “anno/.class”,则Spring 仅会扫描基包里 anno 子包中的类。
- 通过resource-pattern 属性仅可以按资源名称对几包中的类进行过滤,如果仅使用resource-pattern,你会发现很多时候它并不满足你的要求,如果仅过滤基类包中实现了XxxService 接口的类或标注了某个特定注解的类等。
- 这些需求可以通过 < context:component-scan >的过滤子元素实现
- < context:include-filter > 表示要包含的目标类,而< context:exclude-filter > 表示要排除在外的目标类。
<context:component-scan base-package="com.baobaotao.ditype" resource-pattern="anno/*.class"/>
<context:component-scan base-package="com.baobaotao">
<context:include-filter type="regex" expression="com\.baobaotao\.anno.*"/>
<context:exclude-filter type="aspectj" expression="com.baobaotao..*Controller+"/>
</context:component-scan>
类 别 | 示例 | 说 明 |
---|---|---|
annotation | com.baobaotao.XxxAnnotation | 所有标注了 XxxAnnotation 的类。该类型采用目标类是否标注了某个注解进行过滤 |
assignable | com.baobaotao.XxxService | 所有继承或扩展 XxxService 的类。该类型采用目标类是否继承或扩展某个特定类进行过滤 |
aspectj | com.baobaotao…*Service+ | 所有类名以 Service 结束的类及继承或扩展他们的类。该类型采用 AspectJ 表达式经写过滤 |
regex | com.baobaotao.anno…* | 所有com.baobaotao.anno 类包下的类。该类型采用正式表达式根据目标类的类名进行过滤 |
custom | com.baobaotao.XxxTypeFilter | 采用 XxxTypeFile 通过代码的方式根据过滤规则。该类必须实现 org.springframework.core.type.TypeFilter 接口 |
4.10.3 自动装配 Bean
- 使用@Autoeired 进行自动注入 其属性 required
package com.baobaotao.anno.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baobaotao.anno.dao.LogDao;
import com.baobaotao.anno.entity.UserBo;
//@1 定义一个Service的Bean
@Service
public class LogonService {
// @2 分别注入 LogDao 及 UserDao 的 Bean
@Autowired(required=false)
private LogDao logDao;
public void logInUser() {
UserBo user = new UserBo();
System.out.println(user);
}
}
- @Autowired 默认按类型匹配的方式,在容器查找匹配的 Bean,当有且仅有一个匹配的 Bean 时,Spring 将其注入到@Autowired 标注的变量中
- 如果容器中没有一个和标注变量类型匹配的 Bean,Spring 容器启动时将包 NoSuchBeanDefinitionException的异常。如果希望Spring 即使找不到匹配的 Bean 完成注入也不要报异常时,那么可以使用 @Autowired(required=false)
使用 @Qualifier 指定注入 Bean 的名称
- 容器中有一个以上匹配的 Bean 时,则可以通过 Qualifier 注解限定 Bean 的名称
package com.baobaotao.anno.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import com.baobaotao.anno.dao.LogDao;
import com.baobaotao.anno.dao.UserDao;
@Service
public class LogonService {
@Autowired(required=false)
private LogDao logDao;
// 注入名为userDao,类型为 UserDao 的 Bean
@Autowired
@Qualifier("userDao")
private UserDao userDao;
}
对类方法进行标注
- @Autowired 可以对成员变量以及方法的入参进行标注
package com.baobaotao.anno.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import com.baobaotao.anno.dao.LogDao;
import com.baobaotao.anno.dao.UserDao;
@Service
public class LogonService {
private LogDao logDao;
private UserDao userDao;
//自动将LogDao 传给方法入参
@Autowired
public void setLogDao(LogDao logDao) {
this.logDao = logDao;
System.out.println("logDao:" +logDao);
}
//自动将名为 userDao 的 Bean 传给方法入参
@Autowired
@Qualifier("userDao")
public void getUserInfo(UserDao userDao) {
this.userDao = userDao;
System.out.println("userDao:" + userDao);
}
//Spring 自动选择匹配入参类型的 Bean 进行注入
@Autowired
public void getUserInfo(UserDao userDao, LogDao logDao) {
this.userDao = userDao;
System.out.println("userDao:" + userDao + " LogDao:" + logDao);
}
// 如果一个方法拥有多个入参,默认情况下,Spring 自动选择匹配入参类型的 Bean 进行注入, Sprig 允许对方法入参标注@Qualifier 以指定注入 Bean 的名称
@Autowired
public void getUserInfo(@Qualifier("userDao")UserDao userDao, LogDao logDao) {
this.userDao = userDao;
System.out.println("userDao:" + userDao + " LogDao:" + logDao);
}
}
对集合类进行标注
- 如果对类中集合类的变量或方法入参进行@Autowired标注,Spring 会将容器中类型匹配的所有Bean 都自动注入进来
package com.baobaotao.anno.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MyComponent {
@Autowired(required=false)
private List<Plugin> plugin;
public List<Plugin> getPlugins(){
return plugin;
}
}
package com.baobaotao.anno.service;
public interface Plugin {
}
package com.baobaotao.anno.service.impl;
import org.springframework.stereotype.Component;
import com.baobaotao.anno.service.Plugin;
@Component
public class OnePlugin implements Plugin{
}
package com.baobaotao.anno.service.impl;
import org.springframework.stereotype.Component;
import com.baobaotao.anno.service.Plugin;
@Component
public class TwoPlugin implements Plugin{
}
4.10.4 Bean 作用范围及生命过程方法
- 通过注解配置的 Bean 和通过 < bean >配置的 Bean 一样,默认的作用范围都是 singleton, Spring为注解配置提供了一个@Scope 的注解, 可通过它显示指定 Bean 的作用范围;
package com.baobaotao.ditype;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Scope("prototype")
@Component
public class Car {
...
}
- 使用< bean >进行配置时,我们可以通过 init-method 和 destory-method 属性指定 Bean 的初始化及容器销毁前执行的方法。
- @PostConstruce 和 @PreDestroy注解,在Spring 中它们相当于 init-method 和 destory-method 属性的功能,不过使用注解时,可以在一个 Bean 中定义多个:
package com.baobaotao.anno.entity;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Boss {
private Car car;
public Boss() {
System.out.println("construce...");
}
@Autowired
public void setCar(Car car) {
System.out.println("execute in setCar");
this.car = car;
}
@PostConstruct
private void init1() {
System.out.println("execute in init1");
}
@PostConstruct
private void init2() {
System.out.println("execute in init2");
}
@PreDestroy
private void destory1() {
System.out.println("execute in destory1");
}
@PreDestroy
private void destory2() {
System.out.println("execute in destory2");
}
}
package com.baobaotao.anno;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
((ClassPathXmlApplicationContext)ctx).destroy();
}
}
- 输出信息
- construce …
- execute in setCar
- execute in init1
- execute in init2
- execute in destory1
- execute in destory2
- 说明 Spring 先调用 Boss 的构造函数示例化 Bean,再执行@Autowired 进行自动注入,然后分别执行标注了 @PostConstruct 的方法,当容器关闭时,则分别执行标注了 @PreDestroy 的方法
4.11 基于 Java 类的配置
4.11.1 使用 Java 类提供 Bean 定义信息
- 普通的 POJO 只要标注 @Configuration 注解,就可以为 Spring 容器提供 Bean 定义的信息了,每个标注了 @Bean 的类方法都相当于提供一个Bean 的定义信息。
package com.baobaotao.conf;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.baobaotao.anno.dao.LogDao;
import com.baobaotao.anno.dao.UserDao;
import com.baobaotao.anno.dao.impl.LogDaoImpl;
import com.baobaotao.anno.dao.impl.UserDaoImpl;
import com.baobaotao.anno.service.LogonService;
//@1 将一个POJO标注为定义 Bean 的配置类
@Configuration
public class AppConf {
//@2 以下两个方法定义了两个 Bean,并提供了 Bean 的示例化逻辑
@Bean
public UserDao userDao() {
return new UserDaoImpl();
}
@Bean
public LogDao logDao() {
return new LogDaoImpl();
}
//@3 定义了 logonService 的 Bean
@Bean
public LogonService logonService() {
LogonService logonService = new LogonService();
//@4 将@2和@3处定义的 Bean 注入到logonService Bean 中
logonService.setLogDao(logDao());
logonService.getUserInfo(userDao(), logDao());
return logonService;
}
}
- @1 处在AppConf 类的定义处标注了 @Configuration 注解,说明这个类可用于为Spring 提供Bean 的定义信息。类的方法处可以标注 @Bean 注解, Bean 的类型由方法返回值类型决定,名称默认和方法名相同,也可通过入参显示指定 Bean 名称,如@Bean(name=“userDao”)。之间在@Bean 所标注的方法中提供 Bean 的实例化逻辑。
- 在@2 处 userDao() 和 logDao() 方法定义了一个 UserDao 和一个 LogDao的 Bean,他们的Bean名称分别是 userDao 和 logDao。 在@3 处,定义了一个logonService Bean,并且在@4处注入@2 处所定义的两个Bean。
package com.baobaotao.conf;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.baobaotao.anno.dao.LogDao;
import com.baobaotao.anno.dao.UserDao;
import com.baobaotao.anno.dao.impl.LogDaoImpl;
import com.baobaotao.anno.dao.impl.UserDaoImpl;
@Configuration
public class DaoConfig {
@Bean
public UserDao userDao() {
return new UserDaoImpl();
}
@Bean
public LogDao logDao() {
return new LogDaoImpl();
}
}
- 由于 @Configuration 注解类本身已经标注了 @Component 注解,所以任何标注了 @Configuration 的类,本身也相当于标注了 @Component,即他们可以像普通的Bean 一样被注入到其他 Bean 中。DaoConfig 标注了 @Configuration 注解后就成为一个Bean,它可以被自动注入 ServiceConfig中:
package com.baobaotao.conf;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import com.baobaotao.anno.service.LogonService;
@Configuration
public class ServiceConfig {
//@1 像普通 Bean 一样注入 DaoConfig
@Autowired
private DaoConfig daoConfig;
public LogonService logonService() {
LogonService logonService = new LogonService();
//@2 像普通 Bean 一样,调用 Bean 相关的方法
logonService.setLogDao(daoConfig.logDao());
logonService.getUserInfo(daoConfig.userDao(), daoConfig.logDao());
return logonService;
}
}
- 调用 daoConfig 的 logDao() 和 userDao() 方法,就相当于将 DaoConfig 配置类中定义的 Bean 注入进来。Spring 会对配置类所有标注@Bean的方法进行AOP增强,将 daoConfig.userDao() 方法时,不是简单的执行 DaoConfig 类中定义的方法逻辑,而是从Spring 容器中返回相应 Bean 的单例。
- 在@Bean 处,添加@Scope 注解以控制Bean 的作用范围。如果在@Bean 处标注了@Scope(“prototype”),则每次调用 daoConfig.logDao() 都会返回一个新的 LogDao Bean:
package com.baobaotao.conf;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import com.baobaotao.anno.dao.LogDao;
import com.baobaotao.anno.dao.impl.LogDaoImpl;
@Configuration
public class DaoConfig2 {
@Scope("prototype")
@Bean
public LogDao logDao() {
return new LogDaoImpl();
}
}
4.11.2 使用基于 Java 类的配置信息启动 Spring 容器
- 直接通过@Configuration 类启动Spring 容器
package com.baobaotao.anno;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.baobaotao.anno.service.LogonService;
import com.baobaotao.conf.AppConf;
public class JavaConfigTest {
public static void main(String[] args) {
/* //测试方法一:
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConf.class);
LogonService logonService = ctx.getBean(LogonService.class);
logonService.printHello();*/
//测试方法二:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(DaoConfig.class);
ctx.register(ServiceConfig.class);
ctx.refresh();
LogonService logonService = ctx.getBean(LogonService.class);
logonService.printHello();
}
}
- 测试二 方法通过代码一个一个注册配置类,也可以通过@Import将多个配置类组装到一个配置类中,这样仅需要注册这个组装好的配置类就可以启动容器了:
package com.baobaotao.conf;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import com.baobaotao.anno.service.LogonService;
@Configuration
@Import(DaoConfig.class)
public class ServiceConfig {
//@1 像普通 Bean 一样注入 DaoConfig
@Autowired
private DaoConfig daoConfig;
@Bean
public LogonService logonService() {
LogonService logonService = new LogonService();
//@2 像普通 Bean 一样,调用 Bean 相关的方法
logonService.setLogDao(daoConfig.logDao());
logonService.getUserInfo(daoConfig.userDao(), daoConfig.logDao());
return logonServ
}
}
- 通过 XML 配置文件引用 @Configuration 的配置
<?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-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- @1 通过上下文扫描加载到 AppConf 的配置类 -->
<context:component-scan base-package="com.baobaotao.anno"
resource-pattern="AppConf.class"/>
</beans>
- 通过 Configuration 配置类引用 XML 配置信息
<bean id="logDao" class="com.baobaotao.anno.dao.impl.LogDaoImpl"/>
<bean id="userDao" class="com.baobaotao.anno.dao.impl.UserDaoImpl"/>
package com.baobaotao.conf;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import com.baobaotao.anno.dao.LogDao;
import com.baobaotao.anno.dao.UserDao;
import com.baobaotao.anno.service.LogonService;
@Configuration
@ImportResource("classpath:beans2.xml")
public class LogonAppConfig {
@Bean
@Autowired
public LogonService logonService(UserDao userDao, LogDao logDao) {
LogonService logonService = new LogonService();
logonService.setLogDao(logDao);
logonService.getUserInfo(userDao, logDao);
return logonService;
}
}
4.12 不同配置方式比较
基于 XML 配置 | 基于注解配置 | 基于Java 类配置 | |
---|---|---|---|
Bean 定义 | 在 XML 文件中同通过 < bean > 元素定义 Bean 如:< bean class=“com.hm.UserDao” > | 在 Bean 实现类处通过标注 @component 或者衍型类(@Repository、@Service和@Controller)定义 Bean | 在标注了 @Configuration 的Java类中,通过在类方法上标注 @Bean 定义一个Bean。方法必须提供 Bean 的实例化逻辑 |
Bean 名称 | 通过 < bean > 的id活name属性定义,如:< bean id=“userDao” class=“com.hm.UserDao” > 默认名称为:com.hm.UserDao#0 | 通过注解的 value 属性定义,如 @Component(“userDao”)。默认名称为小写字母打头的类名(不带包名): userDao | 通过@Bean 的 name 属性定义,如 @Bean(“userDao”),默认名称为方法名 |
Bean 注入 | 通过 < property > 子元素或通过 p 命名空间的动态属性,如 p:userDao-ref="userDao"进行注入 | 通过在成员变量或方法入参处标注 @Autowired,按类型匹配自动注入。还可以配合使用 @Qualifief 按名称匹配方法注入 | 比较灵活,可以通过在方法处通过 @Autowired 使方法入参绑定 Bean,然后在方法中通过代码注入,还可以同通过调用配置类的 @Bean 方法进行注入 |
Bean 生命过程方法 | 通过 < bean > 的 init-method 和 destroy-method 属性指定 Bean 实现类的方法名。最多只能指定一个初始化反法和一个销毁方法 | 通过在目标方法上标注 @PostConstruce 和 @preDestroy 注解指定初始化或销毁方法,可以定义任意多个方法 | 通过 @Bean 的 initMethod 或 destoryMethod 指定一个初始化或销毁方法 对于初始化方法来说,可以直接在方法内部通过代码的方式灵活定义初始化逻辑 |
Bean 作用范围 | 通过 < bean > 的 scope 属性指定,如:< bean class=“com.hm.UserDao” scope=“prototype” > | 通过在类定义处标注 @Scope 指定,如 @Scope(“prototype”) | 通过在Bean方法定义处标注 @Scope 指定 |
Bean 延迟初始化 | 通过 < bean > 的 lazy-init 属性指定,默认为 default,继承于 < beans > 的default-lazy-init 设置,该值默认为 false | 通过在类定义处标注 @Lazy 指定,如 @Lazy(true) | 通过在 Bean 方法定义处标注 @lazy 指定 |