1. 注入方式
-
构造方法注入
- 对象构造完,马上可以使用
- 参数过长维护复杂,无法继承,无默认值
-
setter方法注入
- 方法可以命名,可以继承,可以有默认值
- 无法构造完成后,立马使用
- 可以解决 scope = singleton 的bean 的循环依赖
- 提前暴露一个单例工厂方法,从而使其他bean能引用到该bean
- addSingletionFactory
- 提前暴露一个单例工厂方法,从而使其他bean能引用到该bean
-
接口注入
- 基本不用,强制实现不必要的接口
2. 两种类型的容器
-
BeanFactory 。基础类型IoC容器,提供完整的IoC服务支持。如果没有特殊指定,默认采用延
迟初始化策略(lazy-load)。 -
ApplicationContext 。 ApplicationContext 在 BeanFactory 的基础上构建,是相对比较高
级的容器实现,除了拥有 BeanFactory 的所有支持, ApplicationContext 还提供了其他高级特性,比如事件发布、国际化信息支持等 ,在该类型容器启动之后,默认全部初始化并绑定完成。所以,相对于 BeanFactory 来
说, ApplicationContext 要求更多的系统资源,同时,因为在启动时就完成所有初始化,容
器启动时间较之 BeanFactory 也会长一些。在那些系统资源充足,并且要求更多功能的场景中,
ApplicationContext 类型的容器是比较合适的选择。
3. BeanFactory 的对象注册与依赖绑定方式
直接编码
- BeanFactory 定义获取bean及bean的各种属性,各个 BeanFactory 的具体实现类负责具体Bean的注册以及管理工作
-
BeanDefinition 每个bean 都有一个BeanDefinition 对应,保存对象的所有必要信息,包括其对应的对象的class类型、是否是抽象
类、构造方法参数以及其他属性等 - BeanDefinitionRegistry 定义对BeanDefinition的各种增删改操作
外部配置文件方式
- Properties文件格式
- XML文件格式
- 如果需要可以引入自己的文件格式
注解方式
-
版本要求:Spring 2.5以及Java 5
或者更高版本的情况之下
4. bean 的 scope (面试:spring bean 支持哪几种scope/作用域 )
<bean id="mockObject2" class="...MockBusinessObject"
scope="prototype"/>
scope 取值
-
singleton
- 对象实例数量: 一个 所有对该对象的引用将共享这个实例
- 生命周期: 从容器启动,第一请求而初始化以后,将一直存活到容器退出
-
prototype
- 对象实例数量:多个 容器在接到该类型对象的请求的时候,会每次都重新生成一个新的对象实例给请求方
- 生命周期: 对象的实例化以及属性设置等工作由容器负责的,但是只要准备完毕,并且对象实例返回给请求方之后,容器就不再拥有当前返回对象的引用,请求方需要自己负责当前返回对象的后继生命周期的管理工作,包括该对象的销毁
-
request
- 对象实例数量: 多个 每个HTTP请求创建一个全新的 RequestProcessor 对象供当前请求使用
- 生命周期: 当请求结束后,该对象实例的生命周期即告结束
<bean id="requestProcessor" class="...RequestProcessor"
scope="request"/>
-
session
- 对象实例数量: 多个 每个独立的session创建属于它们自己的全新的 UserPreferences 对象实例
- 生命周期: session的生命周期
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
-
global session
- 对象实例数量:
- 生命周期: portlet的Web应用程序中才有意义,它映射到portlet的global范围的
session, 如果在普通的基于servlet的Web应用中使用了这个类型的scope,容器会将其作为普通的session
类型的scope对待
<bean id="userPreferences" class="com.foo.UserPreferences" scope="globalSession"/>
- 自定义 scope ,可以根据自己的需要或者应用的场景,来添加自定义的scope类型
5. BeanFactoryPostProcessor
-
作用: 允许我们在容器实
例化相应对象之前,对注册到容器的 BeanDefinition 所保存的信息做相应的修改。,让我们对最终的 BeanDefinition 做一些额外的操作,比如修
改其中bean定义的某些属性,为bean定义增加其他信息等 例如 我们使用占位符配置数据库相关信息,然后通过配置文件 配置实际的值。
6. bean的生命周期
graph TB
实例化bean对象-->设置对象属性
设置对象属性-->检查Aware相关接口并设置相关依赖
检查Aware相关接口并设置相关依赖-->beanPostProcessor前置处理
beanPostProcessor前置处理-->检查是否是InitializingBean以决定是否调用afterPropertiesSet
检查是否是InitializingBean以决定是否调用afterPropertiesSet-->检查是否配置有自定义init-method
检查是否配置有自定义init-method-->beanPostProcessor后置处理
beanPostProcessor后置处理-->注册必要的Destruction相关回调接口
注册必要的Destruction相关回调接口-->使用中
使用中-->是否实现DisPosableBean接口
是否实现DisPosableBean接口-->是否配置有自定义的destory方法
6.1 实例化bean对象,设置对象属性
- 容器在内部实现的时候,采用“策略模式(Strategy Pattern)”来决定采用何种方式初始化bean实例。
通常,可以通过反射或者CGLIB动态字节码生成来初始化相应的bean实例或者动态生成其子类 - 容器根据BeanDefintion 取得实例化信息,结合CglibSubclassingInstantiationStrategy策略以及不同的bean定义类型,就可以返回实例化完成的对象实例
- 根据beandefinition 有没有lookup-override或者replace-override属性 判断是否用 cglib策略
- 返回 BeanWrapper 对构造完成的对象实例进行包裹,返回相应的 BeanWrapper 实例
- 通过BeanWrapper设置对象属性,免去直接使用Java反射API,java反射很多异常要处理
6.2 检查Aware相关接口并设置相关依赖
- 当对象实例化完成并且相关属性以及依赖设置完成之后,Spring容器会检查当前对象实例是否实
现了一系列的以 Aware 命名结尾的接口定义。如果是,则将这些 Aware 接口定义中规定的依赖注入给当前对象实例。- org.springframework.beans.factory.BeanNameAware 。如果Spring容器检测到当前对象实
例实现了该接口,会将该对象实例的bean定义对应的 beanName 设置到当前对象实例。 - org.springframework.beans.factory.BeanClassLoaderAware 。如果容器检测到当前对
象实例实现了该接口,会将 对应加载当前 bean的Classloader注入当前对象实例。默认会使用
加载org.springframework.util.ClassUtils类的Classloader。 - org.springframework.beans.factory.BeanFactoryAware 。在介绍方法注入的时候,我们
提到过使用该接口以便每次获取prototype类型bean的不同实例。如果对象声明实现了
BeanFactoryAware 接口, BeanFactory 容器会将自身设置到当前对象实例。这样,当前对象
实例就拥有了一个 BeanFactory 容器的引用,并且可以对这个容器内允许访问的对象按照需要
进行访问。 - ApplicationContext 很多 Aware 接口通过BeanPostProcessor 实现 如下ApplicationContextAwareProcessor的postProcessBeforeInitialization方法
- org.springframework.beans.factory.BeanNameAware 。如果Spring容器检测到当前对象实
package org.springframework.context.support;
class ApplicationContextAwareProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
AccessControlContext acc = null;
if (System.getSecurityManager() != null && (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
acc = this.applicationContext.getBeanFactory().getAccessControlContext();
}
if (acc != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
ApplicationContextAwareProcessor.this.invokeAwareInterfaces(bean);
return null;
}
}, acc);
} else {
this.invokeAwareInterfaces(bean);
}
return bean;
}
}
6.3 beanPostProcessor
package org.springframework.beans.factory.config;
import org.springframework.beans.BeansException;
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object var1, String var2) throws BeansException;
Object postProcessAfterInitialization (Object var1, String var2) throws BeansException;
}
- postProcessBeforeInitialization 前置处理
- postProcessAfterInitialization 后置处理
- 可以自定义beanPostProcessor 处理
public class PasswordDecodePostProcessor implements BeanPostProcessor {
public Object postProcessAfterInitialization(Object object, String beanName)
throws BeansException {
return object;
}
public Object postProcessBeforeInitialization(Object object, String beanName)
throws BeansException {
if(object instanceof 我们想要处理的bean.class)
{
// 设置属性,加密解密等。
}
return object;
}
}
6.4 InitializingBean,init-method
package org.springframework.beans.factory;
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
-
InitializingBean
-
作用: 在对象实例化过程调用过“ BeanPostProcessor 的前置处理”
之后,会接着检测当前对象是否实现了 InitializingBean 接口,如果是,则会调用其 afterPropertiesSet() 方法进一步调整对象实例的状态。 -
场景: 在有些情况下,某个业务对象实例化完成后,还
不能处于可以使用状态。这个时候就可以让该业务对象实现该接口,并在方法 afterPropertiesSet()
中完成对该业务对象的后续处理 如图片验证码拦截器 在实例化之后,通过afterPropertiesSet 从配置中读取要拦截的路径
-
作用: 在对象实例化过程调用过“ BeanPostProcessor 的前置处理”
-
init-method
-
作用: 自定义初始化操作可以以任何方式命名,还可以通
过最顶层的 <beans> 的 default-init-method 统一指定。 - 场景: InitializingBean 一样
-
作用: 自定义初始化操作可以以任何方式命名,还可以通
- 对比:InitializingBean 业务对象实现这个接口,有侵入性 init-method 就没有这个问题
6.5 注册必要的Destruction(一次性)相关回调接口
- bean 使用前还有注册一些相关回调接口
6.6 DisposableBean,destroy-method
package org.springframework.beans.factory;
public interface DisposableBean {
void destroy() throws Exception;
}
-
作用: 为该实例注册一个用于对象销毁的回调(Callback),以便在这些singleton类型的对象实例销毁之
前,执行销毁逻辑。只有该对象实例不再被使用的时候,
才会执行相关的自定义销毁逻辑,此时通常也就是Spring容器关闭的时候 - 执行对象的自定义销毁方法
- BeanFactory destroySingletons()
- ApplicationContext registerShutdownHook() 使用了addShutdownHook()
//m中增加一个关闭的钩子,当jvm关闭的时候,会执行系统中已经设置的所有通过方法addShutdownHook添加的钩子,当系统执行完这些钩子后,jvm才会关闭。所以这些钩子可以在jvm关闭的时候进行内存清理、对象销毁等操作。
Runtime.getRuntime().addShutdownHook(Thread hook)