一、Bean实例化
1.1 加载Spring Bean
通过XML、Java annotation(注解)以及Java Configuration(配置类)等方式加载Spring
Bean:这是指在Spring容器启动时,通过配置文件、注解或配置类等方式告诉Spring框架需要创建哪些Bean。
1.2. 解析Bean的定义
BeanDefinitionReader:这是一个解析器,它将Bean的定义解析成Spring内部的BeanDefinition结构。可以将其理解为将配置文件中的标签转换为Spring框架内部使用的数据结构。
1.3. Bean属性定义
BeanDefinition:它包含了Bean的各种属性和方法,例如id(标识符)、class(类名)、scope(作用域)、ref(依赖的Bean)等。BeanDefinition实际上是将配置文件中的标签的定义信息存储到对应的BeanDefinition属性中。
1.4. BeanFactoryPostProcessor 扩展接口
BeanFactoryPostProcessor:这是Spring容器的扩展接口,它在Spring容器加载完BeanDefinition之后、Bean实例化之前执行。它可以对Bean的元数据(BeanDefinition)进行加工处理,例如填充或修改BeanDefinition的属性。
1.5. 实例化Bean对象
BeanFactory:它是Bean的工厂,根据我们的要求生产各种不同的Bean。BeanFactory根据配置文件中的BeanDefinition信息来实例化Bean对象,并进行属性赋值。(根据class属性反射机制实例化对象,反射赋值设置属性)
1.6. Aware感知
Aware 感知是一种机制,它允许在Bean的生命周期中获取对Spring容器的访问权限。通过实现相应的Aware接口,Bean可以获得与Spring容器的交互能力,以便在特定的生命周期阶段执行自定义操作。
在Spring框架中,有多个Aware接口可供实现,每个接口都提供了不同的功能。以下是一些常见的Aware接口及其作用:
- BeanNameAware:通过实现BeanNameAware接口,Bean可以获取自己在Spring容器中的名称。这对于需要知道自己在容器中的标识的Bean非常有用。
- ApplicationContextAware:通过实现ApplicationContextAware接口,Bean可以获取对Spring应用上下文的引用。这使得Bean能够在需要时访问Spring容器中的其他Bean或执行其他与容器相关的操作。
- BeanFactoryAware:通过实现BeanFactoryAware接口,Bean可以获取对Spring Bean工厂的引用。这使得Bean能够在需要时访问Bean工厂中的其他Bean或执行其他与Bean工厂相关的操作。
通过实现这些Aware接口,Bean可以在初始化过程中获得对Spring容器的访问权限,并在需要时执行特定的操作。这为开发人员提供了更大的灵活性和控制权,使得他们能够根据自己的需求自定义Bean的行为。
1.7. 初始化方法
如果Bean定义中指定了初始化方法,容器会在属性赋值完成后调用该方法进行一些初始化操作。可以使用@PostConstruct注解或者实现InitializingBean接口来指定初始化方法。如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。
1.8. 后置处理
BeanPostProcessor:它是一个后置处理器,在Bean对象实例化和依赖注入完成后,在显示调用初始化方法之前或之后添加自定义的逻辑。可以将其类比为AOP中的环绕通知。
注:只有当检测到Bean对象实现了BeanPostProcessor接口时,才会执行Before和After方法。
BeanPostProcessor的执行顺序是:
Before方法 -> 初始化Bean(InitializingBean接口和init-method方法)-> After方法。
1.9. destroy 销毁
destroy:这是Bean的销毁阶段,如果这个Bean的Spring配置中配置了destroy-method属性,在Spring容器关闭时调用。在销毁阶段,可以执行一些清理工作,例如释放资源等。
二、Bean的单例与多例模式
2.1 单例模式
单例模式是指在整个应用程序中只创建一个Bean实例。
当使用单例模式时,Spring容器在首次请求该Bean时创建一个实例,并将其缓存起来。之后的每次请求都会返回同一个实例。
- 优点:减少了对象创建和销毁的开销,节省了系统资源。
- 缺点:由于共享同一个实例,可能导致状态信息被多个线程共享,需要注意线程安全问题。
2.2 多例模式
多例模式是指每次请求时都会创建一个新的Bean实例。
当使用多例模式时,Spring容器在每次请求该Bean时都会创建一个新的实例,而不是返回之前创建的实例。
- 优点:每次请求都会返回一个全新的实例,避免了状态信息共享的问题。
- 缺点:频繁创建和销毁对象可能导致系统资源消耗增加。
2.3 实例
假设我们有一个简单的Number类,用于计数:
public class Number { private int count; public int getCount() { return count; } public void increment() { count++; } }
我们将使用Spring配置来演示单例和多例模式的区别。
2.3.1 单例模式:
<bean id="counter" class="com.xqx.Number" scope="singleton" /> public class SingletonDemo { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Counter count1 = context.getBean("number", Number.class); Counter count2 = context.getBean("number", Number.class); counter1.increment(); System.out.println("Count 1: " + count1.getCount()); // 输出:Count 1: 1 System.out.println("Count 2: " + count2.getCount()); // 输出:Count 2: 1 } }
在单例模式下,count1和count2引用的是同一个实例,因此它们的计数值是相同的。
2.3.2 多例模式:
<bean id="counter" class="com.xqx.Number" scope="prototype" /> public class PrototypeDemo { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Counter count1 = context.getBean("number", Number.class); Counter count2 = context.getBean("number", Number.class); counter1.increment(); System.out.println("count 1: " + count1.getCount()); // 输出:count 1: 1 System.out.println("count 2: " + count2.getCount()); // 输出:Count 2: 0 } }
在多例模式下,count1和count2引用的是不同的实例,因此它们的计数值是独立的。
2.4 总结
单例模式和多例模式各有优缺点,具体使用哪种模式取决于应用程序的需求和设计考虑。在需要共享状态或资源的场景下,可以使用单例模式;在需要独立状态或避免状态共享的场景下,可以使用多例模式。