Giter VIP home page Giter VIP logo

derekyrc / mini-spring Goto Github PK

View Code? Open in Web Editor NEW
5.1K 91.0 1.2K 1.04 MB

mini-spring是简化版的spring框架,能帮助你快速熟悉spring源码和掌握spring的核心原理。抽取了spring的核心逻辑,代码极度简化,保留spring的核心功能,如IoC和AOP、资源加载器、事件监听器、类型转换、容器扩展点、bean生命周期和作用域、应用上下文等核心功能。

Home Page: https://github.com/DerekYRC/mini-spring

License: Apache License 2.0

Java 100.00%
mini-spring springboot spring springframework spring-boot spring-mvc tiny-spring

mini-spring's Introduction

Hi there 👋


欢迎来到我的GitHub主页,我是Derek,目前在字节跳动从事后端开发。

微信 mail

img

我热爱开源热爱技术喜欢分享,是一名框架源码爱好者,熟悉以下框架原理及源码:

  • Spring/Spring Boot
  • Spring Cloud(服务注册和发现、负载均衡、Feign、API网关、流量控制、熔断降级、配置中心等等)
  • Netty
  • RocketMQ
  • Sentinel
  • 其他

🌱 我的开源项目:

🌱 我参与的开源项目:

  • spring-cloud-tencent

    Spring Cloud Tencent 是腾讯开源的一站式微服务解决方案,实现了Spring Cloud 标准微服务 SPI,开发者可以基于 Spring Cloud Tencent 快速开发 Spring Cloud 云原生分布式应用。

🏆 Github Stats:

Star History Chart

mini-spring's People

Contributors

1713612859 avatar derekyrc avatar freeslaver avatar rice666 avatar wangflyzihao avatar whale-lyi avatar zqczgl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mini-spring's Issues

代理对象生成逻辑问题

分支:main/populate-proxy-bean-with-property-values
具体描述:在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation的方法中理论上是提前返回代理对象,会使用applyBeanPostProcessorsBeforeInstantiation方法,结果实现类中返回null从而走applyBeanPostProcessorsAfterInitialization方法,这完全没有必要只会增加代码的理解难度。建议将逻辑优化一下。

bean容器中注册BeanDefinition后,使用bean时才会实例化

bean容器作为BeanDefinitionRegistry和SingletonBeanRegistry的实现类,具备两者的能力。向bean容器中注册BeanDefinition后,使用bean时才会实例化。

这句话个人感觉是不是不太准确呢

在Spring容器中,有两种主要的Bean作用域:

Singleton(单例)作用域:对于单例作用域的Bean,它们在容器启动时(或懒加载情况下,在首次使用时)被实例化,并在容器的整个生命周期中只存在一个实例。这意味着无论何时从容器中请求这个Bean,都会获得同一个实例。单例Bean在容器启动时被初始化。

Prototype(原型)作用域:对于原型作用域的Bean,它们在每次被请求时都会创建一个新的实例。这意味着每次从容器中请求这个Bean时都会得到一个不同的实例。原型Bean在容器启动时并不会被初始化,而是在每次请求时才会创建新实例。

关于ConfigurableBeanFactory和ConfigurableListableBeanFactory

作者你好,我在查看代码的过程当中,发现ConfigurableBeanFactory和ConfigurableListableBeanFactory这两个接口当中都存在addBeanPostProcessor接口方法,但是ConfigurableBeanFactory和ConfigurableListableBeanFactory这两个接口存在继承关系,想请问作者这里是否存在冗余呢?能否去掉ConfigurableListableBeanFactory中的addBeanPostProcessor接口方法呢?
微信截图_20230525220521
微信截图_20230525220454

doCreateBean

请问AbstractAutowireCapableBeanFactory#doCreateBean方法中返回之前会执行getSingleton,那之前的applyPropertyValues,initializeBean等工作不就白做了嘛?还是我哪里理解的有问题?

初始化与销毁方法中,只避免销毁方法执行两次而初始化方法没有相关的判断

DisposableBeanAdapter#destroy() 会通过判断避免 bean 继承自 DisposableBean ,且自定方法与 DisposableBean 方法同名导致销毁方法方法执行两次,代码如下

@Override
public void destroy() throws Exception {
    if (bean instanceof DisposableBean) {
	    ((DisposableBean) bean).destroy();
    }

    //避免同时继承自DisposableBean,且自定义方法与DisposableBean方法同名,销毁方法执行两次的情况
    if (StrUtil.isNotEmpty(destroyMethodName) && !(bean instanceof DisposableBean && "destroy".equals(this.destroyMethodName))) {
	    //执行自定义方法
	    Method destroyMethod = ClassUtil.getPublicMethod(bean.getClass(), destroyMethodName);
	    if (destroyMethod == null) {
		    throw new BeansException("Couldn't find a destroy method named '" + destroyMethodName + "' on bean with name '" + beanName + "'");
	    }
	    destroyMethod.invoke(bean);
    }
}

而 AbstractAutowireCapableBeanFactory#invokeInitMethods() 中缺少类似判断,会导致初始化方法执行两次,代码如下

protected void invokeInitMethods(String beanName, Object bean, BeanDefinition beanDefinition) throws Throwable {
    if (bean instanceof InitializingBean) {
	    ((InitializingBean) bean).afterPropertiesSet();
    }
    String initMethodName = beanDefinition.getInitMethodName();
    if (StrUtil.isNotEmpty(initMethodName)) {
	    Method initMethod = ClassUtil.getPublicMethod(beanDefinition.getBeanClass(), initMethodName);
	    if (initMethod == null) {
		    throw new BeansException("Could not find an init method named '" + initMethodName + "' on bean with name '" + beanName + "'");
	    }
	    initMethod.invoke(bean);
    }
}

在查看spring-framework 5.3.x 源码后可以发现源码是做了相关的判断的,如下

protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
			throws Throwable {

		boolean isInitializingBean = (bean instanceof InitializingBean);
		if (isInitializingBean && (mbd == null || !mbd.hasAnyExternallyManagedInitMethod("afterPropertiesSet"))) {
			if (logger.isTraceEnabled()) {
				logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
			}
			if (System.getSecurityManager() != null) {
				try {
					AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
						((InitializingBean) bean).afterPropertiesSet();
						return null;
					}, getAccessControlContext());
				}
				catch (PrivilegedActionException pae) {
					throw pae.getException();
				}
			}
			else {
				((InitializingBean) bean).afterPropertiesSet();
			}
		}
                // 在这里做了判断
		if (mbd != null && bean.getClass() != NullBean.class) {
			String initMethodName = mbd.getInitMethodName();
			if (StringUtils.hasLength(initMethodName) &&
					!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
					!mbd.hasAnyExternallyManagedInitMethod(initMethodName)) {
				invokeCustomInitMethod(beanName, bean, mbd);
			}
		}
	}

属性占位符替换时,如果当前bean存在String类型的属性但是不用占位符替换,会重复添加PropertyValue

当前属为String类型时,会尝试使用resolvePlaceholder来进行占位符替换,对于直接使用value来指定属性值的属性不会有任何作用:

image-20240311143928710

但是这里没有添加判断,导致重复添加PropertyValue,虽然不会影响后面bean的创建,但是属性列表有重复项也是一种冗余

image-20240311143756148

增加了一定的判断之后:

image-20240311144024800
出现问题的代码在:
https://github.com/DerekYRC/mini-spring/blob/ef07ca1105b4c57b70d63a50425a0de6d6b5814a/src/main/java/org/springframework/beans/factory/PropertyPlaceholderConfigurer.java#L78C5-L78C88

关于循环依赖

我想问一下博主,如果当两个prototype类型的对象互相引用的话,循环依赖是不是还存在呢?我感觉您的代码只能解决单例的循环依赖

博主写的文档太简略了,看不太懂~

AbstractAutowireCapableBeanFactory#doCreateBean 调用 getSingleton不理解

原代码是
if (beanDefinition.isSingleton()) { //如果有代理对象,此处获取代理对象 exposedObject = getSingleton(beanName); addSingleton(beanName, exposedObject); }
spring已经在 initializeBean后 bean 不是已经被替换为 代理对象了吗,如果这里再调用getSingleton 方法,就会再生成一个新的代理对象。这段逻辑不理解

多个切面匹配到同一个对象情况

如题,在多个切面切到同一个对象的时候,在现有逻辑的情况下无法处理,在org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator#wrapIfNecessary中逻辑匹配到对于的class后然后就返回了代理对象。如果是不支持多切面的话感觉太别扭了,毕竟非常核心的功能

有代理对象解决循环依赖这节内容有些不理解的内容

作者大大好,整个项目快看到尾声了,在这节内容上有些问题。
分支:circular-reference-with-proxy-bean

  1. 解决有代理对象的循环依赖问题,因为上一节中,singletonObjects(一级缓存)和earlySingletonObjects(二级缓存)没办法解决有代理对象循环依赖的问题,所以这节中引入了singletonFactory(三级缓存),在代码中的体现就是AbstractAutowireCapableBeanFactory#doCreateBean()方法,这个方法在75行的时候,通过bean复制一个finalBean对象,然后放入singletonFactories三级缓存中,接着在第104行通过getSingleton(beanName)这个方法就是会先从一级缓存找,然后接着二级缓存找,最后再三级缓存中找,对应到项目实际debug的过程中,实际上从三级缓存中去获取,就是调用了singletonFactory.getObject()该方法。也就是上面75行存入的三级对象的getObject方法,最终返回这个代理对象。这是我的理解。那这样的话问题就来了,就是在整个bean的生命周期,还有其他很多的操作步骤,比如BeanPostProcessor修改属性值,填充属性,以及初始化前后的处理,那么实际上对应的代码就是刚刚上面提到75行和104行中间的很多操作步骤。也就是说我们最终获取到的代理bean是根据一开始75行存入三级缓存中的finalBean。而在这个之后,代码中的很多步骤都是对原先bean去做的操作。那75-104行在bean生命周期内所做的操作岂不是对代理对象都不生效了?那代理对象的属性什么的设置是不是就都有问题了。
  2. 另一个问题还是在doCreateBean()方法内,就是获取到代理对象后,105行的代码addSingleton(beanName, exposedObject),这里紧接着将对象放入一级缓存中,并将二级三级缓存进行清空,结合104行的代码,整个过程看起来像是获取到代理对象后,先放入二级缓存中,然后将三级缓存中的清空,接着再放入一级缓存中,再将二级三级缓存清空。所以这里看起来好像放入二级缓存中什么也没操作,那不如直接放入一级缓存,因为放入二级缓存后什么都没操作就又取出来放入一级缓存中了,这里不是太理解。

这节内容debug过程中很多嵌套的,所以理解起来不是很容易,可能自己陷入了某个单独流程上,没办法从整体上去理解。再次感谢作者,学习到了很多东西

关于getBean返回值的一个问题

如题,在Application-context分支下的AbstractApplicationContext的refresh方法会创建beanFactory并加载bean的属性,之后再实例化bean时执行preInstantiateSingletons方法。
这个方法我有一点看不懂,beanDefinitionMap中已经有四个对象(person、car、customBeanFactoryPostProcessor、customerBeanPostProcessor)。之后再引用getBean方法返回DefaultListableBeanFactory的意义是什么?
QQ图片20210801171145

项目时长

想问一下作者这个项目大概需要多长时间才能跟着做完呢?

关于ValueAnnotationTest

在对应的value-annotation.xml文件中是不是没有注册对应的AutowiredAnnotationBeanProcessor

resolveBeforeInstantiation()方法返回的代理对象放入singletonObjects里面吗?

看到动态代理融入bean生命周期这节,有些疑问:
1、 这节resolveBeforeInstantiation()方法生成的代理对象没有放在单例池里面,导致refresh()方法默认提前实例化单例bean的时候会执行一遍创建对象的过程,接着测试方法里面再去调用getBean()方法又会重写再执行一遍创建对象的过程,这里我认为代理对象默认也是单例的,好像应该在创建完成之后放在singletonObjects里面更合适一点。然后我看到下面的bug fix:没有为代理bean设置属性(discovered and fixed by @kerwin89)这节的内容,修复了动态代理融入bean生命周期这节中没有为代理对象设置属性的bug,通过这节中的方式修饰后,生成的代理对象就会走正常的创建完默认单例的对象存入singletonObjects中的流程,也就是在测试的时候,加载完xml文件,调用getBean()方法的时候就直接从singletonObjects中取出来,而不需要重新再执行一遍创建对象的过程了。疑问的点在于我又去重新看了下spring的源码,发现spring中通过resolveBeforeInstantiation(beanName, mbdToUse)方法返回代理对象后就直接返回了,spring中也没有把这个代理对象存储。那这个生成的代理对象到底应该存储起来吗?这里可能会涉及到后面循环依赖的内容,不知道是不是我看到那里就不会纠结这里的内容了
2、bug fix:没有为代理bean设置属性(discovered and fixed by @kerwin89)这节内容修改后,生成代理对象的地方应该就是我下图中划的地方吧。
Snipaste_2023-04-03_23-55-25

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.