Spring IoC容器进阶(1)
学习《Spring+Spring MVC+Mybatis整合开发实战》笔记
前言
- 除了id和class属性外,可以设置Bean的更多属性定义Bean的特性和行为,包括作用域属性(scope)、懒加载属性(lazy-init)、**初始化和销毁方法(init-method、destroy-method)**等。
- Bean的依赖注入类型除了一般的Java对象类型之外,也可以是简单类型或集合类型。
- 同一个Bean注入多个依赖,可以指定匹配的规则和顺序。
- 容器和Bean的扩展点,可以在Bean的不同生命周期进行功能的扩展。
一、Bean实例化的更多方式
Spring允许配置Bean类的静态内部类来初始化该内部类实例,也可以使用Bean类的静态工厂方法和实例工厂方法进行实例化。
1.1 静态内部类
静态内部类是在类的内部定义的,并且使用static关键字修饰的类,可以使用 外部类名.静态内部名 的方式进行访问。
例:一个内部类定义如下:
public class OuterClass{
static class InnerClass{
public void innerMethod(){
System.out.println("This is InnerClass's InnerMethod")
}
}
}
则可以通过OuterClass.InnerClass来调用内部类。
如果使用Spring容器来初始化和管理该内部类实例,与代码中使用 “.” 不同,Bean的配置使用 “$” 进行连接,配置文件中配置应如下:
<bean id="innerObject" class="first.second.OuterClass$InnerClass"/>
1.2 静态工厂与实例工厂方法
静态工厂方法使用该类的一个静态方法返回该类的唯一静态对象。这个静态对象在整个应用中是唯一的,在类加载的时候产生,使用“类名.静态方法”的方式引用。
实例工厂方法将目标类实例的创建放在一个方法中,通过配置工厂类的Bean实例和方法得到目标实例。在现实中,工厂类一般用来对多个不同的目标类实例化,对应不同的实例化方法。下面是静态工厂与实例工厂方法
例: 定义一个Apple类:
public class Apple {
private String color;
private String taste;
public Apple(String color,String taste){
super();
this.color = color;
this.taste = taste;
}
public void setColor(String color){
this.color = color;
}
public void setTaste(String taste){
this.taste = taste;
}
public String getColor(){
return color;
}
public String getTaste(){
return taste;
}
public String toString(){
return "Apple [color=" + color + ",taste=" + taste + "]";
}
}
一个包含静态方法的类staticFactory:
import java.util.HashMap;
import java.util.Map;
public class staticFactory {
public static Map<String, Apple> apples = new HashMap<String, Apple>();
static {
apples.put("red", new Apple("red","good"));
apples.put("green",new Apple("green","bad"));
}
public static Apple getApple(String name){
return apples.get(name);
}
}
一个实例工厂类instanceFactory:
import java.util.HashMap;
import java.util.Map;
public class instanceFactory {
private Map<String, Apple> apples = null;
public instanceFactory(){
apples = new HashMap<String,Apple>();
apples.put("red",new Apple("red","good"));
apples.put("green",new Apple("green","bad"));
}
public Apple getApple(String color){
return apples.get(color);
}
}
Bean的配置:(除了需要指定Bean的id和class属性外,还要使用factory-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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="apple1" class="org.example.Spring4.factory.staticFactory" factory-method="getApple">
<constructor-arg value="red"></constructor-arg>
</bean>
<bean id="appleFactory" class="org.example.Spring4.factory.instanceFactory"/>
<bean id="apple2" factory-bean="appleFactory" factory-method="getApple">
<constructor-arg value="green"></constructor-arg>
</bean>
</beans>
测试
package org.example.Spring4.factory;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class FactoryDemo {
@Test
public static void main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext("spring1_Factory.xml");
Apple apple1 = (Apple) context.getBean("apple1");
Apple apple2 = (Apple) context.getBean("apple2");
System.out.println("--------静态工厂方法的实例结果--------");
System.out.println(apple1.toString());
System.out.println("--------实例工厂方法的实例结果--------");
System.out.println(apple2.toString());
}
}
运行结果如下: 
二、Bean的配置属性
在< bean>的标签元素中,除了class和id属性,还可以设置更多的属性来进行个性化设定。
- 使用scope属性指定Bean的作用域范围;
- 使用init-method和destroy-method指定该Bean在初始化和销毁时回调的方式;
- 使用lazy-init声明懒加载,推迟该Bean初始化以加快容器初始化的速度;
- 使用parent属性从父Bean继承配置数据。
2.1 Bean的作用域配置
作用域,即对象和变量的可见范围,在Spring中指当前配置创建的Bean相对于其他Bean的可见范围。scope属性用来进行Bean作用域配置,可配置6种类型的作用域:
- singleton(单例作用域):Spring IoC容器只创建和维护一个该类型的Bean实例,并将这个实例存储到单例缓存中,针对该Bean的请求和引用,使用的都是同一个实例。从容器启动或第一次调用实例化开始,只要容器没有退出或销毁,该类型的单一实例就会一直存活。
- prototype(原型作用域):原型作用域的Bean在使用容器的getBean方法获取时,每次得到的都是一个新的对象。容器不负责原型作用域Bean实例的完整生命周期,在初始化或装配完该Bean的类对象后,容器就不再对该对象进行管理。
- request (请求作用域):针对每次HTTP请求,Spring都会创建一个Bean实例。
- session (会话作用域):使用于HTTP Session,同一个Session共享同一个Bean实例。
- application (应用作用域):整个Web应用,也就是在ServletContext生命周期中使用一个Bean实例。
- websocket:WebSocket是HTML 5支持的一种协议和新特性,websocket作用域的配置是在一个WebSocket连接的生命周期中共用一个Bean实例。
后4种使用在Web应用程序中。
例:两种作用域对比例子 定义一个Book类:
public class Book {
private String title;
private String author;
private double price;
private String description;
public Book(){
}
public Book(String title, String author, double price){
super();
this.title = title;
this.author = author;
this.price = price;
this.description = "";
}
public String getTitle(){
return title;
}
public String getAuthor(){
return author;
}
public double getPrice(){
return price;
}
public void setTitle(String title){
this.title = title;
}
public void setAuthor(String author){
this.author = author;
}
public void setPrice(double price){
this.price = price;
}
public void setDescription(String description){
this.description = description;
}
public String toString(){
return "Book [title="+title+", author="+author+", price="+price+", description:"+description+"]";
}
}
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.xsd">
<bean id="book1" class="org.example.Spring4.scope.Book" scope="singleton">
<property name="title" value="工科数学分析基础 上册"></property>
<property name="author" value="王绵森 马知恩"></property>
<property name="price" value="47.20"></property>
</bean>
<bean id="book2" class="org.example.Spring4.scope.Book" scope="prototype">
<property name="title" value="工科数学分析基础 上册"></property>
<property name="author" value="王绵森 马知恩"></property>
<property name="price" value="47.20"></property>
</bean>
</beans>
测试:
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class scopeDemo {
@Test
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring2_Scope.xml");
System.out.println("--------使用作用域为singleton的Bean--------");
Book book1 = (Book) context.getBean("book1");
Book book1_new = (Book) context.getBean("book1");
book1.setDescription("这是一个singleton作用域的Book实例");
System.out.print("比较两者:");
System.out.println(book1==book1_new);
System.out.println(book1.toString());
System.out.println(book1_new.toString());
System.out.println("\n--------使用作用域为prototype的Bean--------");
Book book2 = (Book) context.getBean("book2");
Book book2_new = (Book) context.getBean("book2");
System.out.print("比较两者:");
System.out.println(book2==book2_new);
book2.setDescription("这是一个prototype作用域的Book实例");
System.out.println(book2.toString());
System.out.println(book2_new.toString());
}
}
运行结果: 
2.2 Bean初始化或销毁的生命周期回调
如果要在Bean实例化时完成更多的初始化操作,而偏偏这些初始化操作又依赖于注入对象,那么仅通过构造函数无法实现这些功能,可以使用回调的方式解决——在容器实例化对象并设置完属性值之后执行自定义初始化方法。销毁方法也类似,在Bean销毁的时候进行一些额外的处理,类似于资源处理或者日志写入。 有三种方式:
- 使用xml配置init-method与destroy-method
- 继承InitializingBean和 DisposableBean回调接口
- 使用注解 @PostConstruct 与 @PreDestroy
以上三种方式可以在同一个类上同时使用,执行顺序依次是注解方式、接口方式、配置方式。
2.3 懒加载Bean
默认情况下,Spring容器会在启动时将所有singleton作用域的Bean实例化。这样可以在配置有误时,提早在容器启动时发现。但这也延缓了大型项目服务启动的速度,影响系统性能。
而设置懒加载可以使得一些不需要在容器启动时就创建的对象推迟到需要使用时才加载与创建。Bean的懒加载可以全局设置,也可以个别设置。
- 全局设置:设置配置文件根元素< beans >的属性default-lazy-init的值。其中,true代表懒加载;不设置或设置false都是非懒加载。全局设置会应用在所有的Bean上,一般较少使用。
- 个别设置:设置每个Bean的lazy-init属性来实现懒加载。其中,default表示从default-lazy-init继承;true表示懒加载;false表示非懒加载。
- 若同时使用两种方式设置Bean的懒加载属性,则个别设置会覆盖全局设置。而对于prototype的Bean,始终是以懒加载方式创建,即使设置lazy-init="false"也要等到使用时才对Bean实例化。
懒加载在使用getBean方法获取该Bean实例时创建。 特例:如果某个懒加载Bean作为依赖注入其他非懒加载Bean时,容器会在初始化时就创建该懒加载Bean的实例。
2.4 Bean定义的继承
Spring支持Bean继承,子Bean从父Bean的定义继承配置数据,用于Bean实例之间的参数值延续,也可覆盖父Bean中的某些定义。Bean的继承类似于模版,可减少配置工作量。
若父Bean仅仅是作为一个配置模版,可以设置父Bean的abstract属性为true,容器就不会实例化这个Bean,此时也可以不指定class属性。
在父Bean指定了class属性的情况下,子Bean可以不指定class属性,这时子Bean使用的就是父Bean的class。
总结
此次学习对静态工厂方法和实例工厂方法、Bean作用域进行了实例观察,理解较纯看书本更加深入。而对于后三种Bean属性还没有提供实例,计划在以后补充。
|