目录
Spring 是什么
Spring 框架是一个功能强大的开源框架,主要用于简化企业级 Java 应用程序的开发。它提供了一套全面的基础架构,帮助开发者解决常见的编程问题,如依赖注入、面向方面编程(AOP)、事务管理和数据访问等。Spring 的核心是其灵活的依赖注入机制,这使得应用程序的各个组件可以更加松散地耦合,从而提高代码的可维护性和可测试性。
Spring 6 是 Spring 框架的最新版本,进一步优化了企业级 Java 应用的开发。它集成了最新的 Java 和 Jakarta EE 规范,支持 Java 17 及以上版本,提升了性能和安全性。Spring 6 还增强了对模块化架构的支持,通过更加简洁和现代化的编程模型,简化了开发流程。同时,它引入了对 GraalVM 和原生镜像的支持,使得应用启动更快、占用资源更少,为构建云原生应用提供了强有力的支持。
要使用最基础的 Spring 的功能,需要引入以下依赖,由于我的项目中引入了 SpringBoot 做自动版本仲裁,所以这里我不用添加版本,如果需要指定版本,可以去中央仓库查询或查看官网:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
IoC 容器
概述
IoC(控制反转,Inversion of Control)是一种设计原则,用于减少代码之间的耦合性。它指的是将对象的创建和依赖的管理交由外部容器负责,而不是在类内部直接实例化对象或管理其依赖。IoC 容器就是 IoC 思想的一种落地实现。在 Spring 框架中,IoC 主要通过 依赖注入 (DI,Dependency Injection) 实现。
创建对象的过程
IoC 容器创建对象的过程简单来说,就是通过各种方式(例如注解、配置文件)告诉 Spring 容器如何创建和管理一个对象,然后 Spring 容器帮你把这个对象创建并保存起来。当你需要使用这个对象时,再从 Spring 容器中把对象取出来,这个过程就是 DI。
首先,如果想让 Spring 容器帮你创建一个对象,Spring 容器需要知道关于这个对象的信息,例如最基本的,需要知道这个对象属于哪个类。对象的信息,抽象成 Java 中的接口,就是:
org.springframework.beans.factory.config.BeanDefinition
有了对象信息,还不够,Spring 容器还要知道怎么得到这些类的信息(如 XML、注解、Java 配置),这些信息我们称为元信息。元信息最终会将其转换为 BeanDefinition 对象,供 Spring 容器使用,整个读取和加载的过程抽象成 Java 中的接口,就是:
org.springframework.beans.factory.support.BeanDefinitionReader
BeanDefinitionReader 有多个实现类,这些实现类定义了不同的读取元信息并加载 BeanDefinition 的方式:
Spring IoC 容器创建对象的过程中有很多细节,简单来说,容器首先会通过 BeanDefinitionReader 将不同来源的元信息转换成 BeanDefinition 对象(BeanDefinition 是可以有多个的,取决于你元信息的配置),然后将这些 BeanDefinition 对象注册到一个 BeanDefinitionRegistry 对象中,你可以认为 BeanDefinitionRegistry 对象就是一个保存 BeanDefinition 的容器。
org.springframework.beans.factory.support.BeanDefinitionRegistry
将 BeanDefinition 保存到 BeanDefinitionRegistry 后,容器会根据 BeanDefinitionRegistry 中保存的 BeanDefinition 信息,利用反射机制获取 Bean 的 Class 对象,然后创建对象,该对象最终会保存到一个 Map 中,变成一个 Spring Bean。
容器接口
所谓的 Spring 容器,最根本的接口其实是 BeanFactory,既然是一个容器,那么肯定需要从容器中获取一些东西,BeanFactory 就定义了一系列从容器中获取对象的方法。
org.springframework.beans.factory.BeanFactory
由于 BeanFactory 是最底层的接口,因此开发者一般不会直接使用,而是使用继承了 BeanFactory 的一些类或接口,例如 ApplicationContext,ApplicationContext 主要有以下的一些实现类:
类型名 | 简介 |
---|---|
ClassPathXmlApplicationContext | 通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象 |
FileSystemXmlApplicationContext | 通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象 |
ConfigurableApplicationContext | ApplicationContext 的子接口,包含一些扩展方法 refresh() 和 close() ,让 ApplicationContext 具有启动、关闭和刷新上下文的能力。 |
WebApplicationContext | 专门为 Web 应用准备,基于 Web 环境创建 IOC 容器对象,并将对象引入存入 ServletContext 域中。 |
而其中最常用的就是 ClassPathXmlApplicationContext。
org.springframework.context.support.ClassPathXmlApplicationContext
以下是 ApplicationContext 的继承关系,由于层级过多,只展示了一部分:
基于 XML 管理 bean
基于 XML 管理 Bean,就是通过一个 XML 的配置文件告诉 Spring IoC 容器如何管理 Bean,可以使用 bean 标签定义一个bean,一个最基本的 Spring 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="helloWorld" class="com.zhumingjian.spring.entity.HelloWorld">
<property name="message" value="Hello, Spring!"/>
</bean>
</beans>
上面这个 XML 配置文件,告诉容器创建一个 HelloWorld 类的对象,并把这个对象的 message 实例变量赋值为 ”Hello Spring“,HelloWorld 类的代码如下:
public class HelloWorld {
private String message;
public void setMessage(String message) {
this.message = message;
}
public void getMessage() {
System.out.println("Your Message: " + message);
}
}
接下来需要创建 Spring 容器并告诉容器 XML 文件在哪:
public class Application {
public static void main(String[] args) {
//创建一个 Spring IoC 容器,告诉容器 XML 使用类路径下的 application.xml 文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
}
}
获取 bean
根据 id 获取
由于在配置文件中的 bean 标签指定了 id 属性,所以可以根据 id 属性可以精确获取到一个 bean 对象
HelloWorld bean = (HelloWorld) context.getBean("helloWorld");
根据类型获取
当根据类型获取 bean 时,要求 IOC 容器中指定类型的 bean 有且只能有一个,否则会抛出 NoUniqueBeanDefinitionException 异常
HelloWorld bean = context.getBean(HelloWorld.class);
根据类型和 id 获取
当根据类型和 id 获取时,只要保证容器中指定 id 的对象是指定类型的就可以了
HelloWorld bean = context.getBean("helloWorld", HelloWorld.class);
结论
根据类型来获取 bean 时,在满足 bean 唯一性的前提下,其实只是看:『对象 instanceof 指定的类型』的返回结果,只要返回的是true 就可以认定为和类型匹配,能够获取到。
java 中,instanceof 运算符用于判断前面的对象是否是后面的类,或其子类、实现类的实例。如果是返回 true,否则返回 false。也就是说:用 instanceof 关键字做判断时, instanceof 操作符的左右操作必须有继承或实现关系。
依赖注入
所谓的依赖,可以简单理解为,一个类 A,需要用到另一个类 B 的实例,这时候 A 就对 B 产生了依赖。依赖注入就是容器在创建 A 对象的时候,将 B 对象的实例设置给 A,这样 A 就能用到 B 的东西了。
要实现这个需求,有两种方式,分别是通过 setter 方法和 构造器的方式注入,下面通过一个案例分别演示这两种注入方式,首先我们需要准备以下的类:
package com.zhumingjian.spring.entity;
public class Student {
private Integer id;
private String name;
private Integer age;
private String sex;
public Student() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Student(Integer id, String name, Integer age, String sex) {
this.id = id;
this.name = name;
this.age = age;
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}
setter 注入
要通过 setter 方法注入依赖,首先保证注入的属性有 setter 方法,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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="com.zhumingjian.spring.entity.Student">
<!-- property标签:通过组件类的setXxx()方法给组件对象设置属性 -->
<!-- name属性:指定属性名(这个属性名是getXxx()、setXxx()方法定义的,和成员变量无关) -->
<!-- value属性:指定属性值 -->
<property name="id" value="1001"/>
<property name="name" value="张三"/>
<property name="age" value="23"/>
<property name="sex" value="男"/>
</bean>
</beans>
测试:
public class Application {
public static void main(String[] args) {
//创建一个 Spring IoC 容器,告诉容器 XML 使用类路径下的 application.xml 文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
Student bean = context.getBean(Student.class);
//输出:Student{id=1001, name='张三', age=23, sex='男'}
System.out.println(bean);
}
}
构造器注入
要使用构造器注入,首先要保证有正确的构造器,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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- constructor-arg 标签表示构造器的参数,其中 value 表示值,index 表示参数所在构造器的索引 -->
<bean id="student" class="com.zhumingjian.spring.entity.Student">
<constructor-arg value="1002" index="0"/>
<constructor-arg value="李四" index="1"/>
<constructor-arg value="33" index="2"/>
<constructor-arg value="女" index="3"/>
</bean>
</beans>
测试:
public class Application {
public static void main(String[] args) {
//创建一个 Spring IoC 容器,告诉容器 XML 使用类路径下的 application.xml 文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
Student bean = context.getBean(Student.class);
//输出:Student{id=1002, name='李四', age=33, sex='女'}
System.out.println(bean);
}
}
属性注入
如果一个类,没有 setter 方法,也没有对应参数的构造器,那么 Spring 就会尝试用反射机制获取字段,并赋值。
特殊值处理
null 值
如果要声明一个变量为 null,不能直接在 value 那写null,否则 Spring 会把它解析为字面量的null,应该使用以下方式:`
<property name="name">
<null />
</property>
xml 实体
如果遇到一些特殊的符号,例如 < 和 > ,这些符号和 XML 语法冲突了,这时候要使用替代的符号,例如:
<!-- 小于号在XML文档中用来定义标签的开始,不能随便使用 -->
<!-- 解决方案一:使用XML实体来代替 -->
<property name="expression" value="a < b"/>
CDATA 节
CDATA 也是解决 XML 语法冲突的方法之一,CDATA节中写什么符号都随意:
<property name="expression">
<!-- 解决方案二:使用CDATA节 -->
<!-- CDATA中的C代表Character,是文本、字符的含义,CDATA就表示纯文本数据 -->
<!-- XML解析器看到CDATA节就知道这里是纯文本,就不会当作XML标签或属性来解析 -->
<!-- 所以CDATA节中写什么符号都随意 -->
<value><![CDATA[a < b]]></value>
</property>
注入自定义对象
首先需要准备以下几个对象,其中 UserInfoController 依赖 UserService:
// 用户接口
public interface UserService {
String getUserInfo();
}
// 用户接口实现
public class UserServiceImpl implements UserService {
@Override
public String getUserInfo() {
return "用户信息";
}
}
// 用户 Controller,在这个类中会使用 UserService,形成依赖
public class UserInfoController {
private UserService userService;
// setter方法注入方式
public void setUserService(UserService userService) {
this.userService = userService;
}
public UserInfoController(UserService userService) {
this.userService = userService;
}
public UserInfoController() {
}
public void test() {
System.out.println(userService.getUserInfo());
}
}
引用外部 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">
<!--由于 UserInfoController 会用到 UserService,所以需要先创建 UserService 对象-->
<bean id="userService" class="com.zhumingjian.spring.service.impl.UserServiceImpl"/>
<!--创建 UserInfoController 对象-->
<bean id="userInfoController" class="com.zhumingjian.spring.controller.UserInfoController">
<!--将 UserService 引用给 UserInfoController-->
<property name="userService" ref="userService"/>
<!--或使用构造器注入-->
<!--<constructor-arg name="userService" ref="userService"></constructor-arg>-->
</bean>
</beans>
内部 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">
<!--创建 UserInfoController 对象-->
<bean id="userInfoController" class="com.zhumingjian.spring.controller.UserInfoController">
<property name="userService">
<!-- 在一个 bean 中再声明一个 bean 就是内部 bean -->
<!-- 内部 bean 只能用于给属性赋值,不能在外部通过 IOC 容器获取,因此可以省略id属性 -->
<bean class="com.zhumingjian.spring.service.impl.UserServiceImpl"/>
</property>
</bean>
</beans>
注入集合类型
准备一个测试类,只有简单的几个集合变量和对应的 setter 方法:
import java.util.*;
public class CollectionExample {
private List<String> stringList;
private Set<Integer> integerSet;
private Map<String, Double> doubleMap;
public CollectionExample() {
}
public void printCollections() {
System.out.println("List:");
for (String str : stringList) {
System.out.println(str);
}
System.out.println("\nSet:");
for (Integer num : integerSet) {
System.out.println(num);
}
System.out.println("\nMap:");
for (Map.Entry<String, Double> entry : doubleMap.entrySet()) {
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
}
public void setStringList(List<String> stringList) {
this.stringList = stringList;
}
public void setIntegerSet(Set<Integer> integerSet) {
this.integerSet = integerSet;
}
public void setDoubleMap(Map<String, Double> doubleMap) {
this.doubleMap = doubleMap;
}
}
bean 内部注入方式
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="collectionExample" class="com.zhumingjian.spring.entity.CollectionExample">
<property name="stringList">
<list>
<value>Apple</value>
<value>Orange</value>
<value>Banana</value>
</list>
</property>
<property name="integerSet">
<set>
<value>1</value>
<value>2</value>
<value>3</value>
</set>
</property>
<property name="doubleMap">
<map>
<entry key="John" value="100.0"/>
<entry key="Doe" value="200.0"/>
<entry key="Jane" value="300.0"/>
</map>
</property>
</bean>
</beans>
测试:
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
CollectionExample bean = context.getBean(CollectionExample.class);
bean.printCollections();
}
输出结果:
List:
Apple
Orange
Banana
Set:
1
2
3
Map:
John -> 100.0
Doe -> 200.0
Jane -> 300.0
util 方式
使用 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.xsd
http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
<!-- 声明一个字符串的List -->
<util:list id="stringList">
<value>Apple</value>
<value>Orange</value>
<value>Banana</value>
</util:list>
<!-- 声明一个整数的Set -->
<util:set id="integerSet">
<value>1</value>
<value>2</value>
<value>3</value>
</util:set>
<!-- 声明一个键为字符串、值为双精度浮点数的Map -->
<util:map id="doubleMap">
<entry key="John" value="100.0"/>
<entry key="Doe" value="200.0"/>
<entry key="Jane" value="300.0"/>
</util:map>
<!-- 声明 CollectionExample 的 bean,并注入集合数据 -->
<bean id="collectionExample" class="com.zhumingjian.spring.entity.CollectionExample">
<property name="stringList" ref="stringList"/>
<property name="integerSet" ref="integerSet"/>
<property name="doubleMap" ref="doubleMap"/>
</bean>
</beans>
测试:
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
CollectionExample bean = context.getBean(CollectionExample.class);
bean.printCollections();
}
输出结果:
List:
Apple
Orange
Banana
Set:
1
2
3
Map:
John -> 100.0
Doe -> 200.0
Jane -> 300.0
P 命名空间
P命名空间是Spring提供的一种简化配置的方式,首先准备一个类:
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// Getter 和 Setter 方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
// toString 方法,用于输出对象信息
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
要使用 P 命名空间,要引入特定的命名空间,最后的配置文件如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 使用P命名空间声明一个Person的bean,并注入name和age -->
<bean id="person" class="Person"
p:name="John"
p:age="30" />
</beans>
测试:
public static void main(String[] args) {
// 加载 Spring 配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 获取 Person 对象
Person person = (Person) context.getBean("person");
// 输出 Person 对象信息
System.out.println(person);
}
引入外部文件注入
Spring 允许你将外部的配置文件引入,以配置数据库连接池为例,首先准备一个外部的配置文件:
jdbc.user=root
jdbc.password=123456
jdbc.url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC
jdbc.driver=com.mysql.cj.jdbc.Driver
Spring 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: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
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 引入外部属性文件 -->
<context:property-placeholder location="classpath:datasource.properties"/>
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
自动装配
在 Spring 框架中,自动装配(Autowiring)是一个强大的特性,它允许 Spring 容器在不需要显式指定依赖关系的情况下,自动将 bean 装配到一起。
在 Spring 的 XML 配置中,自动装配可以通过 bean 标签的 autowire 属性来实现。autowire 属性可以设置为以下几种模式之一:
- byName(按名称自动装配):Spring 容器查找与属性名相匹配的 bean 的 id。
- byType(按类型自动装配):Spring 容器查找与属性类型相匹配的 bean。如果存在多个相同类型的bean,则会抛出异常,因为它无法决定使用哪一个 bean。
- constructor(构造函数自动装配):类似于 byType,但用于构造函数参数。如果类有多个构造函数,且它们都是可接受的(即,具有相同数量的参数且每个参数的类型都匹配一个 bean),则可能会抛出异常。
- default(默认自动装配):这个值允许你指定自动装配的默认行为,但它本身并不启用自动装配。它实际上让子 beans 标签可以覆盖这个设置。
- no(不自动装配):这是默认设置,表示不进行自动装配。显式地设置这个值可以清晰地表明你不想使用自动装配。
以下是一个简单的配置例子:
<bean id="userInfoController" class="com.zhumingjian.spring.controller.UserInfoController" autowire="byType"/>
<bean id="userService" class="com.zhumingjian.spring.service.impl.UserServiceImpl"/>
bean 作用域
在 Spring 中可以通过配置 bean 标签的 scope 属性来指定 bean 的作用域范围,各取值含义参加下表:
取值 | 含义 | 创建对象的时机 |
---|---|---|
singleton(默认) | 在 IOC 容器中,这个 bean 的对象始终为单实例 | IOC 容器初始化时 |
prototype | 这个 bean 在 IOC 容器中有多个实例 | 获取 bean 时 |
如果是在 WebApplicationContext 环境下还会有另外几个作用域(但不常用):
取值 | 含义 |
---|---|
request | 在一个请求范围内有效 |
session | 在一个会话范围内有效 |
bean 生命周期
基本流程
- 实例化:当 Spring 容器接收到 bean 的定义后,通过构造函数创建 bean 的实例。
- 依赖注入:在实例化 bean 后,Spring 框架通过依赖注入将相关的依赖注入到 bean 中,包括属性注入、构造函数注入或方法注入。
- 调用后置处理器(初始化之前)
- 初始化方法调用:如果 bean 实现了 InitializingBean 接口,或者在配置文件中使用 init-method 指定了初始化方法,Spring 容器在依赖注入完成后调用这些方法进行一些初始化操作。
- 调用后置处理器(初始化之后)
- 使用:此时bean已经可以正常使用了,它会被注入到其他bean中,或者被容器中的其他组件使用。
- 销毁前回调方法:当 bean 不再需要时,Spring 容器会调用预定义的方法来释放资源,这些方法可以是实现 DisposableBean 接口的 destroy() 方法,或者在配置文件中使用 destroy-method 指定的方法。
后置处理器
bean 的后置处理器会在生命周期的初始化前后添加额外的操作,需要实现 BeanPostProcessor 接口,且配置到 IoC 容器中,需要注意的是,bean 后置处理器不是单独针对某一个 bean 生效,而是针对 IoC 容器中所有bean 都会执行
创建 bean 的后置处理器:
public class MyBeanProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("☆☆☆" + beanName + " = " + bean);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("★★★" + beanName + " = " + bean);
return bean;
}
}
将后置处理器注册到容器中:
<!-- bean的后置处理器要放入IOC容器才能生效 -->
<bean id="myBeanProcessor" class="com.zhumingjian.spring.processor.MyBeanProcessor"/>
FactoryBean
当 Bean 的创建过程比较复杂时,可以通过 FactoryBean 来实现复杂的创建过程,隐藏实现细节,使使用者无需关心 Bean 的创建过程。
org.springframework.beans.factory.FactoryBean<T>
配置一个 FactoryBean 类型的 bean,在获取 bean 的时候得到的并不是 class 属性中配置的这个类的对象,而是 getObject() 方法的返回值。通过这种机制,Spring 可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。
以下是一个简单的例子,首先编写一个 FactoryBean:
public class StudentFactoryBean implements FactoryBean<Student> {
@Override
public Student getObject() throws Exception {
return new Student();
}
@Override
public Class<?> getObjectType() {
return Student.class;
}
}
然后将其注册到容器中:
<bean id="student" class="com.zhumingjian.spring.factory.StudentFactoryBean"/>
最后获取对象:
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
Student bean = context.getBean(Student.class);
}