spring bean的重新加载
架构体系
在谈spring bean的重新加载前,首先我们来看看spring ioc容器。
spring ioc容器主要功能是完成对bean的创建、依赖注入和管理等功能,而这些功能的实现是有下面几个组件完成的:
Resource:对资源的抽象,不同的资源有不同的实现,例如:ClasspathResource、FileSystemResource。。。
BeanDefinition:描述一个具体的bean,里面包含了bean的一些基本信息,不同类型的bean也有不同的实现类:ScannedGenericBeanDefinition、RootBeanDefinition
BeanDefinitionReader:将资源转化BeanDefinition的一个接口,针对不同类型的资源有不同的实现:XmlBeanDefinitionReader、PropertiesBeanDefinitionReader
BeanFactory:bean工厂,实现了对bean的创建、注入于与管理
ApplicationContext:应用上下文,持有BeanFactory对象
BeanFactory
SpringBean的创建是典型的工厂模式,这一系列的Bean工厂,也即IOC容器为开发者管理对象间的依赖关系提供了很多便利和基础服务,在Spring中有许多的IOC容器的实现供用户选择和使用

BeanFactory定义了工厂类基本的操作,BeanFactory有三个直属子类:AutowireCapableBeanFactory,HierarchicalBeanFactory,ListableBeanFactory;这个三个子类定义了spring对不同bean的处理。
spring对不同的场景提供了不同实现,下面的类
AutowireCapableBeanFactory:定义了对bean的创建与注入
HierarchicalBeanFactory:定义了对结构化的bean进行处理
ListableBeanFactory:定义了对bean的匹配查找
DefaultListableBeanFactory:以上三个接口的默认实现。提供了bean定义的注册与缓存和对単例bean的初始化
BeanDefinition
BeanDefinition是对一个具体的bean的描述,包括了initMethodName、destroyMethodName、资源文件等 。

bean 创建注入
ioc容器的初始化过程包括对资源的定位、资源的解析、beanDefinition的注册、bean的创建与注入。
refresh是整个ioc容器创建的入口
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// 实现了对资源的定位,解析,beanDefinition的注册
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// 初始scope为singleton和不是延迟加载的bean
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
resource的解析和beanDefinition的注册
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
//beanDefintion的注册
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 将beanDefintion注册到beanFactory
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
单例bean的创建入口。
public void preInstantiateSingletons() throws BeansException {
List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
// 创建bean
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return ((SmartFactoryBean<?>) factory).isEagerInit();
}
}, getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
else {
getBean(beanName);
}
}
}
}
spring在内部维护了一个缓存用来保存已创建的单例bean,所以spring在创建bean的时候会首先在缓存里看有没有,如果没有则创建,这同时也为循环依赖提供了支持,spring不对是原型的bean提供循环依赖支持
protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// 首先从缓存里取
Object sharedInstance = getSingleton(beanName);
//省略其它代码
。。。
。。。
。。。
}
创建bean
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
bean注入过程,根据注入类型注入
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Add property values based on autowire by name if applicable.
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
一般我们在项目中使用@Autowired注解是由AutowiredAnnotationBeanPostProcessor这个类实现注入的
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
//获取注入元数据
//AutowiredAnnotationBeanPostProcessor里面维护了一个变量用户缓存注入元数据
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);//依赖注入
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable {
Collection<InjectedElement> elementsToIterate =
(this.checkedElements != null ? this.checkedElements : this.injectedElements);
if (!elementsToIterate.isEmpty()) {
boolean debug = logger.isDebugEnabled();
for (InjectedElement element : elementsToIterate) {
if (debug) {
logger.debug("Processing injected element of bean '" + beanName + "': " + element);
}
element.inject(target, beanName, pvs);
}
}
}
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
try {
Object value;
if (this.cached) {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
else {
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
TypeConverter typeConverter = beanFactory.getTypeConverter();
//获取依赖的bean,会递归调用getBean()方法,直到获取到的bean没有任何依赖
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
synchronized (this) {
if (!this.cached) {
if (value != null || this.required) {
this.cachedFieldValue = desc;
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
if (beanFactory.containsBean(autowiredBeanName)) {
if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
this.cachedFieldValue = new RuntimeBeanReference(autowiredBeanName);
}
}
}
}
else {
this.cachedFieldValue = null;
}
this.cached = true;
}
}
}
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);//通过反射设置值
}
}
catch (Throwable ex) {
throw new BeanCreationException("Could not autowire field: " + field, ex);
}
}
}
spring bean的重新加载
大家都知道我们在eclipse debug模式运行应用程序时,当我们修改了方法体内的代码时,是不需要马上重启就能生效的(其中的原理暂时没找到)。现在我们要实现的spring bean的重新加载,不仅可实现对方法体外做了代码修改能生效,也不需要在dubug模式运行。要实现这样的功能,首先我们要收集到spring为我们创建了哪些bean,然后监听bean对应的字节码文件有没有发生改变,当发生改变时,重新加载。
spring bean的收集
spring在创建bean时,使用了beanDefinition来描述一个bean,并把beanDefinition维护在DefaultListableBeanFactory.beanDefinitionMap。收集bean 可以在spring容器启动完成时获取beanFactory并持有,创建一个ApplicationListener的实现类
public class Startup implements ApplicationListener<ContextRefreshedEvent> {
public void onApplicationEvent(ContextRefreshedEvent event) {
BeanWatch beanWatch = new BeanWatch(event.getApplicationContext());
beanWatch.start();
}
}
获取需要监听的bean
private Map<String, ClassBeanDefinition> findWatchBean() {
Map<String, ClassBeanDefinition> watchBeans = new HashMap<String, ClassBeanDefinition>();
for (String name : applicationContext.getBeanDefinitionNames()) {// 获取spring管理的所有bean名称
Object bean = applicationContext.getBean(name);// 获取bean
BeanDefinition beanDefinition = beanFactory.getMergedBeanDefinition(name);
Class<?> clazz = bean.getClass();
URL resource = clazz.getResource("");
File file = new File(resource.getPath() + clazz.getSimpleName() + ".class");
ClassBeanDefinition classBeanDefinition = new ClassBeanDefinition();
classBeanDefinition.setBeanDefinition(beanDefinition);
classBeanDefinition.setFile(file);
classBeanDefinition.setBeanName(name);
watchBeans.put(name, classBeanDefinition);
}
return watchBeans;
}
字节码重新加载
收集完字节码文件之后要做的就是,对文件的监听,当一个文件发生改变时,我们需要做的是重新将改变的class文件加载进来,但spring为我们创建bean时已经将class文件加载进来了,由于java的类加载机制,一个类只会加载一次,所以我们需要自定义一个classLoader去动态的加载我们需要的类
public class DynamicClassLoader extends ClassLoader {
public DynamicClassLoader(ClassLoader parent) {
super(parent);
}
public Class<?> loadClass(File file,String className) throws ClassNotFoundException {
try {
InputStream input = new FileInputStream(file);
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int data = input.read();
while (data != -1) {
buffer.write(data);
data = input.read();
}
input.close();
byte[] classData = buffer.toByteArray();
return defineClass(className, classData, 0, classData.length);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
重新加载bean
当我们拥有了最新的字节码时,我们需要把bean工厂维护的beanDefinition修改为最新的字节码并把spring工厂对应的bean缓存删除,然后再调用beanFactory的getBean()方法
public void reloadBean(ClassBeanDefinition classBeanDefinition, Class<?> clazz, DynamicClassLoader classLoader) {
try {
//只针对ScannedGenericBeanDefinition做实现
ScannedGenericBeanDefinition beanDefinition = (ScannedGenericBeanDefinition) beanDefinitionMap.get(classBeanDefinition.getBeanName());
beanDefinition.setBeanClass(clazz);
// 删除beanFactory缓存的bean
factoryBeanObjectCache.remove(classBeanDefinition.getBeanName());
// 删除合并的BeanDefinitions
mergedBeanDefinitions.remove(classBeanDefinition.getBeanName());
Object bean = beanFactory.getBean(classBeanDefinition.getBeanName());
reloadReferenceClass(classBeanDefinition, bean, classLoader);
} catch (Exception e) {
e.printStackTrace();
}
}
到此我们已经把改变的bean重新加载进来,但是,如果该bean是别的bean的一个依赖,那么我们需要为有依赖了改变的bean重新进行注入。由于我们前面将bean加载进来时,bean的字节码是被不同的ClassLoader重新加载进来的,如果此时直接将有依赖的bean从缓存删掉,然后再调用getBean()会导致spring在为bean注入时抛ClassCastException,所以我们需要维护一个ClassLoader对象用来作为每次加载新字节码的ClassLoader的父ClassLoader。
public void reloadReferenceClass(ClassBeanDefinition classBeanDefinition, Object bean, DynamicClassLoader parent) {
Class<?> typeClass = bean.getClass();
Set<String> injectCacheKeys = findReferenceClass(typeClass.getName());
DynamicClassLoader classLoader = new DynamicClassLoader(parent);
Thread.currentThread().setContextClassLoader(classLoader);
for (String cacheKey : injectCacheKeys) {// 将引用了该bean的类重新创建
Map<String, InjectionMetadata> map = (Map<String, InjectionMetadata>) Reflections.getFieldValue(autowiredAnnotationBeanPostProcessor, "injectionMetadataCache");
InjectionMetadata metadata = map.get(cacheKey);
ClassBeanDefinition definition = beanDefinitions.get(cacheKey);
try {
Class<?> clazz = classLoader.loadClass(definition.getFile(), definition.getBeanDefinition().getBeanClassName());
Field[] fields = clazz.getDeclaredFields();
Collection<InjectedElement> injectedElements = (Collection<InjectedElement>) Reflections.getFieldValue(metadata, "injectedElements");
Set<InjectedElement> checkedElements = (Set<InjectedElement>) Reflections.getFieldValue(metadata, "checkedElements");
Collection<InjectedElement> elementsToIterate = (checkedElements != null ? checkedElements : injectedElements);
for (Field f : fields) {
Class<?> type = f.getType();
if (type.isInstance(bean)) {
for (InjectedElement element : elementsToIterate) {
Field field = (Field) element.getMember();
if (field.getType().getName().equals(f.getType().getName())) {
Reflections.setFieldValue(element, "member", f);//修改spring维护的InjectedElement属性
break;
}
}
}
}
parentClassLoader = parent;
reloadBean(definition, clazz, classLoader);
} catch (Exception e) {
e.printStackTrace();
}
}
}
spring bean的重新加载的更多相关文章
- Spring bean是如何加载的
Spring bean是如何加载的 加载bean的主要逻辑 在AbstractBeanFactory中doGetBean对加载bean的不同情况进行拆分处理,并做了部分准备工作 具体如下 获取原始be ...
- Spring Boot 学习系列(09)—自定义Bean的顺序加载
此文已由作者易国强授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. Bean 的顺序加载 有些场景中,我们希望编写的Bean能够按照指定的顺序进行加载.比如,有UserServ ...
- Spring XML Bean 定义的加载和注册
前言 本篇文章主要介绍 Spring IoC 容器怎么加载 bean 的定义元信息. 下图是一个大致的流程图: 第一次画图,画的有点烂.
- interface21 - web - ContextLoaderListener(Spring Web Application Context加载流程)
前言 最近打算花点时间好好看看spring的源码,然而现在Spring的源码经过迭代的版本太多了,比较庞大,看起来比较累,所以准备从最初的版本(interface21)开始入手,仅用于学习,理解其设计 ...
- Sspring bean被初始化加载2次
Sspring bean被初始化加载2次 spring框架的web项目时,启动的时候发现某个bean被加载了两次,比如使用SchedulingConfigurer或者使用@PostConstruct的 ...
- SpringXML方式配置bean的懒加载lazy-init
lazy-init(懒加载),表示该bean在容器初始化的时候不进行初始化. 例如: <bean name="role1" class="com.fz.entity ...
- Spring源码剖析2:Spring IOC容器的加载过程
spring ioc 容器的加载流程 1.目标:熟练使用spring,并分析其源码,了解其中的思想.这篇主要介绍spring ioc 容器的加载 2.前提条件:会使用debug 3.源码分析方法:In ...
- Spring源码剖析3:Spring IOC容器的加载过程
本文转自五月的仓颉 https://www.cnblogs.com/xrq730 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https ...
- 深入Spring之IOC之加载BeanDefinition
本文主要分析 spring 中 BeanDefinition 的加载,对于其解析我们在后面的文章中专门分析. BeanDefinition 是属于 Spring Bean 模块的,它是对 spring ...
随机推荐
- iOS开源项目、框架资源
总结的 iOS.Mac开源项目.库.知识点:http://www.open-open.com/lib/view/open1442664670352.html
- libcpmt.lib 与 msvcprt.lib
https://msdn.microsoft.com/en-us/library/2kzt1wy3(VS.80).aspx
- IBatis和Hibernate区别
1. 简介 Hibernate是当前最流行的O/R mapping框架.它出身于sf.net,现在已经成为Jboss的一部分了.iBATIS是另外一种优秀的O/R mapping框架,现已改名叫myB ...
- Linux下安裝Oracle database內核參數設置
參考:1529864.1 ************************************************** RAM ...
- CompletionService/ExecutorCompletionService/线程池/concurrent包
线程池 线程池的基本思想:线程频繁的创建.销毁会极大地占用系统资源,为了减少系统在创建销毁线程时的开销,线程池应运而生.线程池包括多个已创建的线程,当有任务要在新线程中执行时,将任务提交给线程池,线程 ...
- 分享Kali Linux 2016.2第50周镜像文件
分享Kali Linux 2016.2第50周镜像文件Kali Linux官方于12月11日发布Kali Linux 2016.2的第50周镜像.这次保持以往规律,仍然是11个镜像文件.默认的Gnom ...
- 实践:Backbone作前端,Django+Tastypie作后端的简单Web在线聊天室
一.界面设计: 二.数据模型设计 id 每个发言都有一个独立的id由tastypie自动生成 content 发言的内容 username 发言者 date 发言时间 三.前端制作 这里没有用到Bac ...
- containing block
1(position:static和relative) 它的包含块由它最近的块级.单元格(table cell)或者行内块(inline-block)祖先元素创建. 2.position:fixed ...
- Codeforces632E Thief in a Shop(NTT + 快速幂)
题目 Source http://codeforces.com/contest/632/problem/E Description A thief made his way to a shop. As ...
- Codeforces525E Anya and Cubes(双向搜索)
题目 Source http://codeforces.com/contest/525/problem/E Description Anya loves to fold and stick. Toda ...