Spring源码 IOC和循环依赖AOP
IOC篇
1、BeanFactory
IOC容器,以BeanFactory为载体。
BeanFactory,是Spring容器。这是由Spring管理,产生SpringBean生命周期的载体。
豆子的工厂,进厂打工,就得听工厂(Spring)的话。
与之相比较的是FacotryBean,就不是Spring管理,由道友们自己定义。FactoryBean,工厂豆子。
工厂的豆子,重点是豆子,所以自己做主。
BeanFactory 可以怎么用?
/China_eboy/article/details/115513512 (别人那里看到的)本人也没怎么用过。
2、IOC 控制反转
以下是IOC的介绍。IOC英语,Inversion of Control, 反转 of 控制,直译,控制反转。
我理解,IOC是为了把一些配置类,一些服务类,能够解放出来,交给Spring管理。
其中包括了注册和使用。
@Service @Autowired类似。
将xml,json,yml各种格式,都交给Spring解析。
其中IOC容器,经过增强器,实例化,具体经过两个过程,才会变成完整的对象。
3、Bean的实例化
实例化,大致有如下过程。
需要经过实例化和初始化!!
属性赋值,不仅仅是自定义属性赋值,还有继承Aware接口的,将会赋予Spring容器内有的特定值。
属性赋值,完成对象。初始化!!详细如下图。
这就是IOC,将bean交给IOC管理。
AOP,就是切面,如上图,用在了BeanPostProcessor 增强处理器,处理Bean上。
其中讲一个循环依赖的问题。
循环依赖和AOP篇
三级缓存map
//第1级缓存 用于存放 已经属性赋值、完成初始化的 单列BEANprivate final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);//第2级缓存 用于存在已经实例化,还未做代理属性赋值操作的 单例BEANprivate final Map<String, Object> earlySingletonObjects = new HashMap<>(16);//第3级缓存 存储创建单例BEAN的工厂private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
二级缓存就能够解决一般的循环依赖。
如上图,假设A,B两个Class互相引用。
public class A{private B b;}public class B{private A a;}
这就是循环依赖。
但是加入了切面,AOP。二级缓存就不够用了。
三级缓存,是为了解决AOP。
AB循环依赖,时序图如下。
先存
三级缓存方法(addSingletonFactory) 先存 A ,再存A的属性bbb,引用B对象。所以再存B。
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {synchronized (this.singletonObjects) {// 若第1级缓存没有bean实例if (!this.singletonObjects.containsKey(beanName)) {// 放入第3级缓存this.singletonFactories.put(beanName, singletonFactory);// 从第2级缓存删除,确保第2级缓存没有该beanthis.earlySingletonObjects.remove(beanName);// 放入已注册的单例池里this.registeredSingletons.add(beanName);}}}
获取B的对象A。
B:aaa,ref = A
A移入二级缓存,移除三级缓存。 找到了bean A.
deepCopy.add(new PropertyValue(pv, convertedValue)); pv:"bean property 'aaa'"
BeanName zyB,B class完成
applyPropertyValues(beanName, mbd, bw, pvs);
返回出来
addSingletonprotected void addSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) {// 放入第1级缓存this.singletonObjects.put(beanName, singletonObject);// 从第3级缓存删除this.singletonFactories.remove(beanName);// 从第2级缓存删除this.earlySingletonObjects.remove(beanName);// 放入已注册的单例池里this.registeredSingletons.add(beanName);}}
放入一级缓存,移除二级缓存,移除三级缓存。
添加registerdSingleton.add(beanName)
继续跳出到,bean = B。把bean property
addSingleton(7个了 A 和 B)
放入一级缓存,移除二级缓存,移除三级缓存。
添加registerdSingleton.add(beanName)
3、知识点详解
BeanDefinition
BeanDefinition 直译 Bean定义;
Spring根据BD对象来创建Bean对象。
BD有很多的属性用来描述Bean
是Spring中非常核心的概念
BeanDefinition重要的属性
beanClass 表示一个bean的类型,比如:UserSerivice.class,OrderService.class.Spring在创建Bean的过程中会根据此属性来实例化得到对象。
scope 表示一个bean的作用域,比如:scope=singleton,该bean则是单例Bean;scope=prototype,该bean就是一个原型Bean。
isLazy 表示一个bean是不是需要懒加载。prototype原型Bean的isLazy不起作用。
懒加载单例Bean,第一次getBean生成该Bean(需要再要).非懒加载的单例Bean,会在Spring启动过程中直接生成好。
dependsOn 表示一个bean在创建之前所以来的其他bean,在一个bean创建之前,它所依赖的这些bean,得先创建好。
primary bean是主Bean,依赖注入时,根据类型找多了多个bean,如果存在主bean,直接将这个bean注入属性。
initMethodName 表示一个bean的初始化方法。
以下都会被解析成BeanDefinition
@Component@Bean
BeanFactory
BeanFactory时一种Spring容器BeanFactory 翻译过来就是Bean工厂。顾名思义,它可以用来创建Bean,获取Bean。BeanFactory 是Spring中非常核心的组件BeanDefinition,BeanFactory,Bean对象 三者的关系
BeanFactory 将 利用BeanDefinition来生成Bean对象
BeanDefinition相当于BeanFactory的原材料。
Bean对象相当于BeanFacoty所生产出来的产品 。
BeanFactory的核心子接口和实现类
ListableBeanFactoryConfigurableBeanFactoryAutowireCapableBeanFactoryAbstractBeanFactoryDefaultListableBeanFactoryDefaultListableBeanFactory 最重要
支持单例Bean、支持Bean别名、支持父子BeanFactory、支持Bean类型转化、支持Bean后置处理、支持FactoryBean、支持自动装配 等等
什么是Bean的生命周期
Bean生命周期描述的是 Spring中一个Bean创建过程和销毁过程中所经历的步骤,其中Bean创建过程是重点。
程序员可以利用 Bean生命周期机制对Bean进行自定义加工。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UR8ml0aw-1663773346666)(/gh/yasin4040/pic@main/img/image-041220383.png)]
@Autowired是什么?
@Autowired表示某个属性是否需要进行依赖注入,可以写在属性和方法上。
注解中的required属性默认为true,表示如果没有对象可以注入给属性,则抛出异常。
@Autowired 加在某个属性上。 属性注入
@Autowiredprivate Service service
Spring在进行Bean的生命周期过程中,给属性填充这一步,会基于实例化出来的对象,对该对象中加了@Autowired的属性自动给属性赋值。Spring会先根据属性的类型去Spring容器中找出该类型所有的Bean对象,如果找出来多个,再根据属性的名字从多个中再确定一个。如果required的属性为true,根据属性信息找不到对象,则直接抛出异常。
@Autowired 注解写在某个方法上。set方法注入
@Servicepublic class OrderService{private UserSerivice userSerice;@Autowiredpublic void setUserService(UserSerivce userSerivice){this.userservice = userService;}}
当@Autowired注解写在某个方法上时,Spring在Bean生命周期的属性填充阶段,会根据方法的参数类型,参数名字从Spring容器找到对象当作方法入参,自动将值放入,反射调用该方法。
@Autowired加载构造方法上时
@Servicepublic class OrderService{private UserSerivice userSerice;@Autowiredpublic OrderService(UserSerivce userSerivice){this.userservice = userService;}public OrderService(UserSerivce userSerivice,AdminService adminService){this.userservice = userService;this.adminService = adminService;}}
Spring会在推断构造方法来进行实例化,在反射调用构造方法之前,会 先根据构造方法参数类型、类型名从Spring容器中找到Bean对象,当作构造方法入参。
@Resource是什么
@Resource是java层面 。name
@Autowired是spring所提供的。type
属性有值。
属性没有值。
@Value
从properties文件,或者yml,获取值,并且@Value可以解析SpEL(Spring表达式)。
@Value(“liziyao”)
直接传"liziyao"
@Value(“${liziyao}”)
从properties中获取属性。
@Value(“#{liziyao}”)
会将#{}中的字符串当作spring表达式进行解析。spring会把"liziyao"当作beanName,去Spring容器中找对应Bean。
FactoryBean
自定义的工厂Bean.
FactoryBean机制被广泛地应用在Spring内部和Spring与第三方框架或组件地整合过程中。
重点来了!ApplicationContext是什么?
AppliacationContext是比BeanFactory更加强大地Spring容器,它既可以创建Bean,获取 Bean,还可以支持国际化,事件广播,获取资源等BeanFactory不具备的功能。
ApplicationContext所继承的接口
EnviromentCapable 拥有获取环境变量的功能,获取操作系统环境变量和JVM环境变量ListableBeanFactory 获取所有beanNames,判断某个beanName是否存在beanDefinition对象,统计BeanDefinition个数,获取某个类型对应的所有beanNames,等功能HierarchicalBeanFactory 获取父BeanFactory,判断某个name是否存在bean对象的功能。getBean()MessageSource 拥有国际化功能,比如直接利用MessageSource对象获取某个国际化资源(比如不同国家语言所对应的字符)。ApplicationEventPublisher 拥有事件发布功能,可以发布事件,这是ApplicationContext相对于BeanFactory比较突出,常用的功能。ResourcePatternResolver 拥有了加载并获取资源的功能,这里的资源可以是文件,图片等,某个url资源都可以BeanPostProcessor
后置处理器。
AOP是什么?
面向切面编程。
AOP中核心概念
Advice 通知,建议。在Spring中通过定义Advice来定义代理逻辑。Pointcut 切点,Advice对应的代理逻辑应用在哪个类、哪个方法上。Advisor 等于Advice+Pointcut,表示代理逻辑和切点的一个整体,程序员可以通过定义 或封装一个Advisor,来定义切点和代理逻辑。Weaving 织入。将Advice代理逻辑在源代码级别嵌入到切点的过程就叫做 织入。 织衣服。Target 目标对象,也就是被代理对象,在 AOP生成的代理对象中会持有目标对象(原始对象)。Join Point 连接点,在SpringAOP中,就是方法的执行点。
JavaBean 和 SpringBean,对象问题
Bean
是 对象!
JavaBean 一个规范。
规定好的 private 属性,给了getter,setter。javaBean扩展性,兼容性更好,通过方法控制属性,更好。
SpringBean
通过applicationContext的SpringBean.
通过Spring容器获取User.
//凡是由Spring管理的对象都是—>Spring对象
Spring面试专题
技巧,总,分。
总:当前问题回答的是哪些具体的点。
分:以1,2,3,4,5的方式分细节描述相关的知识点,如果有哪些点不清楚,直接忽略过去。
突出一些技术名词(核心概念 ,接口,类,关键方法)
避重就轻
一个问题能占用面试官多少时间?问得越多,可能露馅就越多,当面试官问到一个你熟悉的点的时候,一定要尽量拖时间
1、谈谈SpringIOC的理解,原理与实现?
总分:
控制反转:理论思想。原来的对象由使用者来进行控制,有了spring之后,将整个对象交给Spring来帮我们进行管理。
DI:依赖注入,把对应的属性的值注入到具体的对象中,@Autowired,populateBean完成属性值的注入。
容器:存储对象,使用map结构来存储,在Spring中一般存在三级缓存,singletionObject存放完整的bean对象。整个bean的生命周期从创建到使用到销毁的过程全部都是由容器来管理的。(bean的生命周期)
singletionObject,earylySingletonObject,singletionObjectFacoty
分:
1、一般聊ioc容器的时候要涉及到容器的创建过程(BeanFactory,DefaultListableBeanFactory),向bean工厂中设置一些参数(BeanPostProcessor,Aware接口的子类)等等属性。
2、加载解析bean对象。准备要创建的bean对象定义对象beanDefinition(xml或者注解的解析过程)
3、beanFactoryPostProcessor的处理,此处是扩展点,PlaceHodlerConfigurSupport(处理占位符),ConfigurationClassPostProcessor
4、BeanPostProcessor的注册功能,方便后续对bean对象完成具体的扩展功能。
5、通过反射的方式将BeanDefinition对象实例化具体的bean对象
6、bean对象的初始化过程(填充属性,调用aware方法获取spring属性,调用BeanPostProcessor前置处理方法,调用init-method方法,调用BeanPostProcessor的后置处理方法)
7、生成完整的bean对象,通过getBean方法可以获取。
8、销毁过程。
面试官,这是我对ioc的整体理解,包含了一些详细的处理过程,您看一下有什么问题,可以指点一下(这是面试官允许你讲完过程)。
2、谈一下SpringIOC的底层实现
底层实现:工作原理,过程,数据结构,流程,设计模式 ,设计思想。
回答:你对他的理解和你了解过的实现过程。
反射,工厂,设计模式(会的说,不会的不会说),关键的几个方法
createBeanFactory,getBean,doGetBean,createBean,doCreateBean,createBeanInstance(getDeclaredConstructor,newInstance),populateBean
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qkcAsdaM-1663773346672)(/gh/yasin4040/pic@main/img/image-0417214232412.png)]
3、描述一下bean的生命周期
1、实例化bean,反射的方式生成对象
2、填充bean的属性,populateBean(),循环依赖的问题(三级缓存)
3、调用aware接口相关方法:invokeAwareMethod(完成BeanName,BeanFactory,BeanClassLoader对象的属性设置)
–ApplicationContextAware实现该接口,可以访问Spring容器中缓存的的其他Bean。
4、调用BeanPostProcessor前置的处理方法:使用比较多的有(ApplicationContextPostProcessor,设置ApplicationContext,Environment,ResourceLoader,EmbeddValueResolver等对象)
5、调用initmethod方法:invokeInitmethod(),判断是否实现了InitializingBean接口,如果有,调用afterPropertiesSet方法,没有就不调用
6、调用BeanPostProcessor的后置处理方法:Spring的aop就是在此处实现的,AbstractAutoProxyCreator 注册Destuction相关的回调接口:钩子函数。
7、获取到完整的对象,可以通过对getBean的方式来进行对象的获取
8、销毁流程:1、判断是否实现了DisposableBean接口 2、调用destroyMethod方法
4、Spring是如何解决循环依赖的问题?
关键点:三级缓存,提前暴露对象,AOP
总:什么是循环依赖问题,A依赖B,B依赖A
分:先说明bean的创建过程:实例化,初始化(填充属性)
先创建A对象,实例化A对象,此时A对象的b属性为空,填充属性b从容器中获取B对象,如果找到了,直接赋值不存在循环依赖问题。,找不到直接创建B对象实例化B对象,但是此时B对象的A属性是空的,填充属性a然后又去容器中查找A对象,找不到,又重新进入创建A。形成闭环,这就是循环依赖!
此时,如果仔细琢磨,A对象其实是存在的,只不过A对象不是一个完整的状态,只完整了实例化但是未完成初始化,如果在程序调用过程中,拥有某个对象的引用,能否在后期给他完成赋值的操作,可以优先把非完成状态的处于半成品的对象优先赋值,等待后续操作来完成赋值,相当于提前暴露了某个不完整的对象的引用**,所以解决问题的核心在于实例化和初始化分开操作**,这也是解决循环依赖问题的关键。
当所有的对象都完成实例化和初始化操作之后,还要把完整对象放到容器中,此时在容器中存在对象的2个状态,半成品(实例化没有初始化)和成品,因为都在容器中,所以要使用不同的map结构来进行存储,此时就有了一级缓存和二级缓存。如果一级缓存中有了,那么二级缓存就不会有同名的对象,因为他们的查找顺序是1,2,3这样的方式来查找的。一级缓存中,放的是完整的对象,二级缓存放的是非完整对象。
为什么需要三级缓存?三级缓存的value类型是ObjectFactory,是一个函数式接口,存在的意义是保证在整个容器的运行过程中同名的bean对象只能有一个。
如果一个对象需要被代理,或者说需要生成代理对象,那么要不要优先生成一个普通对象?要!
普通对象和代理对象是不能同时出现在容器中的,因为当一个对象需要被代理的时候,就要使用代理对象覆盖掉之前的普通对象。
实际的调用过程中,是没有办法确定什么时候对象被使用,所以就要求当某个独享被调用的时候,优先判断此对象是否需要被代理,类似一种回调机制的实现。传入lambda表达式的时候,所以通过lambda表达式来执行对象的覆盖过程,getEarlyBeanRefrence().
因此,所有的bean对象在创建的时候都要优先放到三级缓存中,在后续的使用过程中,如果需要被代理则返回代理对象,如果不需要被代理,则直接返回普通对象。代理对象就是增强后的对象!!
4.1 缓存的放置时间和删除时间
三级缓存:
createBeanInstance之后:addSingletionFactory(singletonFactories.put(beanName,singletonFactory)
二级缓存:
第一次从三级缓存确定对象是代理对象还是普通对象的时候,同时删除三级缓存getSingleton
earlySingletonObjects.put(beanName,singletonObject)
一级缓存:
生成完整对象之后放到一级缓存,删除二三级缓存。addSingleton
singletonObjects.put(beanName,singletonObject)
5、BeanFacoty与FactoryBean有什么区别
自定义怎么new,怎么反射,怎么动态代理都可以。
6、Spring中用到的设计模式
单例模式:bean默认都是单例的。
原型模式:指定作用域为prototype
工厂模式 :BeanFactory.proxyFactory
模板方法:postProcessBeanFactory,onRefresh,initPropertyValue
策略模式: ClassPathXmlApplicaitonContext,FileSystemApplicationContext, XmlBeanDefinitonReader,PropertiesBeanDefinitonReader,实例化策略(simple,cglib)
观察者模式:listener 监听器,event监听事件,multicast广播器
适配器模式 :通知的时候有Adapter,AdvisorAdapter
装饰者模式 :BeanWrapper
责任链模式:使用AOP会先生成一个拦截器链。AOP进行通知条用的时候,会使用责任链模式。
代理模式 :cglib,jdk进行动态代理
委托者模式:delegate,BeanDefinitonParserDeltegate
构建者模式:BeanDefinitonBuilder (一步一步可以自定义)
访问者模式:BeanDefinitionVistor访问者模式是:把不变的固定起来,变化的开放出去。
…
**访问者模式对于 ASM 这种场景而言,字节码规范是非常严格且稳定的,如果随便更改可能出问题。但我们又需要对字节码进行动态修改,从而达到某些目的。在这种情况下,ASM 的设计者采用了访问者模式将变化的部分隔离开来,将不变的部分固定下来,从而达到了灵活扩展的目的。景的定义,我们会觉得模板方法模式与这个使用场景的定义很像。但它们还是有些许差别的。访问者模式的变化与非变化(即访问者与被访问者)之间,它们只是简单的包含关系,而模板方法模式的变化与非变化则是继承关系。但它们也确实有类似的地方,即都是封装了固定不变的东西,开放了变动的东西。
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
7、Spring的AOP的底层实现原理
动态代理
aop是ioc的一个扩展功能,先有的ioc,再有的aop,只是在ioc的整个流程中新增的一个扩展点而已:BeanPostProcessor
总:aop概念,应用场景,动态代理
分:
bean的创建过程中,有一个步骤可以对bean进行扩展实现,aop本身就是一个扩展功能,所以在BeanPostProcessor的后置处理方法中来实现。
1、代理对象的创建过程(advice,切面,切点)
2、通过jdk或者cgLib的方式来生成代理对象
3、在执行方法调用的时候,会调用到生成的字节码文件中,直接回找到DynamicAdvisoredInterceptor类中的intecept方法,从此方法开始执行
4、根据之前定义好的通知来生成拦截器链
5、从拦截器链中一次获取每一个通知,开始进行执行。在执行过程中,为了方便找到下一个通知是哪一个,会有一个CglibMethodInvovation对象,找的时候是从-1的位置依次开始查找并且执行的。
8、Spring的事务是如何回滚的?
一般是声明式事务。
spring的事务管理是如何实现的
总:spring的事务是有aop来实现的,首先要生成具体的代理对象,然后按照aop的整套流程来执行具体的操作逻辑。正常情况下,要通过通知来完成核心功能,但是事务不是通过通知来实现的,而是通过一个TransactionInterceptor来实现的。然后调用invvoke来实现具体的逻辑。
分:1、先做准备工作,要实现N多个通知来实现整个流程。解析各个方法上事务相关的属性,根据具体的属性来判断是否开启新事务。
2、当需要开启的时候,获取数据库连接,关闭自动提交功能,开启事务
3、执行具体的sql逻辑操作。
4、在操作过程中,如果执行失败了,那么会通过completeTransactionAfterThrowing,来完成事务的回滚操作。回滚的具体操作是通过doRollBack方法来实现的,实现的时候也是要先获取连接对象,通过连接对象来回滚。
5、如果执行过程中,没有任何意外发生。那么通过completeTrancsationAfterReturnning来完成事务的提交操作,提交的具体逻辑是通过具体的docommit的方法来实现的。实现的时候也是要获取链接,通过连接对象来提交。
6、当事务执行完毕之后需要清楚相关的食物信息,cleanupTransactionInfo。
如果需要聊更细致。需要知道TransactionStatus.TransactionInfo
9、Spring的事务传播
传播特性有几种?7种
Required,
Required_new,
Nested,
Support,
Not_support,
Nerver,
Mandatory
某一个事务,嵌套另一个事务的时候怎么办。
A方法调用B方法,AB都有事务,并且传播特性不同。如果A有异常,B怎么办?B如果有异常,A怎么办 。
总:事务的传播特性指的是不同方法的嵌套调用过程中,事务 应该如何进行处理,是用同一个事务,还是用不同的事务。当出现异常的时候,是回滚还是提交 ,两个方法之间的相关影响。在日常工作中,使用比较多的是required,required_new,nested。
分:1、先说事务的不同分类,可以分为三类:
支持当前事务,不支持当前事务,嵌套事务。
2、如果外层方法是required,内层方法是required,requires_new,nested
3、如果外层方法是requires_new,内层方法是required,requires_new,nested
4、如果外层方法是netsted,内层方法是,required,requires_new,nested.
核心处理逻辑非常简单:
1、判断内外方法是否是同一个事务:
是:异常统一在外层方法处理
不是:内层方法有可能影响到外层方法,但是外层方法是不会影响内层方法的。
(大致可以这么理解,但是 个别不同。nested)
事务的传播。