Spring学习笔记(三):Spring IOC原理及使用
一、Bean的初始化
1.1 概述
Spring 容器是实例化和管理全部bean 的工厂,Spring 默认将所有的bean 设置成单态模式,无须自己完成单态模式,即对所有相同id 的bean 请求都将返回同一个共享实例。因此,单态模式可大大降低Java 对象在创建和销毁时的系统开销。 功能 标识一个应用环境 利用 BeanFactory 创建 Bean 对象 保存对象关系表 够捕获各种事件
1.2 设计思想
静态工厂方法模式 它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。
//实例化Spring容器
ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
//定义Person接口实例
Person p = null;
//通过Spring上下文获得Chinese实例
p = (Person)ctx.getBean("chinese")
单例模式 BeanFactory 提供默认单例模式的Bean,模型提供了具有特定名称的对象的共享实例,可以在查询时对其进行检索。Singleton 是默认的也是最常用的对象模型。对于无状态服务对象很理想。
1.3 容器初始化
ContextLoader是Spring核心的上下文加载类,ContextLoaderListener通过实现 ServletContextListener的contextInitialized方法调用ContextLoader的 initWebApplicationContext方法实现容器加载 initWebApplicationContext 创建WebApplicationContext 加载对应的Spring文件创建里面的Bean实例 将WebApplicationContext放入ServletContext(就是Java Web的全局变量)中
configureAndRefreshWebApplicationContext 将WebApplicationContext设置到ServletContext上下文 调用refresh核心方法加载spring容器
二、Bean的生命周期
若容器实现了流程图中涉及的接口,程序将按照以上流程进行。需要我们注意的是,这些接口并不是必须实现的,可根据自己开发中的需要灵活地进行选择,没有实现相关接口时,将略去流程图中的相关步骤。
分类类型 包含方法 Bean自身的方法 配置文件中的init-method和destroy-method配置的方法、Bean对象自己调用的方法 Bean级生命周期接口方法 BeanNameAware、BeanFactoryAware、InitializingBeanDiposableBean等接口中的方法 容器级生命周期接口方法 后置处理器BeanPostProcessor实现类中重写的方法
2.1 Bean的初始化
Bean组件在Spring的org.springframework.beans包下。这个包下的所有类主要解决了三件事:Bean的定义、Bean的创建以及对Bean的解析。对Spring的使用者来说唯一需要关心的就是Bean的创建,其他两个由Spring在内部帮你完成了,对你来说是透明的。 当Spring成功解析你定义的一个节点后,在Spring的内部就被转化成BeanDefinition对象。以后所有的操作都是对这个对象完成的。 Bean的解析主要就是对Spring配置文件的解析。
2.2 初始化Bean容器
由以上分析可知,初始化Bean容器的关键方法为用ContextLoader的initWebApplicationContext方法,而过程的核心子方法为configureAndRefreshWebApplicationContext方法中调用的refresh方法,该方法的代码如下:
public void refresh() throws BeansException, IllegalStateException {
Object var1 = this.startupShutdownMonitor;
synchronized(this.startupShutdownMonitor) {
//1、创建beanFactory工厂
// 准备上下文进行刷新
this.prepareRefresh();
// 告诉子类刷新内部beanFactory 创建beanFactory
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
// 配置beanFactory,添加spring本身的工具类
this.prepareBeanFactory(beanFactory);
try {
//2、Spring 功能扩展
//2.1允许在上下文子类中对beanFactory进行配置修改
this.postProcessBeanFactory(beanFactory);
//调用在上下文中注册为bean的beanFactory
//获取实现BeanFactoryPostProcessor接口的子类。并执行它postProcessBeanFactory 方法
this.invokeBeanFactoryPostProcessors(beanFactory);
//2.2创建bean时的自定义操作
//获取用户定义的实现了 BeanPostProcessor 接口的子类,并执行把它们注册到 BeanFactory 对象中的 beanPostProcessors 变量中。 (可添加用户自定义操作)
this.registerBeanPostProcessors(beanFactory);
//3、初始化监听事件和对系统的其他监听者的注册
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
//4、实例化所有非惰性初始的bean
this.finishBeanFactoryInitialization(beanFactory);
5、发布被监听的事件
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
由refresh方法可知,该过程主要分为5步:
(1) 构建 BeanFactory (2) Spring 功能扩展,完成容器和Bean实例化前后的自定义操作 (3) 注册监听事件 (4) 创建 Bean 实例对象 (5) 触发被监听的事件
2.3 创建Bean实例
以上初始化容器的过程的finishBeanFactoryInitialization方法主要有两个作用,分别是创建Bean的实例并构建Bean之间的依赖关系,该过程如下:
protected void finishBeanFactoryInitialization(
ConfigurableListableBeanFactory beanFactory) {
.......
// 停止使用临时的类加载器进行类型匹配
beanFactory.setTempClassLoader(null);
// 允许缓存所有bean定义的元数据,不允许进一步的变化
beanFactory.freezeConfiguration();
// 实例化除了延迟初始化的所有bean单例
beanFactory.preInstantiateSingletons();
}
finishBeanFactoryInitialization方法中,创建Bean实例方法preInstantiateSingletons,该方法的大体流程如下:
循环获取beanDefinitionNames的beanName 根据beanName拿到RootBeanDefinition 对于非抽象的,单例的,非懒加载的bean实例化
判断Bean是否FactoryBean的实现 若是且为SmartFactoryBean并且eagerInit,则直接调用getBean实例化Bean 若为普通bean则直接调用getBean实例化 getBean方法最终调用AbstractAutowireCapableBeanFactory.doCreateBean方法,该方法流程如下:
调用createBeanInstance方法,创建bean实例对象(执行bean的构造方法) 调用instantiateBean,最终会走到BeanUtils.instantiateClass 调用populateBean方法,对bean进行填充,注入相关依赖, 调用方法initializeBean,进行相关初始化工作:
调用applyBeanPostProcessorsBeforeInitialization方法,执行每个BeanPostProcessor的postProcessBeforeInitialization 调用invokeInitMethods方法,执行bean的初始化方法 调用applyBeanPostProcessorsAfterInitialization方法,执行每个BeanPostProcessor的postProcessAfterInitialization方法。
三 使用
3.1 自动化装配
public interface Login {
void login();
}
@Component
public class UserLogin implements Login {
public void login() {
System.out.println("login success!");
}
//隐式的Bean发现机制和自动注解
@Component
public class LoginController {
@Autowired //在属性上添加注解
@Resource //在属性上添加注解
private Login login;
public Login getLogin() {
return login;
}
/**
* 常用的注解有两种:@Autowired,@Resource
* @autowired 是通过类的类型装配Bean
* @Resource 是通过类的名称装配bean,名称默认为字段名称或setter方法
*/
@Autowired //按照类型装配,可以在构造方法中添加该注解
@Resource("userLogin") //注入名称为"userLogin"的Bean
public LoginController(Login login) {
this.login = login;
}
@Autowired //在setter方法上添加该注解
@Resource("userLogin") //注入名称为"userLogin"的Bean
public void setLogin(Login login) {
this.login = login;
}
public void login() {
login.login();
}
}
@Configuration
@ComponentScan
(basePackages{"com.ls.loginModel"})
//扫描bean所在的包,可以配置多个包参数
public class LoginConfig {} //自动化配置类
该流程为:
带有@Component注解的接口实现类 可在@Component注解类中添加@autowired注解实现自动装配(属性,构造方法,方法) @Component注解/ XML配置 启动组件扫描,可配置扫描基础包
3.2 Java代码装配
//Java显示配置
@Configuration
@ComponentScan
(basePackages{"com.ls.loginModel"})
//扫描bean所在的包,可以配置多个包参数
public class LoginConfig {
//Java方式配置类
@Bean
public Login userLogin() {
return new UserLogin();
}
@Bean
public LoginController loginController(Login login) {
LoginController controller = new LoginController();
controller.setLogin(login);
return controller;
}
}
3.3 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
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 自动扫描包(自动注入) -->
<context:component-scan base-package="com.ls.springModel" />
</beans>
//XML方式装配bean
<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
http://www.springframework.org/schema/context/spring-context.xsd">
<class="com.ls.springModel.UserLogin"/>
<bean id="loginController" class="com.ls.springModel.Logintroller">
<constructor-arg ref="login"></constructor-arg> <!--构造注入 对应构造函数 -->
<property name="login" ref="login"></property><!--属性注入 对应setter方法 -->
</bean>
</beans>