导图社区 spring
这是一篇关于spring的思维导图,主要内容包括:启动AnnotationConfigApplicationContext,这副图用注解的方式 AnnotationConfigApplicationContext,其他的方式大同小异。
编辑于2024-10-15 18:18:53spring
启动AnnotationConfigApplicationContext, 这副图用注解的方式 AnnotationConfigApplicationContext ,其他的方式大同小异
构造AnnotationConfigApplicationContext
创建BeanFactory,spring最核心的组件,我们可以把它看成spring容器
创建AnnotatedBeanDefinitionReader, 用于读取和解析bean定义, 并注册beanDefinition到spring容器中
构造方法中直接向Spring容器注入了几个 spring自带的核心bean的BeanDefinition
注入ConfigurationClassPostProcessor(重点), 这是个BeanFactoryPostProcessor,它完成了扫描所有配置类 (其实在这个阶段spring容器会把添加了spring定义的注解几乎 所有的类全部看作配置类,包括@configuration、@component等) 生成BeanDefinition并注册到spring容器的全过程
注入AutowiredAnnotationBeanPostProcessor(重点), 这是个beanPostProcessor,主要功能是在创建完bean后做Autowire依赖注入,除了处理@Autowired,它还负责处理@Value和@Inject
注入CommonAnnotationBeanPostProcessor(重点),这是个 beanPostProcessor,主要功能是处理@Resource注解的逻辑,除此之外还会处理@PostConstruct和@PreDestroy注解
注入EventListenerMethodProcessor,这是个BeanFactoryPostProcessor, 将每个使用@EventListener注解定义的事件监听方法变成一个 ApplicationListener实例注册到容器
注入DefaultEventListenerFactory,结合上面的EventListenerMethodProcessor, 一起处理EventListener
根据项目中是否支持Jpa注入PersistenceAnnotationBeanPostProcessor, 如果没有配置jpa则不会注入。
核心方法doRegisterBean().作用是根据 给定的class生成BeanDefinition然后 注册到spring容器中
创建ClassPathBeanDefinitionScanner 用于扫描指定的路径,将spring注解标注 的类扫描到容器中
将传进来的参数(用户定义的配置类) 生成beanDefinition注册到spring容器中
调用refresh()方法(核心方法)
调用prepareRefresh()方法, 该方法做一些准备工作, 比如:用户可以重写这个方法中调用到的initPropertySources()方法 将自定义的参数放到Environment中。紧接着会做参数校验。
调用obtainFreshBeanFactory()方法,主要功能是判断BeanFactory是否需要刷新, 如果需要则会重新创建BeanFactory代替老的,在Annotation方式下,基本可以认为 没做什么事,如果使用的是XML配置方式,这里会刷新,并读取xml文件生成beanDefinition,注入到spring容器中
调用prepareBeanFactory(),准备BeanFactory(初始化BeanFactory)
设置BeanFactory的类加载器、SpringEL表达式解析器、类型转化注册器
向spring容器添加三个BeanPostProcessor,第一个:ApplicationContextAwareProcessor,用于为处理部分Aware(不是全部的Aware都在这里回调)的回调逻辑,也就是说每个bean创建完成后通过这个后置处理器为实现了Aware接口的对象赋值(比如EnvironmentAware)。第二个:ApplicationListenerDetector,识别出实现了ApplicationListener的监听器,存放到监听器列表中。第三个:LoadTimeWeaverAwareProcessor,作用是开启aspectj类加载期织入
记录ignoreDependencyInterface,如果一个属性对应的set方法在ignoredDependencyInterfaces接口中被定义了,则该属性不会进行自动注入(是Spring中的自动注入,不是@Autowired)
记录ResolvableDependency,将对象注册成一个特殊的依赖类型。这种方式注册的对象可以通过 @Autowired 进行注入,但是它并没有在 BeanFactory 中定义为一个 bean。
向spring容器中添加三个单例Bean,1、environment, 2、environment.systemProperties, 3、environment.SystemEnvironment。 这三个都与环境变量相关。 除此之外,还添加了一个单例bean,ApplicationStartup,用于标记应用程序启动期间的步骤并收集有关执行上下文或处理时间的数据。与spring主流程基本上没什么关系,了解即可。如果对这个点感兴趣可以顺带着看看JFR(java飞行记录器)。
调用postProcessBeanFactory()方法,这个方法是个空方法,是留给子类扩展的,比如GenericWebApplicationContext就实现了这个方法,它向spring容器注册了Servlet相关的东西, 在现在我们研究的AnnotationConfigApplicationContext中,可以理解为什么都没做
调用invokeBeanFactoryPostProcessors()方法(重点),作用是执行所有的BeanFactoryPostProcessor,在使用注解方式的默认情况下,到这一步就只有一个ConfigurationClassPostProcessor,在本图的上面已经提到了这个处理器。spring通过它来加载配置类成为BeanDefinition,并注入到spring容器中。需要注意的是,在这个过程可能会注入进来新的BeanFactoryPostProcessor, 所以spring利用了优先级+循环调用把所有引入进来的BeanFactoryPostProcessor全部处理 (这个步骤对应生命周期中执行BeanFactory后置处理器扫描出BeanDefinition 和 生成Bean两个阶段)
执行手动添加的BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()方法,它是BeanFactoryPostProcessor的子接口。我们可以在我们自己的业务代码中、启动spring容器之前通过AnnotationConfigApplicationContext.register()方法注入自己的工厂后置处理器,spring会在这一步执行它。
执行扫描出来的执行扫描出来的BeanDefinitionRegistryPostProcessor(实现了PriorityOrdered)的postProcessBeanDefinitionRegistry()方法,之前注入进来的ConfigurationClassPostProcessor就实现了PriorityOrdered和BeanDefinitionRegistryPostProcessor接口,所以它会在这一步被调用。在spring中如果按优先级排序,PriorityOrdered最高
执行ConfigurationClassPostProcessor的BeanFactoryPostProcessor()方法,扫描并解析所有的配置类。 上面也提到过,这里配置类的范围比较广泛,不限于@configuration,另外还有@Component、@ComponentScan、@Import、@ImportResource,以及包含有@Bean方法的其他spring Bean。区别在于:如果配置了proxyBeanMethods为true(默认值为true)并且类上配置了@configuration便称这个类为full配置类,这个配置类会被包装成代理对象,除此之外的其他配置类(如@Component)都被称为lite配置类,不会被代理。
根据前面的条件获取所有配置类的BeanDefinition,并放到列表中。 然后递归解析配置类,有可能通过解析一个配置类, 得到了其他的配置类,比如扫描和Import
遍历配置类列表,逐个取出配置类的BeanDefinition, 在逐条处理的过程中首先会调用shouSkip()方法。这个 方法里面解析了@Conditional注解,判断是否符合条件, 如果符合,解析配置类(不仅仅解析本类,还会递归解析 父类,直到解析到包名以java开头的父类为止), 否则直接返回,解析下一个配置类。 处理@Conditional注解的流程是spring boot的核心支撑。
解析@configuration,@configuration继承了@Component,作为配置类而言,他们的区别就在于@configuration可以作为full配置类,在整个解析的流程后面会使用cglib为@configuration定义的类创建代理对象并注册到spring容器中。那这个代理对象有什么作用? 简单来说就是增强full配置类里面的@Bean方法,避免我们每次获取到的@Bean方法返回不同的对象,也就是说,如果调用lite配置类的@Bean方法,每次获取的对象都不是同一个,如果调用full配置类的@Bean方法则每次获取到的对象都是同一个。 我们可以找找我们自己项目中的关于事务的配置(如果使用的是@configuration+@bean方式配置数据源和事务管理器),需要多次调用创建datasource的@Bean方法,一次是向sqlSessionFactory中传入database, 另外一次是在创建事务管理器的时候传入database, 如果我们使用lite配置类进行相关配置,那我们业务代码中使用sqlSession获取的链接和事务管理器获取到的链接就不是同一个,事务就会失效。
解析@Component, 将@Component配置类中的所有内部类获取出来,循环判断内部类是否为lite配置类,如果是则添加到一个configurationClasses(一个map)中,这个流程的后面会调用AnnotatedBeanDefinitionReader(就是刚创建spring容器的时候构造方法里的那个reader)的loadBeanDefinitions方法,将这些配置类全部生成BeanDefinition放到spring容器中,并且把配置类下的所有@Bean方法返回的对象也生成BeanDefinition放到spring容器中。
解析@ComponentScan, 根据@ComponentScan的属性新创建一个ClassPathBeanDefinitionScanner,利用这个scanner来扫描指定package下的所有类,然后遍历这些类,判断如果是配置类,则重新走一边解析流程(前面已经提到解析配置类的过程是个递归,如果当前扫描到的类是配置类则递归,继续解析)。比如:扫描到的是个@Component类,会把这个类作为参数再次调用解析方法,然后走到上面的@Component分支流程
解析@Import。 分四种情况, 1、如果导入进来的是普通类,则直接把它当做配置类来解析。2、如果导入的是普通的ImportSelector(实现了ImportSelector接口),那么会调用导入类的processImports()方法,processImports()方法返回的是个包含了用户希望导入到spring容器的类列表,然后将这个列表中的所有类生成BeanDefinition,放到spring容器中。需要注意的是:如果列表中又有实现了ImportSelector接口的类,也会按上面的流程解析一边,这里也是用递归实现的。3、如果导入的是特殊ImportSelector,DeferredImportSelector,那么暂时不会处理,会在解析完所有当前这轮配置类后进行导入,将返回的类再次调用processImports()。4、如果导入的是ImportBeanDefinitionRegistrar,那么暂时不会处理,会在解析完所有当前这轮配置类后,将配置类解析成为BeanDefinition之后进行调用。 后面这两种情况只是稍微晚点处理,也是在这个解析的大流程中解析。功能和上面的一样,也是找出类,生成beanDefinition注册到spring容器中
解析@ImportResource, 解析指定的配置文件,将xml配置文件中的bean取出来,并保存到当前配置类的BeanDefinition中,后面会走lite配置类的解析逻辑。
解析@Bean 首先声明一下,这里@Bean是加在方法上,而不是类上。所以在解析的时候,会找到当前解析的类中所有被@Bean修饰的方法, 并保存到当前配置类的BeanDefinition中, 后面会执行@Bean方法,拿到方法的返回值,放入spring容器中。
解析@PropertySources,这个注解的功能和上面的都不一样。它是解析properties配置文件,并将配置文件的key-value键值对保存到Environment对象中。
执行扫描出来的BeanDefinitionRegistryPostProcessor(实现了Ordered)的postProcessBeanDefinitionRegistry()方法,在spring中如果按优先级排序,Ordered次于PriorityOrdered
执行扫描出来的BeanDefinitionRegistryPostProcessor(普通,也就是没有实现优先级接口)的postProcessBeanDefinitionRegistry()方法
执行扫描出来的BeanDefinitionRegistryPostProcessor(所有)的postProcessBeanFactory()方法
执行手动添加的BeanFactoryPostProcessor的postProcessBeanFactory()方法,注意:由于BeanDefinitionRegistryPostProcessor接口继承了BeanFactoryPostProcessor接口,在按照接口类型获取并执行这些processor时,会出现重复获取的可能,spring利用一个名为processedBeans的列表保存了已经处理过的processor,解决重复执行的问题。下面的流程也是这样的,就不再重复介绍了
执行扫描出来的BeanFactoryPostProcessor(实现了PriorityOrdered)的postProcessBeanFactory()方法
执行扫描出来的BeanFactoryPostProcessor(实现了Ordered)的postProcessBeanFactory()方法
执行扫描出来的BeanFactoryPostProcessor(普通)的postProcessBeanFactory()方法
调用registerBeanPostProcessors()方法,从spring容器的BeaDefinition列表中找到所有实现了BeanPostProcessor的BeaDefinition,按照PriorityOrdered > Ordered > 普通processor(没有实现排序接口) > 实现MergedBeanDefinitionPostProcessor接口(spring称这些类为内部processor)> ApplicationListenerDetector(只有这个是个具体的类) 的顺序,调用getBean()方法获取真正的对象(getBean方法是spring比较核心的方法, 通过它可以根据解析好的BeanDefinition获取对应的实例对象,也就是spring bean,后面会详细说明),然后将这些对象添加到beanFactory的beanPostProcessors列表中。后面会根据顺序执行他们。
调用initMessageSource()方法, 这个方法用于初始化信息源,消息源是 Spring 框架中用于处理国际化(i18n)消息的接口,它提供了一种机制,允许开发者将应用程序中的文本消息外部化,从而使得应用程序能够轻松地支持多种语言和地区的消息显示。这部分不影响spring的整体流程,不做重点介绍
调用initApplicationEventMulticaster()方法, 创建事件广播器。如果用户配置了就用配置的,否则就创建SimpleApplicationEventMulticaster
调用onRefresh()方法,这是个给子类使用的模板方法,是个空方法,什么都没做。比如:在springboot中,它实现了这个模板方法,功能是启动web服务器。
调用registerListeners()方法,把定义的ApplicationListener的Bean对象,设置到ApplicationContext的事件广播器中(就是上面创建出来的事件广播器),并执行在此之前所发布的早期事件(早期事件示在事件广播器还没生成好之前ApplicationContext所发布的事件)。
调用finishBeanFactoryInitialization()方法(重点),这个方法里面调用了getBean(),功能是根据BeanDefinition创建bean对象,如果是配置了单例模式(默认就是单例模式)会缓存到单例池,再次调用getBean()方法时,就会直接去单例池获取。除此之外,依赖注入、AOP、事务等核心功能都在这条链路中。
为factoryBean设置ConversionService, 判断BeanFactory中是否存在名字叫conversionService,并且类型是ConversionService的Bean,如果存在则设置到BeanFactory的conversionService属性。ConversionService是用来进行类型转化的
为BeanFactory设置默认的占位符解析器,用来解析像${xxx}这种占位符
从BeanFactory中获取接口类型为LoadTimeWeaverAware的BeanName列表,如果存在则提前创建这些Bean的实例。LoadTimeWeaverAware与LTW有关,简单来说就是使用AspectJ进行AOP,默认情况下Spring只用到了AspectJ的那几个注解,具体的代理逻辑还是使用了jdk和cglib, 并没有用到它的字节码织入逻辑。所以这段代码一般都运行不到,了解即可
实例化非懒加载的单例bean(重点)
获取从BeanFactory中获取beanDefinitionNames列表,遍历这个列表,根据BeanName(就是列表的元素)获取合并后的BeanDefinition(循环的时候会判断,只处理非懒加载单例Bean),然后调用getBean()方法获取或者创建实例对象。这里的合并逻辑是个递归逻辑,查找当前处理的BeanDefinition所有parent,直到找到RootBeanDefinition(没有指定parent的BeanDefinition),然后遵循child覆盖parent的原则递归覆盖BeanDefinition的属性。 注意:这里的parent不是父类,而是在配置文件中为bean指定的parent。 例如:<bean id="user", class="com.xxx.userImpl", parent="baseUser"/>。
获取/创建FactoryBean(看清楚!不是spring容器BeanFactory,它是一种特殊的bean)FactoryBean创建两个实例对象,一个是FactoryBean对象,创建完成后会和普通bean一样缓存到单例池中,另一个是FactoryBean产生的对象,缓存到一个名叫factoryBeanObjectCache的map中。在这个流程中,首先会以“&” + beanName产生一个新的beanName(因为spring内部FactoryBean的beanName以“&”开头,但这个“&”只在代码流程中出现,用于区分FactoryBean本身和FactoryBean产生的对象。存放到spring容器中的name是不带&的),并根据这个新的beanName调用getBean()方法获取/创建FactoryBean的bean实例。然后,判断这个bean实例是否实现了SmartFactoryBean接口并且非懒加载,如果是则再次调用getBean()方法根据beanName(不加“&”)获取FactoryBean产生的对象(这一段判断逻辑就是实现Factory Bean和实现SmartFactoryBean接口的区别,前者在启动spring容器的时候不会执行getObject()方法,而是在真正调用的时候才会执行,有点像懒加载。后者会在启动spring容器的时候直接执行)。后面的逻辑就是getBean()方法里的逻辑了,getBean()方法中关于FactoryBean的核心其实就是调用FactoryBean的getObject()方法,获取返回的对象并缓存起来,然后进行后续的处理(其他功能大多与普通Bean共用一套代码),下面获取普通bean的流程会详细描述,这里就不重复说明了。 FactoryBean的应用场景,比较典型的就是mybatis的Mapper,因为它是个没有实现类的接口,不能直接注入对象。所以在spring-mybatis的代码中,扫描到@Mapper接口会创建一个FactoryBean,getOjbect()方法中返回一个代理对象,执行到spring的getBean()方法时,会注册到Spring容器中。
调用getBean()获取/创建Bean,这个流程包含了推断构造方法、依赖注入、aop、事务以及执行各种BeanPostProcessor等逻辑。
处理FactoryBean逻辑, 这里需要结合前面获取/创建FactoryBean来看,根据前面的流程我们知道一个FactoryBean的完整流程需要调用两次getBean()。第一次调用是获取/创建FactoryBean本身的bean, 使用getBean("&xxx")。 第二次调用是获取/创建FactoryBean生成的对象,使用getBean("xxx")。 在getBean()的逻辑中,处理FactoryBean本身的bean流程几乎和普通Bean一样,简单理解就是创建实例(ioc、aop等逻辑也会走),然后放到单例池中。如果再次调用getBean("&xxx"),就直接从单例池返回。 处理FactoryBean生成的对象的逻辑是:第一次调用getBean("xxx")会通过调用FactoryBean.getObject()方法获取对象,然后放到factoryBeanObjectCache缓存中,然后执行BeanPostProcessor.postProcessAfterInitialization()。下次再次调用getBean("xxx")则从缓存中直接获取。
处理普通bean,spring支持父子容器,在getBean()方法中会判断当前容器是否存在要获取的BeanDefinition并且是否有父容器,如果有父容器并且当前容器不包含要获取的BeanDefinition就去父容器中找(执行parentBeanFactory.getBean()方法,这个逻辑其实也是个递归),否则,继续走当前容器查找逻辑 。
调用getMergedLocalBeanDefinition()方法,合并BeanDefinition
处理dependsOn逻辑, dependsOn是什么? 举个例子:现在有A、B、C三个类,如果我们希望在创建A类对象的时候,必须先创建出B,而创建B类又必须先创建C。 spring首先会检查当前处理的bean是否配置了dependsOn,如果配置了,则调用getBean("dependsOn依赖类"),从而先处理被依赖的类。 这里可能会出现循环依赖的问题,spring采用两个map解决这里的循环依赖问题。这两个map,一个记录了当前类依赖了哪些类, 另一个记录了哪些类依赖了当前类。在创建被依赖对象之前会先根据这两个map来判断是否出现了循环依赖,如果出现了则会抛出异常。这里说的循环依赖并不是平常说的依赖注入时发生的循环依赖,依赖注入的循环依赖是通过三级缓存来解决的,后面会介绍
获取/创建bean, 这里会判断配置的scope类型, 如果是singleton(默认),会先从单例池(一个名为singletonObjects的map)中找,没找到就调用createBean()创建对象并缓存到单例池。scope其他类型像prototype、request、session,整体上都是直接调用createBean()得到一个实例对象。区别在于调用createBean()前后做了一些处理。比如request和session,需要获取web相关的参数。这些细节就不展开说明了。
调用createBean()创建bean
调用resolveBeanClass()方法,加载类
实例化前,遍历所有的InstantiationAwareBeanPostProcessor,逐个调用他们的postProcessBeforeInstantiation()方法。如果处理的过程中,有一个返回值(返回的是bean)不为null,则会退出这个遍历逻辑, 并会直接对返回的bean做初始化后的操作(遍历所有的BeanPostProcessor,调用他们的postProcessAfterInitialization方法),然后跳出createBean()并返回这个bean。这段逻辑的作用是给BeanPostProcessor一个创建对象的机会。 我理解的是:用户可以自己定义一个InstantiationAwareBeanPostProcessor的实现类并实现postProcessBeforeInstantiation()方法,这样我们这些用户就可以根据特殊场景创建bean并交给spring管理,不用走spring创建bean的生命周期流程
调用doCreateBean()方法创建bean
实例化bean。 调用createBeanInstance()方法,根据推断构造方法的逻辑,选出匹配的构造方法,调用它生成bean对象 (这个步骤对应生命周期中推断构造方法和实例化两个阶段)
如果当前处理的beanDefinition中设置了instanceSupplier,则使用这个supplier提供的实例并返回(调用supplier.get()方法返回一个实例bean)。 这种方式给用户提供了自己new instance的机会。我们可以拿到BeanDefinition然后调用它的setInstanceSupplier()方法,传入实现了Supplier接口的实现类, 在这个实现类中实现它的get()方法。
处理@bean方法返回的对象对应的BeanDefinition。 如果BeanDefinition的factoryMethodName属性不为null,就说明它是一个@Bean方法。 在由@Bean生成的BeanDefinition中,有一个重要的属性isFactoryMethodUnique,表示 factoryMethod是不是唯一的,在普通情况下@Bean生成的BeanDefinition的 isFactoryMethodUnique为true,但是如果出现了方法重载(在一个配置类里配置了两个@Bean方法,并且这两个方法的名字相同),那么就是特殊情况值会设置为false。如果值为true,说明当前BeanDefinition对应的@bean方法只有一个,那在这里就直接使用这个方法创建Bean, 如果值为false,则说明当前BeanDefition对应了多个方法,需要和推断构造方法的逻辑一样,去选择用哪个方法来创建Bean。推断构造方法下面会介绍
如果是一个原型BeanDefinition,会多次来创建Bean,那么就可以把该BeanDefinition所要使用的构造方法缓存起来,避免每次都进行构造方法推断。spring的现实是:将推断出来的构造方法缓存到BeanDefinition的resolvedConstructorOrFactoryMethod字段中。再次创建对象时可以直接使用。
推断构造方法,首先,遍历所有的SmartInstantiationAwareBeanPostProcessor,并执行他们的determineCandidateConstructors()方法得到哪些构造方法是可以用的。 然后, 根据4个条件进行判断,如果存在可用得构造方法,或者当前BeanDefinition的autowired是AUTOWIRE_CONSTRUCTOR,或者BeanDefinition中指定了构造方法参数值,或者创建Bean的时候指定了构造方法参数值,那么就调用autowireConstructor()方法自动构造一个对象( 这个方法中会根据参数类型和获取到的参数做匹配,参数可以是调用getBean()方法传入进来的,也可以是根据type在spring中找出来的。 如果一个参数根据type从spring中找出来多个值,会根据匹配程度打分,比如:假设bean的类型为A,A的父类是B,B的父类是C,同时A实现了接口D 如果构造方法的参数类型为A,那么完全匹配,得分为0 如果构造方法的参数类型B,那么得分为2 如果构造方法的参数类型为C,那么得分为4 如果构造方法的参数类型为D,那么得分为1,分值越小匹配度越高) 。 最后, 如果不是上述情况,就直接调用instantiateBean()方法,根据无参的构造方法实例化一个对象
大概的推断逻辑
类上只有一个构造方法
如果只有一个无参构造方法,则直接使用无参构造方法实例化
如果只有一个有参构造方法则需要分情况来看
如果使用了注解方式,会使用这个构造方法进行实例化,那么Spring会根据构造方法的参数信息去寻找bean,然后传给构造方法并实例化bean
如果使用了xml配置方式,要么在XML中指定构造方法的参数值(手动指定),要么配置autowire=constructor让Spring自动去寻找bean做为构造方法参数值,然后实例化bean
类上有多个构造方法
如果开发者指定了想要使用的构造方法,那么就用这个构造方法。 如果采用的是xml配置方式,可以使用<constructor-arg>标签来指定。如果采用的是注解的方式,可以将@Autowired注解可以写在构造方法上,如果多个构造方法上都有@Autowired注解,则判断这些@Autowired注解上是否有且只有一个配置了required=true这个属性。@Autowired(required=true)的构造方法在每个类中最多只能有一个(也可以一个也没有),如果有一个则使用它创建bean。 如果有多个就报错。但是可以有多个@Autowired(required=false),这种情况下,需要Spring从这些构造方法中去自动选择一个构造方法(自动选择的逻辑下面会讲)。 另外还有一种方式可以指定构造方法,可以在BeanDefinition中设置。
自动选择,如果开发者没有指定想要使用的构造方法,则看开发者有没有让Spring自动去选择构造方法。这种情况只有xml方式支持,注解方式不支持。可以在xml中指定某个bean的autowire为constructor,这个属性表示通过构造方法自动注入,自动注入的时候顺便进行了实例化。
如果开发者也没有让Spring自动去选择构造方法,则Spring利用无参构造方法,如果没有无参构造方法,则报错
BeanDefinition(合并后的)的后置处理,遍历所有的MergedBeanDefinitionPostProcessor,并调用他们的postProcessMergedBeanDefinition()方法,对此时的BeanDefinition进行加工。用户可以实现这个接口方法,对BeanDefinition操作,比如:我们想为某一批对象上的普通字段进行赋值,就可以在方法中这样写beanDefinition.getPropertyValues().add("字段名", 值); 在后面真正为bean赋值的逻辑中spring会将beanDefinition里面的内容设置到bean中。
将bean对象添加到三级缓存,解决依赖注入的循环依赖问题,关于这个问题,后面的依赖注入流程会有详细介绍,但这块内容比较绕,想讲清楚有点难。 这里bean对象添加到的是第三级缓存,第三级存放的实际上是个lamda表达式,里面的逻辑是将bean放到第二级缓存中,并对bean进行提前aop(依赖注入时会调用到这个表达式,第二级缓存存放的都是早期对象的,第一级缓存就是单例池,当creatreBean()的流程全部走完后,会把bean放到单例池中,然后删除第二级和第三级缓存相关的数据,这时候才意味着bean真正可用),我们可以把早期对象简单理解成为半成品。 为什么要提前进行aop?因为在依赖注入时,如果出现了循环依赖(A依赖B,B依赖A),A先被创建,然后依赖注入B,这时会调用getBean(B)->createBean(B),当执行到B的依赖注入时从三级缓存中可以获取到A,就不会在走createBean(A)的逻辑了,而是直接从三级缓存中拿到。但这个时候A还没有被完全创建好,也就是说,还没有进行aop,就直接把“半成品的”A注入到了B中,这样就出问题了, 所以需要在lamda表达式中提前进行aop。 这个例子的过程也就是spring利用三级缓存解决循环依赖的大体思路。 提前aop的逻辑和后面正常流程的aop是同一套代码,调用了同一个方法wrapIfNecessary()。
调用polulateBean()方法,填充属性
实例化后,遍历所有的InstantiationAwareBeanPostProcessor,逐个调用他们的postProcessAfterInstantiation()方法。
自动注入,这里并不是使用@autowired注解的方式注入,而是配置byType或者byName的方式,如<bean id="" class="" autowire="byType|byName|constructor|default|no" />。为什么我们平时都是用的@Autowired注 解而没有用上文说的这种自动注入方式呢?因为这种方式控制力度没有@Autowire注解细。如果我们定义的类中有重载方法,那我们将无法控制到底用哪个。
byType, polulateBean()会调用autowireByType()方法根据参数类型自动注入,这种方式下不需要写@Autowired 注解, 但是需要每个字段都有对应的set方法,spring会获取到set方法中的唯一参数的参数类型,并且根据该类型去容器中获取bean(参数只能有一个,找到多个会报错)。再把属性名(不是真正的属性名,而是set方法截取set后面的部分,比如:setXxx()的属性名就是xxx)和找到的bean添加到MutablePropertyValues中。在后面的流程中会调用set方法进行赋值,完成注入
byName,, polulateBean()会调用autowireByName()方法根据参数名称自动注入,这种方式下不需要写@Autowired 注解, 但是需要每个字段都有对应的set方法,Spring会截取set方法名称的后半段作为name,然后调用getBean(name),根据名称获取bean。把name和bean放到MutablePropertyValues中。在后面的流程中会调用set方法进行赋值,完成注入
constructor, 这种方式的逻辑没在这里, 在推断构造方法的逻辑中。
dsfault, 类似于继承, 如果我们这么配: <beans autowire="byType"> <bean id="" class="" autowire="default" <beans> 那么bean的autowire就是byType。
no, 表示关闭自动autowire
处理属性, 这个步骤中,就会处理@Autowired、@Resource、@Value等注解,遍历所有的InstantiationAwareBeanPostProcessor,执行他们的postProcessProperties()方法。AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor都是这个接口的实现类。AutowiredAnnotationBeanPostProcessor是用来处理@AutoWire的注解的,CommonAnnotationBeanPostProcessor是用来处理@Resource注解的。
处理@Autowired注解,调用AutowiredAnnotationBeanPostProcessor 的postProcessProperties()方法, 这个方法做了两件事,1、将当前处理的bean中所有添加了@Autowired注解的属性和方法全部找出来,2、遍历所有的注入点(找出来的字段和方法),为赋值。 (由于调用了后置处理器的postProcessProperties()方法, 所以与下面的CommonAnnotationBeanPostProcessor的处理逻辑都对应生命周期中的填充属性后阶段,但其实这个阶段属于填充属性)
调用findAutowiringMetadata()方法寻找注入点,查找步骤大概是: 1、遍历当前类的所有的属性字段Field,2、查看字段上是否存在@Autowired、@Value、@Inject中的其中任意一个,存在则认为该字段是一个注入点,3、如果字段是static的,则不进行注入(不支持static字段和方法注入,因为如果bean的scope配置为prototype并且是static的,那这个字段的值没办法确定)。4、获取@Autowired中的required属性的值,5、将字段信息构造成一个AutowiredFieldElement对象,作为一个注入点对象添加到currElements集合中,6、遍历当前类的所有方法Method,7、判断当前Method是否是桥接方法,如果是找到原方法( 桥接方法是由编译器自动生成的,用于保持多态。当你在继承一个包含泛型的类时,由于类型擦除,子类的方法签名可能与父类不一致。编译器会生成桥接方法以确保正确的多态行为。 ), 8、查看方法上是否存在@Autowired、@Value、@Inject中的其中任意一个,存在则认为该方法是一个注入点, 9、如果方法是static的,则不进行注入, 10、获取@Autowired中的required属性的值, 11、将方法信息构造成一个AutowiredMethodElement对象,作为一个注入点对象添加到currElements集合中, 12、遍历完当前类的字段和方法后,将遍历父类的,直到没有父类。13、最后将currElements集合封装成一个InjectionMetadata对象,作为当前Bean对于的注入点集合对象,并缓存
调用InjectionMetadata.inject()方法进行注入,这个方法会遍历所有查找出来的注入点,对其进行赋值。
字段注入,步骤:1、遍历所有的遍历所有的AutowiredFieldElement对象。2、将对应的字段封装为DependencyDescriptor对象,3、调用BeanFactory的resolveDependency()方法,传入DependencyDescriptor对象,进行依 赖查找,找到当前字段所匹配的Bean对象, 4、将DependencyDescriptor对象和所找到的结果对象beanName封装成一个ShortcutDependencyDescriptor对象作为缓存,比如如果当前Bean是原型Bean,那么下次再来创建该Bean时,就可以直接拿缓存的结果对象beanName去BeanFactory中去拿bean对象了,不用再次进行查找了, 5、利用反射将结果对象赋值给字段。
字段注入的第3步,依赖查找,找到当前字段锁匹配的bean对象流程:1、先判断当前字段上有没有@Lazy注解,如果有就走代理逻辑,生成代理对象并赋值给当前字段。2、判断当前字段上有没有@Value注解, 如果有就使用EL表达式解析器解析占位符(比如:#{xxx}),解析出来的值赋值给字段( Spring的EL表达式功能比较强大,不仅能解析Environment中的配置信息,还能解析bean, 也就是说@Value(#{beanName})就等同于@Resource(name="beanName") )。3、根据规则逐步过滤符合当前字段的值(这个过程会逐步过滤,越来越少,直到找到最合适的一个bean,然后赋值给字段)。
第3步的过滤步骤:1、首先根据当前字段的类型查找到所有匹配的bean的beanName,2、调用SimpleAutowireCandidateResolver的isAutowireCandidate()方法,判断BeanDefinitio中的autowireCandidate属性如果为false,表示不能用来进行自动注入,如果为true则继续进行判断。3、调用GenericTypeAwareAutowireCandidateResolver的isAutowireCandidate()方法,判断当前type是不是泛型,如果是泛型是会把容器中所有的beanName找出来的,然后获取到泛型的真正类型,进行匹配,如果当前beanName和当前泛型对应的真实类型匹配,那么则继续判断。 4、 调用QualifierAnnotationAutowireCandidateResolver的isAutowireCandidate()方法进行判断,如果当前DependencyDescriptor上存在@Qualifier注解,那么则要判断当前beanName上是否定义了Qualifier,并且是否和当前DependencyDescriptor上的Qualifier相等,相等则匹配(其实就是根据当前字段上@Qualifier的value值查找bean,哪个 bean上的@Qualifier的value值与字段上的相等就符合)。 5、 查找这些bean上是否有@primary注解,如果有且只有一个,直接使用这个beanName,获取bean并赋值给当前字段,如果找到多个则报错, 如果没有找到则继续判断。 6、查看这些bean中是否定义了@priority注解,如果有,则取出来优先级最高的,直接使用这个beanName获取bean并赋值给当前字段。如果没有,则继续过滤。7、根据字段名在这些bean中查找,找出beanName与当前字段名相同的bean。 8、最后判断是否找到了唯一的beanName(到这里肯定不会有多个,因为beanName不会重复)。 如果找到了就直接获取bean并赋值给当前字段,如果没找到则判断当前字段上的@Autowired是否配置了required属性,如果配置了则报错,如果没配就返回null。 过滤链路总结:autowireCandidate属性 ->泛型 -> @Qualifier注解 ->@primary ->@priority ->beanName
set方法注入,步骤:1、遍历所有的AutowiredMethodElement对象、2、遍历对应的方法的参数,将每个参数封装成MethodParameter对象,3、将MethodParameter对象封装为DependencyDescriptor对象、4、调用BeanFactory的resolveDependency()方法,传入DependencyDescriptor对象,进行依赖查找,找到当前方法参数所匹配的Bean对象。5、将DependencyDescriptor对象和所找到的结果对象beanName封装成一个ShortcutDependencyDescriptor对象作为缓存,比如如果当前Bean是原型Bean,那么下次再来创建该Bean时,就可以直接拿缓存的结果对象beanName去BeanFactory中去拿bean对象了,不用再次进行查找了, 6、利用反射将找到的所有结果对象传给当前方法,并执行
set方法注入第5步,依赖查找,和上面的字段注入调用了同一套逻辑
处理@Autowired注解,调用CommonAnnotationBeanPostProcessor的postProcessProperties()方法, 在这个流程中,会先根据注入点的名字判断BeanFactory中是否存在对应的bean,如果存在则直接使用这个bean给字段赋值。如果不存在,则判断@Resource注解是否配置了name属性, 如果配置了,则直接根据配置的name去查找,找到了就直接用这个bean给当前字段赋值,没找到就报错。 如果@resource注解没有配置name, 则走@autowied的逻辑, 会调用@autowired的代码。 总结:这个过程大概可以理解为 :指定了name,就按name获取, 没指定就按类型(@Autowired)获取
关于循环依赖的问题。 这个问题有点绕,建议边看边画图。 假设现在我们有A和B两个类,A类里有b属性指向B类的对象, B类有a属性指向A类的对象。如果我们不使用spring, 直接A a = new A(); B b = new B(); A.b = b; B.a=a; 这样做完全没问题。 那为什么spring中这种场景就会出现循环依赖呢? 这时我们需要了解一下bean大概的生命周期:扫描bean得到beanDefinition -> 根据beanDefinition经过推断构造方法流程生成bean(这里的bean我们暂时叫做原始对象) -> 为原始对象填充属性(依赖注入) -> 如果原始对象被AOP,生成代理对象->把最终的代理对象(如果被aop就是代理对象,没有就是对象本身)放入单例池,下次getBean()就可以直接从单例池拿到对象。 这种情况下就会出现循环依赖,假设现在spring没有使用三级缓存解决循环依赖, 首先spring创建A的bean实例, 执行到依赖注入时,调用getBean(b), spring开始创建B的bean实例,然后会执行b的依赖注入,调用getBean(a)......, 此刻出现了一头围着磨盘转的驴, 没完没了。
如何解决循环依赖?spring采用加缓存的方式解决, 从前面的流程我们知道, 问题出现在依赖注入的时候相互注入。 那如果创建A的bean对象时, 实例化完成后就把a对象添加到一个缓存里, 然后在依赖注入时创建B的bean对象, 实例化b并将b也放入缓存, 然后往b对象注入a,这时候a对象已经在缓存里面了,所以可以从缓存中拿到a注入到b对象中,走完b的生命周期后,b对象也被拿到了,再把b对象赋值给a...... 循环依赖被解决了。 注意:scope="prototype"的情况和构造方法的依赖注入慎用,缓存并没有解决这两种循环依赖问题。
如何保障被AOP的bean依赖注入时注入代理对象而不是原始对象? 循环依赖是在spring最核心的IOC时出现的,另外一个核心功能AOP在这个时候还没处理(正常情况下,AOP基本上在bean的生命周期最后才会生成代理对象)。从前面的流程中我们发现,虽然a和b都生成了对象,并且完成了依赖注入。但是如果开启了AOP功能就会出问题, 因为在注入的时候从缓存中拿到的是原始对象,而我们需要注入的是AOP后的代理对象。 现在的需求就变成了如果当前bean被AOP,缓存里应该放代理对象,否则应该放原始对象。怎么办? 鸡贼的spring往缓存中放入了一段lamda表达式并把生成的bean对象当作参数传递进去, 这是一段逻辑( 这段逻辑在实例化bean后只是放到了缓存中,并不执行,在依赖注入时如果发生了循环依赖才会执行,怎么判断是否循环依赖后面会说明 ),它调用了wrapIfNecessary()方法, 这个方法就是AOP的入口方法,如果需要AOP就返回代理对象,如果不需要就返回原始对象。这个缓存就是spring三级缓存中的第三级singletonFactories。
为什么缓存需要三级? 前面已经说明了三级缓存中第三级存放的是一段处理逻辑。现在先看看第二级缓存earlySingletonObjects,从名字上能看出它是存放早期对象的。 用前面的例子再来梳理一下流程: 实例化A类->把lamda表达式(a)放入第三级缓存->a对象的依赖注入->实例化B类->把lamda表达式(b)放入第三级缓存->b对象的依赖注入 ->判断是否发生循环依赖 (咱们的例子是)->从第三级缓存中获取lamda表达式(a)->执行lamda表达式(a)并将结果放入第二级缓存earlySingletonObjects中->早期对象赋值给B.a。 看完上面的流程后就知道,earlySingletonObjects中存放的是执行完AOP逻辑后的a对象,但是a现在还没有走完流程(至少A.b属性后面的属性都还没赋值)所以称其为早期对象。可以看到第二级缓存存放的是第三级缓存执行后的返回结果。思考一下,这里是不是可以不用第二级缓存,每次都执行第三级缓存的表达式拿到结果一样吗?我理解的是不一样, 如果还有个C类,它和A、B都发生了循环依赖, 当B在注入C的实例对象时,会创建c对象,c对象也依赖了a,所以会再次去缓存中找a对象,如果没有第二级缓存,再次执行lamda表达式(a),这时候会新生成一个代理对象。这还是单例吗?简单理解:第二级缓存是为了排重(只是个人理解)。 现在看看第一级缓存singletonObjects,bean走完生命周期的流程后,会将对象放到第一季缓存中,并清除掉对应的半成品(第二级缓存和第三级缓存)。当A、B、C这三个发生循环依赖的对象生命周期全部走完后, 三级缓存中只有singletonObjects里面会存在真正的bean对象。第二级缓存和第三级缓存会在使用完后会找到合适的时机清除掉对应的元素。个人理解:加入第一级缓存的作用,就是区分成品bean和半成品bean。
怎样判断是否发生了循环依赖? spring在刚开始创建bean的时候将当前创建的bean保存到了一个名叫singletonsCurrentlyInCreation的map中。 等这个bean创建完成后便会从 singletonsCurrentlyInCreation中移除对应的项。 这样便记录了bean是否现在处于正在创建中。 再看A类、B类循环依赖的例子: getBean(A)->去singletonObjects中找a对象 (第一次get肯定找不到)->singletonsCurrentlyInCreation.put(A) -> 实例化a对象 ->a对象的依赖注入 ->getBean(B)->去singletonObjects中找b对象 (第一次get肯定找不到)->singletonsCurrentlyInCreation.put(B) -> 实例化b对象 ->b对象的依赖注入 -> 调用beanFactory.getSingleton()方法在三级缓存中逐级查找 a对象 ->第一级缓存中找不到(a还没有完成整个流程)->判断a是否在singletonsCurrentlyInCreation中。 到这一步会判断a是否正在被创建, 如果正在被创建,并且第一级缓存中没有a, 说明发生了循环依赖。 就会去第二级、第三级缓存中查找。 从beanFactory.getSingleton()方法中也能看得出来, 第二级缓存和第三级缓存只有在发生了循环依赖的时候才会被用到。 也就是说他们就是为了解决循环依赖而生的
判断PropertyValues(MutablePropertyValues是PropertyValues的实现类),如果当前Bean中的BeanDefinition中设置了PropertyValues,那么就遍历PropertyValues逐一给bea的属性赋值。上面自动注入的逻辑并没有真正给对象赋值, 而是把找到的bean添加到了MutablePropertyValues中,真正赋值其实是在这一步。@Autowired、@Resource、@Value是找到注入点后直接赋值,没用到这一步
调用initializeBean()方法,初始化bean
执行Aware,这里回调了三个Aware接口, 1、BeanNameAware:回传beanName给bean对象。 2、BeanClassLoaderAware:回传classLoader给bean对象。 3、BeanFactoryAware:回传beanFactory给对象。
初始化前, 遍历所有的BeanPostProcessor, 调用他们的postProcessBeforeInitialization()方法。 spring内部默认提供了两个初始化前的流程会在这里被调用到, 1、InitDestroyAnnotationBeanPostProcessor会在初始化前这个步骤中执行@PostConstruct的 方法。 2、ApplicationContextAwareProcessor会在初始化前这个步骤中进行其他Aware的回调:EnvironmentAware:回传环境变量、 . EmbeddedValueResolverAware:回传占位符解析器、 ResourceLoaderAware:回传资源加载器、 ApplicationEventPublisherAware:回传事件发布器、 MessageSourceAware:回传国际化资源、 ApplicationStartupAware:回传应用其他监听对象,可忽略、 ApplicationContextAware:回传Spring容器ApplicationContext
初始化, 判断当前Bean对象是否实现了InitializingBean接口,如果实现了就调用其afterPropertiesSet()方法, 执行BeanDefinition中指定的初始化方法
初始化后,遍历所有的BeanPostProcessor,执行他们的postProcessAfterInitialization()方法。 AOP就是利用这点来做的。 如果我们在配置类上面添加了@EnableAspectJAutoProxy注解,其实是 通过@Import像spring容器中注册了一个AnnotationAwareAspectJAutoProxyCreator,这个类实现了BeanPostProcessor接口
为当前bean进行AOP,我们需要先在配置类上添加@EnableAspectJAutoProxy注解,用于开启AOP功能,其实就是向spring容器中注册了AnnotationAwareAspectJAutoProxyCreator,它实现了BeanPostProcessor接口,当执行到初始化后时,会调用这个类的postProcessAfterInitialization()方法,在这个方法中,会先检查当前bean是否已经存放在earlyProxyReferences中,如果这个bean发生过循环依赖,就会放在earlyProxyReferences中(关于循环依赖在依赖注入中已经描述)。这样就可以判断出当前处理的bean是否提前进行了AOP,如果是,直接返回当前对象。 如果不是,则调用wrapIfNecessary()方法(循环依赖中进行提前AOP调用的就是这个方法),判断是否需要AOP,如果需要就使用ProxyFactory创建代理对象,如果不需要就返回原始对象。
wrapIfNecessary()方法:先判断当前bean是不是要进行aop,比如当前bean的类型是Pointcut、Advice、Advisor等,那就不需要进行aop( 因为这些类本身是spirng提供的AOP基础类,spring利用这些类来给bean做AOP)。 然后调用getAdvicesAndAdvisorsForBean()方法,获取到所有与当前bean匹配的Advice和Advisor。最后调用createProxy()方法, 基于原始bean和所匹配的Advice和Advisor创建代理对象,并返回。 如果没有找到匹配的Advice和Advisor就返回原始bean。
调用getAdvicesAndAdvisorsForBean()获取Advice/Advisor,我们在平常的开发中一般都使用AspectJ的注解(@Before、@After、@Around等)进行AOP, 还有一种方式也可以实现AOP,定义一个实现了Advisor接口的类(实际使用的时候一般要实现Advicor的子接口PointcutAdvisor,这个接口可以获取Pointcut)。 所以在这里spring会先找出所有实现了Advisor接口的bean,然后在找出来Aspectj注解方式定义的所有Advisor, 合并这两种Advisor,然后过滤出所有与当前创建的bean相关的Advisor,添加到列表中,并返回该列表。 之前的流程都是Advisor,那advice呢? 可以通过Advisor.getAdvice()拿到。这个get方法可以根据方法上的注解创建出来不同类型的Advice。在下面调用代理方法的流程中,会调用getAdvice()获取Advice,然后做后续处理。
如何查找实现了Advisor接口的bean? 这个逻辑比较简单,就是根据Advisor.class去spring容器中找,拿到的结果就是所有实现了Advisor接口的bean
如何查找被Aspectj注解? 先去spring容器中根据Object.class找到所有的bean,遍历这些bean, 判断bean上是否有@AspectJ注解, 就说明这个bean是个切面。然后取出切面bean中所有除了被@Pointcut注解标注之外的所有方法并添加到一个列表中,利用排序器对这个列表中的method进行排序,这个排序器制定的排序规则是:根据方法上的注解,从前到后依次为:@Around ->@Before-> @After-> @AfterReturning -> @AfterThrowing ->没有前面5个注解的method 最后,遍历排完序的method列表,解析方法上的注解(只解析上面这5个注解),获取注解中的Pointcut表达式并封装成AspectJExpressionPointcut对象( 如果获取不到表达式,则返回null,下面的逻辑就不走了。上面取出来的所有方法,最后只保留添加了这5个注解的方法。吐槽一下spring,这段逻辑很怪,为什么不在找方法的时候就过滤掉?)。 然后在将表达式对象、method等信息封装成Advisor对象,添加到Advisor列表中 。 遍历完排完序的method列表,最终得到一个有效的Advisor列表。
如何过滤Advisor? 遍历合并后的列表中的所有Advisor,通过Advisor.pointcut与当前正在创建的bean做匹配,先判断类,如果类不匹配则不处理这个Advisor,如果类匹配再判断方法,只要当前处理的bean中有一个method与pointcut的methodMatcher匹配成功,就将当前遍历到的Advisor放到新的列表中。 遍历结束后,就得到了与当前bean相关的所有Advisor
调用createProxy()方法创建代理对象,
首先创建ProxyFactory对象(这个对象功能比较强大,前面的@Lazy和full 配置类都是通过ProxyFactory生成代理对象的),然后将上面获取到Advisor列表和一些其他参数设置到ProxyFactory中,并将spring当前正在创建的bean作为targetSource(被代理对象)也设置到ProxyFactory中, 最后调用proxyFactory.getProxy()方法生成代理对象( 生成对象时,会选择jdk还是cglib, 大概的逻辑是如果当前创建的bean实现了接口就用jdk,否则就用cglib,还有几个配置参数会影响这个流程,具体可以看看DefaultAopProxyFactory.createAopProxy()方法 ),并返回。 到这里spring容器就得到了bean的代理对象。
proxyFactory.getProxy()如何创建代理对象? 我们以jdk代理方式为例,会调到JdkDynamicAopProxy.getProxy()方法,这里面就一行有用的代码: return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this); 就是利用jdk生成动态代理对象,这里的重点是最后一个参数this,这就是说把JdkDynamicAopProxy本身作为了Interceptor,当我们在调用这个代理对象的方法时,会先调用到JdkDynamicAopProxy的invoke方法。 注意:到这里这个this对象里面已经保存了之前生成的Advisor列表。
当开发人员调用代理对象时是怎样的流程? 上面创建代理对象的流程中已经提到了,以jdk代理方式为例,开发人员调用代理对象时,会调用到JdkDynamicAopProxy.invoke()方法, 这个方法首先会遍历advisor列表,根据调用的类和方法与Advisor做匹配,匹配成功的Advisor会执行它的getAdvice()方法,获取到与Advisor中方法上的注解对应的advice对象(比如切面方法上的注解时@before, 那这里得到的对象类型就是AspectJMethodBeforeAdvice),然后将不同类型的Advice对象通过adapt转换成对应的MethodInterceptor对象,并添加到MethodInterceptor列表中。advisor列表遍历完成后,我们就得到了真正的代理链路chain(MethodInterceptor列表的名称就是chain)。
然后将代理对象、被代理对象、当前正在执行的被代理对象中的方法对象、方法参数、被代理类、chain、当前正在执行的代理类中方法对象整合成一个ReflectiveMethodInvocation对象,然后调用它的proceed()方法。在这个方法中会按顺序执行chain中的MethodInvocation.invoke()方法,AOP的代理逻辑正式开始。chain列表执行完后调用目标方法。
chain是排好序的, 对应的注解从前到后依次为:@Around ->@Before-> @After-> @AfterReturning -> @AfterThrowing (这里其实存放的都是MethodInvocation接口的实现类,但类名太长,就写成了对应的注解)。举个例子:如果我们现在调用的目标对象只匹配出一组@Before和@After, 那就是先执行MethodBeforeAdviceInterceptor.invoke()方法, 再执行AspectJAfterAdvice.invoke()方法(这里没写错AspectJAfterAdvice实现了MethodInvocation接口)。
MethodBeforeAdviceInterceptor.invoke()的代码: public Object invoke(MethodInvocation mi) throws Throwable { //执行@Before切面方法 this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis()) //执行下一个MethodInvocation, 如果没了就执行目标方法 return mi.proceed(); }
AspectJAfterAdvice.invoke()的代码: public Object invoke(MethodInvocation mi) throws Throwable { try { //先执行执行下一个MethodInvocation,如果没了就执行目标方法 return mi.proceed(); } finally { //再执行@After切面方法 invokeAdviceMethod(getJoinPointMatch(), null, null); } }
spring事务, spring事务是基于AOP功能实现的, 当我们使用@EnableTransactionManagement注解开启事务功能时,这个注解会利用Import机制向spring容器注册一个AutoProxyRegistrar 和一个ProxyTransactionManagementConfiguration。 AutoProxyRegistrar做的一件比较重要的事就是向spring容器注册一个InfrastructureAdvisorAutoProxyCreator。这个InfrastructureAdvisorAutoProxyCreator其实和开启AOP时注入的AnnotationAwareAspectJAutoProxyCreator继承了相同的父类。 InfrastructureAdvisorAutoProxyCreator里面并没有多少代码,几乎所有的逻辑都是通过父类实现的(模板模式),所以,它的功能就是做AOP。 ProxyTransactionManagementConfiguration就是一个配置类,里面配置了与事务相关的advisor、advice 和 pointcut。 简单理解:@EnableTransactionManagement向spring注册了两个bean:InfrastructureAdvisorAutoProxyCreator和ProxyTransactionManagementConfiguration
ProxyTransactionManagementConfiguration: 这就是一个被@Configuration注解标注了的配置类,这里面包含了三个@Bean方法,即向spring导入三个bean: 1、BeanFactoryTransactionAttributeSourceAdvisor:一个Advisor 2、AnnotationTransactionAttributeSource:相当于AOP中Advisor里面的Pointcut判断类或方法上是否存在在@Transactional注解,如果有就说明匹配,创建代理对象。 3、TransactionInterceptor:相当于AOP中Advisor里面的Advice,它实现了AOP中的MethodInterceptor接口,所以它就是代理逻辑,当某个类中存在@Transactional注解时,到时就产生一个代理对象作为Bean,代理对象在执行某个方法时,最终就会进入到TransactionInterceptor的invoke()方法。
InfrastructureAdvisorAutoProxyCreator: 这个类的主要作用就是开启自动代理,也就是一个BeanPostProcessor,会在初始化后步骤中调用它的postProcessAfterInitialization()方法去寻找Advisor类型的Bean,并判断当前某个Bean是否有匹配的Advisor,是否需要利用动态代理产生一个代理对象。与AOP的流程差不多。这里就会把ProxyTransactionManagementConfiguration中引入的三个bean找出来为目标对象做代理。
当开发人员调用代理对象时(就是添加了@Transactional的方法),会先调用到TransactionInterceptor的invoke()方法,而Invoke()又直接调用了invokeWithinTransaction()方法, 这个方法是事务的核心方法, 事务的传播行为,什么时候该创建新的connection, 提交事务,以及回滚事务,还有调用目标方法,都在这个方法里面。
invokeWithinTransaction()方法, 首先获取到目标方法上@Transactional中配置的属性值,根据这些属性值生成TransactionManager对象,我们平常用的一般都是PlatformTransactionManager类型 ( 另外还有两种类型:ReactiveTransactionManager 它只是执行方式是响应式的, 和CallbackPreferringPlatformTransactionManager拥有回调功能的PlatformTransactionManager。 这两种不常用 ),然后将TransactionManager强转为PlatformTransactionManager,获取当前方法的名称。 根据PlatformTransactionManager、当前方法名称和TransactionAttribute调用createTransactionIfNecessary()方法判断是否有必要创建事务, 如果是则创建,并返回TransactionInfo。这里就涉及到事务传播机制的实现。TransactionInfo表示一个逻辑事务,比如两个逻辑事务属于同一个物理事务。 再往下的逻辑就是执行下一个Interceptor或被代理对象中的方法和目标方法.代码逻辑: try{ // 执行下一个Interceptor或被代理对象中的方法 retVal = invocation.proceedWithInvocation(); }catch(Throwable ex){ // 抛异常了,则回滚事务 completeTransactionAfterThrowing(txInfo, ex); throw ex; }finally{ //恢复被挂起的老事务, 比如事务传播行为配置了new,执行完下游方法时,需要把 //上游方法被挂起的事务恢复回来。后面会介绍事务时如何被挂起的 cleanupTransactionInfo(txInfo); } // 提交事务 commitTransactionAfterReturning(txInfo); return retVal;
createTransactionIfNecessary()方法调用到了AbstractPlatformTransactionManager.getTransaction()方法,它是真正开启事务的方法。 这里面的逻辑有点复杂,大概的流程是:判断当前线程中是否已经存在事务 ( 创建数据库链接的时候会把链接放到ThreadLocal中,事务执行完后,会清除ThreadLocal )。如果存在则调用handleExistingTransaction()方法, 如果不存在则根据开发人员配置的不同的事务传播行为来处理。 其实handleExistingTransaction()方法的核心逻辑也是根据开发人员配置的不同的事务传播行为来处理。 这些逻辑的最终目的就是根据事务传播行为决定要不要创建创建新的链接。区别是,由于handleExistingTransaction()方法处理的是当前线程已经存在事务的情况,所以如果创建新的事务(其实就是创建新的数据库链接),需要考虑挂起老的事务,等新事务执行完还要恢复。 而当前线程中不存在事务的情况则不需要考虑挂起老事务。 然后就是创建新的数据库连接,会把开发人员配置的事务隔离级别、超时时间、以及commit=false等参数都设置到connection中。 关于事务的传播行为后面提供一个例子来帮助理解这里面的流程。
假设现在有都添加了@Tranctional注解的两个方法a()和b() @Component public class UserService { //这里需要自己注入自己,解决a()方法调用b()方法时,事务失效的问题 //如果直接在a()方法中直接调用b(),不会走代理逻辑。 @Autowired private UserService userService; @Transactional public void a() { // a()方法中的sql(省略) userService.b(); } @Transactional public void b() { // b方法中的sql(省略) } } 借着这个例子说一下为什么在a()方法中直接调用b()不走代理逻辑,自己注入自己就可以。 我们假设还有一个UserController, 在UserController中使用@Autowired注入了UserService。 那在spring容器启动时创建UserController时会注入UserService,这时候spring会调用getBean(UserService)获取UserService对象, 第一次获取肯定是走创建逻辑,创建UserService的bean对象->给UserService注入属性->初始化后,AOP返回代理对象->将代理对象赋值给userController的属性中。 此时很明显userController中调用UserService的a()方法走的是代理逻辑( 执行流程是userController中的方法 -> userService代理对象的invoke()方法->method.invoke(target, args)->最终执行到userService.a() ).但是当这个流程执行到a()方法中,当前对象就不在是代理对象,而是UserService对象本身,那在a()中直接调用b()时(实际上调用的是this.b()),肯定就不会走代理逻辑(事务失效)。 如果自己注入自己。在Spring启动时创建UserService,依赖注入时会注入自己,注入时spring会检查到存在循环依赖,从而引发提前AOP,然后将产生的代理对象设置到userService属性中。这样在a()方法中调用 userService.b();自然走的是代理逻辑。 其实spring默认用的是jdk或cglib动态代理,这两种方式都相当于在目标方法前加了一个代理层,执行到目标方法时就相当于跨过了代理层。 所以会出现事务失效的问题。但如果我们使用AspectJ应该就不会出现这种问题(个人观点,没测试过),因为AspectJ是直接修改了目标方法的字节码文件,每次调用目标方法时都会经过代理逻辑。
默认情况下传播机制为REQUIRED,表示当前如果没有事务则新建一个事务,如果有事务则在当前事务中执行。所以这种情况的正常执行流程如下: 1. 新建一个数据库连接conn 2. 设置conn的autocommit为false 3. 执行a()方法中的sql 4. 执行b()方法中的sql 5. 执行conn的commit()方法进行提交。 如果在执行a()方法或b()方法时抛出了异常, 那第5步就是执行行conn的rollback()方法进行回滚。 需要注意的是,抛出的异常不能被捕获,如果业务需要捕获,需要在catch块中再次抛出异常让spring的事务机制感知到异常,或者使用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();进行强制回滚。
如果配置了传播行为@Transactional(propagation = Propagation.REQUIRES_NEW) 1. 新建一个数据库连接conn 2. 设置conn的autocommit为false 3. 执行a()方法中的sql 4. 又新建一个数据库连接conn2 5.设置conn2的autocommit为false 6. 执行b()方法中的sql 7.执行conn2的commit()方法进行提交。 8.执行conn的commit()方法进行提交。 如果在方法b中出现了异常, 第6步以后的就变成: 7.b()方法抛出异常 8.执行conn2的rollback()方法进行回滚 9.继续抛异常,对于a()方法而言,它会接收到一个异常,然后抛出 10. 执行conn的rollback()方法进行回滚,最终还是两个方法中的sql都回滚了 spring的事务传播机制情况非常多, 配置了什么隔离级别,在那个方法中抛异常,try.. catch是如何实现的, 这些场景都会影响到事务提交/回滚
在这个流程中出现了一个概念需要介绍一下,就是TransactionSynchronization类型。 Spring事务有可能会提交,回滚、挂起、恢复,所以Spring事务提供了一种机制,可以让程序员来监听当前Spring事务所处于的状态。我们可以在执行业务代码时,在执行sql前通过TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { ............... } )的方式利用事务同步管理器注册一个我们自己对TransactionSynchronization接口的实现。 这样我们就可以在事务的每个阶段做点自己的事情了。
注册DisposableBeanAdapter,用于bean销毁。 这个步骤会去判断当前创建的Bean是不是DisposableBean,判断条件有4个,1、当前bean是否实现了DisposableBean接口, 2、当前Bean是否实现了AutoCloseable接口, 3、BeanDefinition中是否指定了destroyMethod, 4、调用DestructionAwareBeanPostProcessor.requiresDestruction(bean)进行判断ApplicationListenerDetector中直接使得ApplicationListener是DisposableBean,. InitDestroyAnnotationBeanPostProcessor中使得拥有@PreDestroy注解了的方法就是 DisposableBean。 经过这四个判断后,把符合上述任意一个条件的Bean适配成DisposableBeanAdapter对象,并存入disposableBeans中(一个LinkedHashMap)。 当调用applicationContext.close();关闭spring容器时首先发布ContextClosedEvent事件,然后调用lifecycleProcessor的onCloese()方法,最后销毁单例bean。销毁单例bean时会把单例bean从单例池中逐一移除,调用disposableBean的destroy(),除此之外还会清空一些spring内部的集合。如果这个disposableBean还被其他Bean依赖了,那么也得销毁其他Bean 除了调用applicationContext.close();可以触发bean的销毁方法外,还有一种方式也可以触发, 我们可以执行applicationContext.registerShutdownHook()方法给jvm注册一个回调方法, 当jvm即将关闭时会回调它。 这个时基于jvm完成的
获取/创建好bean后,检查通过name所获得到的beanInstance的类型是否是requiredType。我们在调用getBean()的时候传入类型为Class的requiredType参数,spring在获取到bean后会判断获取到的bean是否为requiredType类型的,如果不是则获取 类型转换器做类型做换,如果转换失败则会抛出异常。
所有的非懒加载单例Bean都创建完后,获取所有的非懒加载单例Bean,判断Bean是否实现了SmartInitializingSingleton接口,如果实现了该接口,调用bean.afterSingletonsInstantiated()方法。执行到这个方法的时候,说明所有的非懒加载单例Bean都已经完全创建好了,并且所有的Ioc、aop、beanPostProcessor逻辑都已经执行完成。
调用finishRefresh()方法,这个方法中最重要的步骤就是发布事件,告诉事件监听器spring已经启动完成。另外这个方法还清理前面记录的类资源(因为已经有了BeanDefinition)。触发DefaultLifecycleProcessor的onRefresh方法,简单来讲就是触发那些实现了Lifecycle的bean的start方法并将running状态设置为true。Lifecycle表示的是ApplicationContext的生命周期,可以定义一个SmartLifecycle来监听ApplicationContext的启动和关闭
本图中有涉及到三个重要的BeanPostProcessor为了直观好找,分别使用三个不同的颜色标注了这三个BeanPostProcessor并且加大了字码
spring的生命周期流程, 从前到后依次为: 在下面的脑图中,会用带有星号的图形标注生命周期中对应的内容
1. 启动ApplicaionContext
创建BeanFactory
初始化BeanFactory
执行BeanFactory后置处理器扫描出BeanDefinition
生成BeanDefinition
合并beanDefinition
加载类
实例化前
推断构造方法
实例化
BeanDefinition的后置处理器
实例化后
填充属性
填充属性后
执行Aware
初始化前
初始化
初始化后