spring底层原理与源码分析,springmvc底层原理
00-1010推断bean生命周期构建方法1的底层原理。使用哪种施工方法。如果有参数,将哪个bean对象赋给参数AOP实现原理spring transaction @ Configuration循环依赖为什么提前发生循环依赖AOP一级缓存singletonObjects二级缓存earlySingletonObjects三级缓存singletonFactories
目录
userService.class -推断构造方法-对象依赖注入-初始化前(@ post construct)-初始化(@ AfterPropertieSet)-初始化后(AOP) -放入Map (singleton pool) -
bean生命周期
推断构造方法的底层原理
@ component public class OrderService { private UserService UserService;@ Override public OrderService(UserService UserService){ this . UserService=UserService;} @ Override public void test){ system . out . println(userService);}}在上面的例子中,因为写了一个参数化的构造方法,所以不能使用无参数的构造方法。
此时,userService属性上没有@Autowired注释,但是打印结果显示这个userService对象存在。
OrderService是一个bean。如果spring想要创建这个bean,它必须使用构造方法。当它发现构造方法有参数时,它将返回并找到一个userService对象来分配给这个属性。
当添加无参数构造函数时,spring将使用无参数构造函数。此时,userService没有任何价值;当有多个构造函数时,如果没有显式通知(通知用@Autowired注释),spring会找到无参数构造函数,如果没有无参数构造函数,会直接报错。
对于第一种情况:“orderService是一个bean。如果spring想要创建这个bean,它必须使用构造函数。当它发现构造函数有参数时,它将返回并找到一个userService对象来分配给该属性。spring,的userService对象,是从哪里找出来并赋给属性的?
首先spring会根据beanName去singleton池查找是否有对应的bean对象,也就是userService,如果有,直接赋给属性。如果没有,就创建它(前提是orderService是一个bean,但是之前没有创建过,但是如果有多个bean的实例,就直接创建)。
但是,如果是创建,可能会有循环依赖。考虑userService中有一个orderService属性,有一个参数化的构造方法:
@ component public class UserService { private OrderService OrderService;@ Override public UserService(OrderService OrderService){ this . OrderService=OrderService;} @覆盖public void test){ system . out . println(orderService);}}这时候在创建orderService的时候就需要使用参数化的构造函数了。因为没有userService,所以此时必须创建它。创建一个用户服务需要一个构造函数,但是已经完成了。这又需要一个orderService,但是orderService本身是被创建的,也就是发生了循环依赖。
00-1010假设singleton池中有一个userService对象,可以直接取出来使用,但是如何在singleton池中获取这个bean呢?因为参数名可以随意设置,所以不能直接找到参数名,要根据类型在singleton池中寻找。如果只有一个这种类型的bean对象,可以直接分配它。然而,在单例池中可能有多个相同类型的对象(不同的beanName)。此时,根据参数名称匹配它们。如果发现他们,会直接分配。如果它们不匹配,将会报告一个错误。(先按类型,再按名称)
00-1010打开AOP的动态代理后,原例子中的userService没有值,因为AOP是在初始化后发生的,初始化后得到的动态代理对象不会再做依赖注释。
入,直接放入了单例池,所以即使属性上面有@Autowired注解也没用。
cglib是基于父子类实现的,代理对象实质是继承了普通对象,并且代理对象中会有一个普通对象的属性、以及被增强的方法,在被增强方法中会先执行切面逻辑,再执行普通对象的方法,而普通对象中是有值的
spring事务
根据上面的AOP实现,事务是基于AOP的实现,生成的是代理类,如果有@Transactional就开启spring事务切面:
1、事务管理器会新建数据库连接,并且设置conn.autocommit = false,因为不管是mybatis还是jdbctemplate都是自动提交,这样就算出现异常,也已经提交了。在新建之后,当target即普通对象去执行test方法市,不管是mybatis还是jdbctemplate操作数据库都要拿到这个连接才能执行sql
2、如果执行完没有抛异常就执行conn.commit
3、
在a方法上的注解加了never,原本应该是要抛出异常的,但是还是顺利写进了数据库,原因是执行a方法的还是userService的普通对象(没有经过AOP增强的对象),就识别不了注解。为什么第一个test方法可以识别?因为一开始是被spring管理的bean对象userService执行,会有相应的逻辑代码去识别注解,识别到注解后生成了代理类和代理对象,然后去运行的test方法,但是执行a方法的时候相当于是 new userService,没有对应的逻辑代码去识别注解。
解决办法:把userService拆出一个新的类,把a方法写进新类
@Configuration
一开始没有加@configuration注解回滚失败。
jdbcTemplate是拿事务管理器新建的数据库连接conn。jdbcTemplate是通过ThreadLocal<Map<DataSource, conn>,线程可能会执行很多方法,可能会有执行不同的datasource,所以是一个map。
因为语法逻辑中jdbcTemplate和事务管理器中是返回新new出来的datasource对象,这样如果没有@configuration,那么jdbcTemplate和事务管理器拿到的是两个不同的datasource对象,那么jdbcTemplate去Map里面找不到对应的conn,只能自己创建新的连接,这样就不能被spring事务管理。
而如果加上了@configuration,那么AppConfig会基于动态代理产生AppConfig代理对象
AppConfig代理对象会先执行自己的代理逻辑,然后去执行普通对象的jdbcTemplate方法,进到父类的jdbcTemplate方法后会执行dataSource方法,但是都是代理对象在执行。代理对象执行dataSource方法的时候先执行代理逻辑:先去spring容器有没有dataSource这个bean,如果没有就创建,如果有就直接返回。
那么就能拿到一样的datasource对象。
循环依赖
为什么会出现循环依赖
首先上面这个例子考虑打破循环依赖。
可以添加一个map<"对象名",对象>,并把实例化AService得到的普通对象放入这个map中,这样在B填充A属性的时候就把AService普通对象注入,B就可以完成创建并放入了单例池,A也就能把单例池中的B对象注入。
但是存在的问题是如果AService在初始化后需要进行AOP,那么最终放入单例池里面的会是AService的代理对象,但是BService拿到的是AService普通对象,因为AService是单例bean,所以只能有一个对象在单例池中,又因为进行了AOP,所以只能是AService的代理对象,并且在其他地方如果依赖了AService,那么应该拿到的是AService的代理对象。
提前AOP
解决方法上述打破循环依赖出现的问题的方法是在把AOP提前,让B创建注入A属性的时候拿到的是AService的代理对象,即提前AOP。如果出现了循环依赖,那么就提前AOP,否则还是在初始化后进行AOP。
如何判断出现了循环依赖?创建一个creatingSet<beanName>,放入正在创建的bean的名称,代表该bean正在创建,后续可以在属性注入的步骤中,如果在单例池中找不到对应的bean对象而在creatingSet中找到了,就可以判定出现了循环依赖。
在判定出现循环依赖之后进行了提前AOP,那么应该什么时候创建AService的代理对象使得BService注入属性的时候拿到的是AService的代理并放入单例池呢?跳到二级缓存
第一级缓存singletonObjects
即单例池
第二级缓存earlySingletonObjects
二级缓存的作用是为了保证单例性:用于出现循环依赖的情况下,会提前产生一个没有经过完整生命周期的早期bean对象,并保存在二级缓存中。否则可能多次创建同一个类型的bean对象。
考虑如下例子:在上述例子中AService再加入一个CService属性,并且在CService也依赖AService属性
在进行bService的生命周期注入aService时会先去二级缓存中根据beanName找有没有aService的bean对象,如果没有就进行AOP并创建aService的代理对象放入二级缓存。
当进行cService的生命周期注入aService时就去二级缓存中找,发现已经有了aService,只可以直接取得
但是二级缓存中存放的不是完整的生命周期的bean对象,所以完成属性填充等动作之后从二级缓存中拿到aService的代理对象放入单例池中。
这时候就不需要在第四步进行AOP了,并且因为AOP的实质是在原有bean的基础上加入切面逻辑,并且AOP后生成的代理对象中还是会有target普通对象
所以在进行属性填充等动作的时候还是对AService的普通对象进行的,那么代理的对象中的普通对象还是可以拿到这些属性值,就相当于代理对象也拿到了
第三级缓存singletonFactories
打破循环依赖的关键,类似于上面提及的map,只是在spring的实现中key值是beanName,value是一个lamda表达式,三级缓存中不去判断是否出现循环依赖,而是只要是支持循环依赖并且是单例的,那么就会加入三级缓存
而lamda表达式返回的是一个对象,执行lamda方法的时候就执行了aop,所以返回的是一个代理对象
在底层源码中,通过第三级缓存来控制第四步中是否需要AOP
如果第三级缓存的map中remove出来是null,整明没有循坏依赖,就这时候进行AOP并返回增强后的代理对象,反之整明之前已经进行了AOP,不需要再进行AOP,直接返回普通对象。
到此这篇关于Spring底层原理深入分析的文章就介绍到这了,更多相关Spring底层原理内容请搜索盛行IT以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。