一、创建容器
容器(也称为 IoC 容器或 Bean 容器)的创建是 Spring 框架的核心之一,一般有两种创建方式:基于 XML 配置文件和基于注解。
1、基于 XML 配置文件的容器创建
第一步是加载配置文件。Spring 框架会在类路径下搜索所有以“applicationContext”为前缀并以“.xml”结尾的文件,并将它们作为配置文件加载。在配置文件中,我们需要定义各个 Bean 的名称、类型、属性及其依赖关系。
例如,在配置文件 applicationContext.xml 中,我们可以定义一个名为“userService”的 Bean,该 Bean 的类型为 UserServiceImpl,其依赖于名为“userDao”的另一个 Bean,同时还指定了该 Bean 的初始化方法和销毁方法:
<bean id="userService" class="com.example.UserService">
<property name="userDao" ref="userDao"/>
<property name="name" value="张三"/>
<property name="age" value="18"/>
<init-method init="init"/>
<destroy-method destroy="destroy"/>
</bean>
<bean id="userDao" class="com.example.UserDao"/>
第二步是使用 Spring 提供的 ApplicationContext 接口加载配置文件并创建容器。ApplicationContext 是一个高级容器,它提供了很多丰富的服务,包括国际化支持、事件传播和 AOP 等。
例如,在 Java 代码中,我们可以使用 ClassPathXmlApplicationContext 类加载 applicationContext.xml 文件,并通过getBean()方法获取名为“userService”的 Bean 实例:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
System.out.println(userService.getName());
}
}
2、基于注解的容器创建
第一步是在 Java 代码中通过注解声明 Bean。在 Spring 4.x 版本之后,支持使用注解来声明 Bean,这样可以使得代码更加简洁和易读。
例如,我们可以在 UserServiceImpl 类上使用@Component注解声明其为一个 Bean,并指定该 Bean 的名称为“userService”:
import org.springframework.stereotype.Component;
@Component("userService")
public class UserServiceImpl {
...
}
同时,在 UserDaoImpl 类上也可以使用 @Component 注解声明其为另一个 Bean,并指定该 Bean 的名称为“userDao”:
import org.springframework.stereotype.Component;
@Component("userDao")
public class UserDaoImpl {
...
}
第二步是通过 Java 代码创建容器,并将这些 Bean 注册到容器中。在 Spring 中,可以通过 AnnotationConfigApplicationContext 类来实现这一功能:
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan("com.example");
context.refresh();
UserService userService = (UserService) context.getBean("userService");
System.out.println(userService.getName());
}
}
在上面的代码中,我们首先创建了一个 AnnotationConfigApplicationContext 对象,并通过scan()方法扫描指定路径下的所有 Bean,然后通过refresh()方法加载并注册这些 Bean 到容器中。最后,我们通过getBean()方法获取名为“userService”的 Bean 实例,并输出其名称。
二、获取bean
1. 基于 XML 配置文件的容器获取 Bean
首先,在 XML 配置文件中定义一个 Bean。例如,我们可以定义一个名为“userService”的 Bean:
<bean id="userService" class="com.example.UserService">
<property name="userDao" ref="userDao"/>
<property name="name" value="张三"/>
<property name="age" value="18"/>
<init-method init="init"/>
<destroy-method destroy="destroy"/>
</bean>
<bean id="userDao" class="com.example.UserDao"/>
然后,在 Java 代码中获取该 Bean 的实例。可以使用 ClassPathXmlApplicationContext 类加载配置文件,并调用 getBean() 方法获取 Bean 实例:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
System.out.println(userService.getName());
}
}
在上面的代码中,我们首先创建了一个 ApplicationContext 对象,然后通过 getBean() 方法获取名为“userService”的 Bean 实例,最后输出 userService 的名称。
2. 基于注解的容器获取 Bean
首先,在 Java 代码中声明一个 Bean。例如,我们可以在 UserServiceImpl 类上使用 @Component 注解声明其为一个 Bean,并指定该 Bean 的名称为“userService”:
import org.springframework.stereotype.Component;
@Component("userService")
public class UserServiceImpl {
...
}
然后,在 Java 代码中获取该 Bean 的实例。可以使用 AnnotationConfigApplicationContext 类加载配置文件,并调用 getBean() 方法获取 Bean 实例:
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan("com.example");
context.refresh();
UserService userService = (UserService) context.getBean("userService");
System.out.println(userService.getName());
}
}
在上面的代码中,我们首先创建了一个 AnnotationConfigApplicationContext 对象,并通过 scan() 方法扫描指定路径下的所有 Bean,然后通过 refresh() 方法加载并注册这些 Bean 到容器中。最后,我们通过 getBean() 方法获取名为“userService”的 Bean 实例,并输出其名称。
三、容器类层次结构
在 Spring 中,容器类层次结构是一种管理和组织 Bean 的方式。这种层次结构由多个 BeanFactory 实例构成,每个 BeanFactory 实例都可以管理一组 Bean,并且它们可以相互嵌套,形成父子关系的层次结构。
Spring 提供了多个接口来实现这种层次结构,其中最常用的包括:
-
BeanFactory:所有 BeanFactory 实现的顶层接口,提供了基本的 Bean 容器的功能,如 Bean 的加载、实例化和依赖注入等。
-
ListableBeanFactory:继承了 BeanFactory 接口,提供了额外的功能,允许我们以编程方式查找和枚举容器中所有的 Bean。
-
HierarchicalBeanFactory:继承了 ListableBeanFactory 接口,添加了支持父子 BeanFactory 层次结构的能力。
-
ApplicationContext:继承自 ListableBeanFactory 接口,是 BeanFactory 接口的超集,提供了更多应用级别的服务,例如国际化消息解析、事件发布、AOP 功能等。
-
ConfigurableApplicationContext:继承自 ApplicationContext 接口,添加了对上下文配置的可编辑性,允许我们在应用程序运行时对 Bean 进行修改。
-
WebApplicationContext:用于 Web 应用程序的 ApplicationContext,提供了特定于 Web 应用程序的服务。
容器类层次结构中的每个 BeanFactory 实例都有一个父级 BeanFactory,它可以作为容器类层次结构中的子容器,这种关系通常被称为父子容器。子容器可以通过委托其父级容器来查找 Bean,如果父级容器无法找到所需的 Bean,则子容器自己会尝试进行查找。
四、BeanFactory
BeanFactory 是所有 Spring 容器架构的基础,它定义了一组基本的方法,允许我们加载、实例化和管理 Bean 对象。BeanFactory 的主要作用是提供了简单的 Bean 实例化功能,并管理 Bean 的依赖注入和配置。
BeanFactory 最重要的方法包括:
1. getBean(String name):根据 Bean 的名称获取 Bean 实例。
2. getBean(Class<T> requiredType):根据 Bean 的类型获取 Bean 实例。
3. containsBean(String name):判断容器是否包含指定名称的 Bean。
4. isSingleton(String name):判断指定名称的 Bean 是否为单例模式。
5. getType(String name):返回指定名称的 Bean 的类型。
6. getAliases(String name):返回指定名称的 Bean 的别名。
除了上述方法外,它还提供了 Bean 生命周期的支持,允许我们在 Bean 实例化之前或之后执行定制逻辑,例如 BeanPostProcessor 和 BeanFactoryPostProcessor。
Spring 中有多种实现了 BeanFactory 接口的类,包括:
1. XmlBeanFactory:最早的 BeanFactory 实现类,在 Spring 3.1 版本已经被废弃。
2. DefaultListableBeanFactory:是 Spring 默认的 BeanFactory 实现,在大多数情况下都可以满足需求。它可以用于加载一般的 XML 配置文件,同时也可以通过编程方式注册 Bean。
3. GenericApplicationContext:是 ApplicationContext 接口的默认实现,同时也实现了 BeanFactory 接口,因此它可以作为一个简单的 BeanFactory 使用。与 DefaultListableBeanFactory 相比,GenericApplicationContext 还支持 Annotation 驱动的配置和自动扫描组件。