基础
bean.xml
定义需要实例化对象的全限定类名以及类之间依赖关系描述
BeanFactory
IOC容器,通过反射技术来实例化对象并维护对象之间的依赖关系
Spring框架IOC实现
纯xml和xml+注解模式下IOC容器启动方式:
JavaSE应用
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
或
ApplicationContext applicationContext = new FileSystemXmlApplicationContext("c:/bean.xml");
JavaWeb应用
通过监听器 ContextLoaderListener 去加载xml
纯注解模式下IOC容器启动方式:
JavaSE应用
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
JavaWeb应用
通过监听器 ContextLoaderListener 去加载xml
TIPS:学习注解的技巧,找xml中标签和注解的一一对应关系即可
BeanFactory和ApplicationContext
BeanFactory是Spring框架中IOC容器的顶层接口,它只是用来定义一些基础功能,定义一些基础规范,而ApplicationContext是它的一个子接口,所以ApplicationContext是具备BeanFactory提供的全部功能。
通常我们称BeanFactory为SpringIOC的基础容器,ApplicationContext是容器的高级接口,比BeanFactory要拥有更多的功能,例如国际化支持和资源访问等等
启动IoC容器的方式
- Java环境下启动IoC容器
- ClassPathXmlApplicationContext:从类的根路径下加载配置文件(推荐使用)
- FileSystemXmlApplicationContext:从磁盘路径上加载配置文件
- AnnotationConfigApplicationContext:纯注解模式下启动Spring容器
- Web环境下启动IoC容器
- 从xml启动容器
<web-app> <display-name>Archetype Created Web Application</display-name> <!-- 配置Spring ioc容器的配置文件 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!-- 使用监听器启动Spring的IOC容器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> </web-app>
- 从配置类启动容器
<web-app> <display-name>Archetype Created Web Application</display-name> <!-- 告诉ContextLoaderListener我们使用注解的方式启动IOC容器 --> <context-param> <param-name>contextClass</param-name> <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value> </context-param> <!-- 配置启动类的全限定类名 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>com.hooda.config.SpringConfig</param-value> </context-param> <!-- 使用监听器启动Spring的IOC容器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> </web-app>
实例化Bean的三种方式
- 方式一:使用无参构造函数
<bean id="userService" class="com.hooda.service.UserServiceImpl"/>
- 方式二:使用静态方法创建
public class DefinedBeanFactory {
public static UserService getUserService() {
return new UserServiceImpl();
}
}
<bean id="userService" class="com.hooda.factory.DefinedBeanFactory" factory-method="getUserService"/>
- 方式三:使用实例化方法创建
public class DefinedBeanFactory {
public UserService getUserService() {
return new UserServiceImpl();
}
}
<bean id="definedBeanFactory" class="com.hooda.factory.DefinedBeanFactory"/>
<bean id="userService" factory-bean="definedBeanFactory" factory-method="getUserService"/>
Bean的属性及生命周期
- 标签属性
Id属性:用于给bean提供一个唯一标识。在一个标签内部,标识必须唯一
class属性:用于指定创建Bean对象的全限定类名
name属性:用于给Bean提供一个或多个名称,多个名称用空格分割
factory-bean属性:用于指定创建当前Bean对象的工厂Bean的唯一标识。当指定了此属性后,class属性失效
factory-method属性:用于指定创建当前Bean对象的工厂方法,如配合factory-bean属性使用,则class属性失效;如配合class属性使用,则方法必须是static的
scope属性:用于指定Bean对象的作用范围,通常情况下就是singleton。当要用到多例模式时,可以配置为prototype
init-method属性:用于指定Bean对象的初始化方法,此方法会在Bean对象装配后调用。必须是无参方法
destory-method属性:用于指定Bean对象的销毁方法,此方法会在Bean对象销毁前执行。他只能为scope是singleton时起作用 - 生命周期
singleton
单例模式的bean对象生命周期和容器一样
prototype
多例模式的bean对象,Spring框架只负责创建,不负责销毁 - 构造注入(constructor-arg)
name:用于给构造函数中指定名称的参数赋值
index:用于给构造函数中指定索引位置的参数赋值
value:用于指定基本类型或者String类型的数据
ref:用于指定其他Bean类型的数据,填写的是其他Bean唯一标识 - set注入(property)
name:指定注入时调用的set方法名称
value:指定注入的数据,支持基本类型或者String类型
ref:指定引用的数据,支持其他Bean类型,写的是其他Bean唯一标识 - DI注解实现方式
@Autowired:Spring提供的注解,默认按照类型注入。如果出现一个类型有多个Bean值时,需要配合@Qualifier使用
@Resource:J2EE提供的注解,默认按照name自动注入。如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到抛异常;如果指定了name,则从上下文中查找id匹配,找不到抛异常;如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或找到多个都会抛异常;
TIPS:@Resource在JDK11中已经移除,如需使用,单独引入jar包 - 纯注解
@Configutation:表明当前类是一个配置类
@ComponentScan:替代context:component-scan
PropertySource:引入外部数学配置文件
@Import:引入其他配置类
@Value:对变量赋值,可以直接赋值,也可以使用${}读取资源配置文件中的信息
@Bean:将方法返回对象加入SpringIOC容器
Spring IOC高级特性
lazy-init延迟加载
ApplicationContext容器默认在启动服务时将所有singleton-bean进行实例化。设置lazy-init为true的bean将不会在ApplicationContext启动时提前被实例化,而是第一次向容器通过getBean获取时实例化
如果一个设置了立即加载的bean1引用了一个延迟加载的bean2,那么bean1在容器启动时被实例化,而bean2由于被bean1引用,所以也会被实例化
如果一个bean的scope属性为prototype时,即使设置了lazy-init=false,容器启动时也不会实例化bean,而是调用时实例化。
FactoryBean&BeanFactory
Spring中Bean有两种,一种是普通Bean,一种是工厂Bean(FactoryBean),FactoryBean可以生成某一个类型的Bean实例,也就是说我们可以借助于它自定义Bean的创建过程。
// 可以让我们自定义Bean的创建过程(完成复杂Bean的定义)
public interface FactoryBean<T> {
// 返回FactoryBean创建的Bean实例,如果isSingleton=true,则该实例会放到Spring容器的单利对象池中
@Nullable
T getObject() throws Exception;
// 返回FactoryBean创建的Bean类型
@Nullable
Class<?> getObjectType();
// 返回作用域是否单例
default boolean isSingleton() {
return true;
}
}
// 例
public class Company {
private name, address, scale;
}
public class CompanyFactoryBean implements FactoryBean<Company> {
private String companyInfo;
@Override
public Company getObject() throws Exception {
// 模拟创建复杂对象
Company company = new Company();
company.setName(companyInfo.split(",")[0]);
...
return company;
}
@Override
public Class<?> getObjectType() {
return Company.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
// xml配置
<bean id="companBean" class="CompanyFactoryBean">
<property name="companyInfo" value="xx,xx,1"/>
</bean>
// 获取CompanyFactoryBean产生的对象
Object companyBean = applicationContext.getBean("companyBean");
// 获取CompanyFactoryBean对象本身
Object companyBean = applicationContext.getBean("&companyBean");
后置处理器
Spring提供了两种后置处理bean的扩展接口,分别为BeanPostProcessor和BeanFactoryPostProcessor,两者在使用上是有所区别的。
BeanPostProcessor是针对Bean级别的处理,在Bean对象实例化之后。
public interface BeanPostProcessor {
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; }
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; }
}
该接口提供了两个方法,分别在Bean的初始化方法前后执行,具体初始化方法指的什么方法,类似我们在定义bean时,init-method所指定的方法
定义一个类实现了BeanPostProcessor,默认会对整个Spring容器中所有bean进行处理。如果要对某个bean处理,可以通过beanName来判断我们将要处理的具体bean
BeanFactoryPostProcessor是针对BeanFactory级别的处理,是对整个Bean的工厂进行处理。典型应用:PropertyPlaceHolderConfigurer
@FunctionalInterface
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;
}
此接口只提供了一个方法,方法参数为ConfigurableListableBeanFactory,该参数类型定义了一些方法
我们可以根据getBeanDefinition方法找到BeanDefinition对象,然后对定义的属性进行修改。
TIPS:调用BeanFactoryPostProcessor方法时bean还没有实例化,此时bean刚被解析成BeanDefinition对象
Bean生命周期关键时机点
Bean对象构造函数调用时机&InitializingBean之afterPropertiesSet
在AbstractApplicationContext.refresh()的finishBeanFactoryInitialization(BeanFactory)处
BeanFactoryPostProcessor初始化和调用时机
BeanFactoryPostProcessor初始化和调用都在AbstractApplicationContext.refresh()的invokeBeanFactoryPostProcessor(beanFactory)
BeanPostProcessor初始化和调用时机
BeanPostProcessor初始化在AbstractApplicationContext.refresh()的registerBeanPostProcessors(beanFactory)处
postProcessBeforeInitialization&postProcessAfterInitialization都在AbstractApplicationContext.refresh()的finishBeanFactoryInitialization(beanFactory)处
Spring IOC容器初始化主流程
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
// 第一步 刷新前的预处理
prepareRefresh();
/*
第二步 获取BeanFactory,默认实现DefaultListableBeanFactory
加载BeanDefinition并注册到BeanDefinitionRegistry中
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 第三步 BeanFactory的预准备工作(进行一些设置,比如Context的类加载器等)
prepareBeanFactory(beanFactory);
try {
// 第四步:BeanFactory准备工作完成后进行的后置处理工作
postProcessBeanFactory(beanFactory);
// 第五步:实例化并调用实现了BeanFactoryPostProcessor接口的Bean
invokeBeanFactoryPostProcessors(beanFactory);
// 第六步:注册BeanPostProcessor(Bean的后置处理器),在创建bean前后执行
registerBeanPostProcessors(beanFactory);
// 第七步:初始化MessageSource组件(做国际化功能;消息绑定;消息解析)
initMessageSource();
// 第八步:初始化事件派发器
initApplicationEventMulticaster();
// 第九步:子类重写这个方法,在容器刷新的时候可以自定义逻辑
onRefresh();
// 第十步:注册实现了ApplicationListener接口的监听器
registerListener();
/*
第十一步:初始化所有剩下的非懒加载单例bean
初始化创建非懒加载方式的单例bean(未设置属性)
填充属性
初始化方法调用(比如调用afterPropertiesSet方法,init-method方法)
调用BeanPostProcessor(后置处理器)对实例bean进行后置处理
*/
finishBeanFactoryInitialization(beanFactory);
// 第十二步:完成context刷新,主要是调用LifecycleProcessor的onRefresh方法,并且发布事件(ContextRefreshEvent)
finishRefresh();
}
...
}
}
BeanFactory创建流程
获取BeanFactory子流程
BeanDefinition加载解析级注册子流程
Resource定位:指对BeanDefinition的资源定位过程。找到定义JavaBean信息的xml文件并将其封装成Resource对象
BeanDefinition载入:把用户定义好的JavaBean表示为IoC容器内部的数据结构
step1:入口在AbstractRefreshableApplicationContext#refreshBeanFactory方法中调用了loadBeanDefinition(beanFactory)
step2:调用多个类的loadBeanDefinition方法,链 AbstractXmlApplicationContext -> AbstractBeanDefinitionReader -> XmlBeanDefinitionReader#doLoadBeanDefinitions,在doLoadBeanDefinition方法中执行registerBeanDefinition(doc, resource)方法
网友评论