spring常用知识体系
Posted 朱培
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring常用知识体系相关的知识,希望对你有一定的参考价值。
Spring
- IOC
- BeanFactory
- BeanFactory 和 ApplicationContext有什么区别?
- BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。BeanFactroy采用的是延迟加载形式来注入Bean的。
- ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能。ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。
- BeanFactory 和 FactoryBean有什么区别?
- BeanFactory是一个工厂类(接口), 它负责生产和管理bean,是从Ioc容器中获取Bean,检索Ioc容器中是否包含指定的Bean。如果要获取FactoryBean的实现类,就要getBean(&BeanName),在BeanName之前加上&。
- FactoryBean是一个接口,是一种工厂bean,在IOC容器中的Bean实现了FactoryBean后,通过getBean(String BeanName)获取到的Bean对象是这个实现类中的getObject()方法返回的对象。
- BeanFactory 和 ApplicationContext有什么区别?
- ioc的加载过程
- 简答
- 1.spring启动之后,读取xml配置信息、注解、java类,将每一个bean配置解析为一个BeanDefinition对象,再将所有的BeanDefinition对象放入BeanDefinitionMap中(其中bean的id为key键,BeanDefinition对象为值)。2.从beanDefinitoinMap中取出所有的非懒加载beanDefinition实例化bean(通过反射来实例化),并将bean加载进缓存,即放入singletonObjects(仅单例)的map中。
- 详细过程
- 1、实例化化容器ApplicationContext的对象;2.调用bean工厂后置处理器完成扫描;3.循环解析扫描出来的类信息;4.实例化一个BeanDefinition对象来存储解析出来的信息;5.把实例化好的beanDefinition对象put到beanDefinitionMap当中缓存起来,以便后面实例化bean;6.再次调用其他bean工厂后置处理器;7.当然spring还会干很多事情,比如国际化,比如注册BeanPostProcessor等等,如果我们只关心如何实例化一个bean的话那么这一步就是spring调用finishBeanFactorylnitialization方法来实例化单例的bean,实例化之前spring要做验证,需要遍历所有扫描出来的类,依次判断这个bean是否Lazy, 是否prototype, 是否abstract等等;8.如果验证完成spring在实例化一个bean之 前需要推断构造方法,因为spring实例化对象是通过构造方法反射,故而需要知道用哪个构造方法:9.推断完构造方法之后spring调用构造方法反射实例化-个对象; 这个时候对象已经实例化出来了,但是并不是一个完整的bean,最简单的体现是这个时候实例化出来的对象属性是没有注入,所以不是一个完整的bean;10.spring处理合并后的beanDefinition11.populateBean判断是否需要完成属性注入12.如果需要完成属性注入,则开始注入属性13.判断bean的类型回调Aware接口14、最后容器刷新 发布刷新时间
- 简答
- bean的生命周期
- 总结:实例化 -> 属性赋值 -> 初始化 -> 销毁1. 实例化Bean对象,这个时候Bean的对象是非常低级的,基本不能够被我们使用,因为连最基本的属性都没有设置,可以理解为连Autowired注解都是没有解析的;2. 填充属性,当做完这一步,Bean对象基本是完整的了,可以理解为Autowired注解已经解析完毕,依赖注入完成了;3. 如果Bean实现了BeanNameAware接口,则调用setBeanName方法;4. 如果Bean实现了BeanClassLoaderAware接口,则调用setBeanClassLoader方法;5. 如果Bean实现了BeanFactoryAware接口,则调用setBeanFactory方法;6. 调用BeanPostProcessor的postProcessBeforeInitialization方法;7. 如果Bean实现了InitializingBean接口,调用afterPropertiesSet方法;8. 如果Bean定义了init-method方法,则调用Bean的init-method方法;9. 调用BeanPostProcessor的postProcessAfterInitialization方法;当进行到这一步,Bean已经被准备就绪了,一直停留在应用的上下文中,直到被销毁;10. 如果应用的上下文被销毁了,如果Bean实现了DisposableBean接口,则调用destroy方法,如果Bean定义了destory-method声明了销毁方法也会被调用。
- Aware
- Spring Aware 的目的是为了让 Bean 获得 Spring 容器的服务。使用Aware机制对Bean实现方法回调进行扩展。注意:所有的Aware方法都是在初始化阶段之前调用的!
- 常见的 Spring Aware 接口
- BeanNameAware:获取容器中 Bean 的名称
- BeanFactoryAware 获取当前 BeanFactory ,这样可以调用容器的服务
- ApplicationContextAware 获得ApplicationContext对象(spring上下文)
- MessageSourceAware 获取 Message Source 相关文本信息
- ApplicationEventPublisherAware 发布事件
- ResourceLoaderAware 获取资源加载器,这样获取外部资源文件
- 扩展点BeanPostProcessor
- BeanPostProcessor的执行是定义在容器的刷新过程中,容器刷新对象具体的方法为:AbstractApplicationContext.refresh(),在refresh方法执行的调用栈中会去调用AbstractAutowireCapableBeanFactory.doCreateBean()方法。始化方法前后会分别调用applyBeanPostProcessorsBeforeInitialization()和applyBeanPostProcessorsAfterInitialization()。
- 实现了这些接口的Bean会切入到多个Bean的生命周期中。正因为如此,这些接口的功能非常强大,Spring内部扩展也经常使用这些接口,例如自动注入以及AOP的实现都和他们有关。InstantiationAwareBeanPostProcessor、BeanPostProcessor
- BeanPostProcessor注册时机与执行顺序
- Spring是先执行registerBeanPostProcessors()进行BeanPostProcessors的注册,然后再执行finishBeanFactoryInitialization创建我们的单例非懒加载的Bean。
- 关于执行顺序这里需要引入两个排序相关的接口:PriorityOrdered > Ordered > 默认
- 扩展点
- 会影响多个Bean的方法BeanPostProcessorInstantiationAwareBeanPostProcessor
- 只影响单个Bean的方法Aware Aware Group1:BeanNameAware、BeanClassLoaderAware、BeanFactoryAware Aware Group2:EnvironmentAware、EmbeddedValueResolverAware、ApplicationContextAware(ResourceLoaderAware\\ApplicationEventPublisherAware\\MessageSourceAware)生命周期 : InitializingBean、DisposableBean
- spring-context中常用的扩展点:ApplicationContextAware: 可获取applicationContextApplicationListener/ApplicationEvent: 事件监听(很重要)ApplicationEventMulticaster: 事件派发,管理多个ApplicationListener并向它们发布事件。通常,ApplicationContext 会使用 ApplicationEventMulticaster 作为实际发布事件的委托。ImportSelector: 在初始化bean工厂时期[invokeBeanFactoryPostProcessors()] 会调用。ImportSelector的导入实现是通过BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry()来实现的。ImportBeanDefinitionRegistrar: 需要与 @Import 和 @Configuration 共同配合使用。@EnableFeignClients, @EnableDubboConfig 等都是通过ImportBeanDefinitionRegistrar 来动态注入的服务调用类到spring容器里面。SmartLifecycle/Lifecycle: Lifecycle是Spring中最基础的生命周期接口,该接口定义了容器启动和停止的方法,Lifecycle是容器生命周期级别的。SmartLifecycle是对Lifecycle的一个扩展接口。当需要根据Spring容器的生命周期,来做一些逻辑时,一般会自定义一个类实现SmartLifecycle接口;很少有人会使用Lifecycle。多个SmartLifecycle时,可以使用接口中的getPhase()方法控制回调的顺序。方法返回值越小,越靠前执行start()方法,越靠后执行stop()方法。SmartLifecycle这个接口的isAutoStartup()方法,一定要返回true,容器启动时才会回调SmartLifecycle的start()方法。ClassPathBeanDefinitionScanner: 可以扫描路径下的类(里面的一些方法可以脱离spring环境独立使用).
- BeanFactory
- DI依赖注入
- 依赖注入实现方式
- 依赖注入分为接口注入(Interface Injection),Setter方法注入(Setter Injection)和构造器注入,其中接口注入由于在灵活性和易用性比较差,现在从Spring4开始已被废弃。
- 自动注入模式
- byName
- 找到所有set方法所对应的XXX部分的名字根据XXX部分的名字去获取bean
- byType
- 获取到set方法中的唯一参数的参数类型,并且根据该类型去容器中获取bean如果找到多个,会报错。
- constructor
- 构造方法注入相当于byType+byName.当某个bean是通过构造方法来注入时,spring利用构造方法的参数信息从Spring容器中去找bean,找到bean之后作为参数传给构造方法,从而实例化得到一个bean对象,并完成属性赋值
- default,表示默认值
- 自动探测,如果有构造方法,通过 construct的方式自动装配,否则使用 byType的方式自动装配
- no,表示关闭autowire
- byName
- @Autowired注解与@Resource注解的区别与用法
- @Autowired默认按类型(byType)装配(这个注解是属于spring的),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用,Qualifier可以在多个相同bean的情况下,消除歧义
- @Resource(这个注解属于java的), 默认根据名字name注入,其次按照类型搜索,也可以通过name和type属性进行选择性注入
- 寻找注入点
- 遍历当前类的所有的属性字段Field查看字段上是否存在@Autowired、@Value、@Inject中的其中任意一个,存在则认为该字段是一个注入点如果字段是static的,则不进行注入获取@Autowired中的required属性的值将字段信息构造成一个AutowiredFieldElement对象,作为一个注入点对象添加到currElements集合中。遍历当前类的所有方法Method判断当前Method是否是桥接方法,如果是找到原方法查看方法上是否存在@Autowired、@Value、@Inject中的其中任意一个,存在则认为该方法是一个注入点如果方法是static的,则不进行注入获取@Autowired中的required属性的值将方法信息构造成一个AutowiredMethodElement对象,作为一个注入点对象添加到currElements集合中。遍历完当前类的字段和方法后,将遍历父类的,直到没有父类。最后将currElements集合封装成一个InjectionMetadata对象,作为当前Bean对于的注入点集合对象,并缓存。
- 字段注入
- 遍历所有的AutowiredFieldElement对象。将对应的字段封装为DependencyDescriptor对象。调用BeanFactory的resolveDependency()方法,传入DependencyDescriptor对象,进行依赖查找,找到当前字段所匹配的Bean对象。将DependencyDescriptor对象和所找到的结果对象beanName封装成一个ShortcutDependencyDescriptor对象作为缓存,比如如果当前Bean是原型Bean,那么下次再来创建该Bean时,就可以直接拿缓存的结果对象beanName去BeanFactory中去那bean对象了,不用再次进行查找了利用反射将结果对象赋值给字段。
- set方法注入
- 遍历所有的AutowiredMethodElement对象遍历将对应的方法的参数,将每个参数封装成MethodParameter对象将MethodParameter对象封装为DependencyDescriptor对象调用BeanFactory的resolveDependency()方法,传入DependencyDescriptor对象,进行依赖查找,找到当前方法参数所匹配的Bean对象。将DependencyDescriptor对象和所找到的结果对象beanName封装成一个ShortcutDependencyDescriptor对象作为缓存,比如如果当前Bean是原型Bean,那么下次再来创建该Bean时,就可以直接拿缓存的结果对象beanName去BeanFactory中去那bean对象了,不用再次进行查找了利用反射将找到的所有结果对象传给当前方法,并执行。
- @Resource注解底层原理
- 依赖注入实现方式
- AOP
- 基本概念
- AOP (面向切面编程)要实现的是在我们原来写的代码的基础上,进行一定的包装,如在方法执行前、方法返回后、方法抛出异 常后等地方进行一定的拦截处理或者叫增强处理。
- AOP实现的关键在于 代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。
- 动态代理
- JDK动态代理
- 只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对象。
- CGLIB动态代理
- 是一个代码生成的类库,通过Enhancer可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
- ProxyFactory
- Spring中将两种代理进行了封装,如果实现了接口,那么ProxyFactory底层就会用jdk动态代理,如果没有实现接口,就会用cglib技术
- 示例:ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.setTarget(target);proxyFactory.addAdvice(new MethodInterceptor() @Override public Object invoke(MethodInvocation invocation) throws Throwable System.out.println("before..."); Object result = invocation.proceed(); System.out.println("after..."); return result; );
- JDK动态代理
- Advice
- 表示通知,表示在一个特定连接点上所采取的动作。
- Before Advice:方法之前执行After returning advice:方法return后执行After throwing advice:方法抛异常后执行After (finally) advice:方法执行完finally之后执行,这是最后的,比return更后Around advice:这是功能最强大的Advice,可以自定义执行顺序
- Spring会把五个注解解析为对应的Advice类:@Before:AspectJMethodBeforeAdvice,实际上就是一个MethodBeforeAdvice@AfterReturning:AspectJAfterReturningAdvice,实际上就是一个AfterReturningAdvice@AfterThrowing:AspectJAfterThrowingAdvice,实际上就是一个MethodInterceptor@After:AspectJAfterAdvice,实际上就是一个MethodInterceptor@Around:AspectJAroundAdvice,实际上就是一个MethodInterceptor
- Advisor
- 一个Advisor是有一个Pointcut和一个Advice组成的,通过Pointcut可以指定要需要被代理的逻辑
- EnableAspectJAutoProxy
- 可以通过注解来解析数据,解析之后得到对应的Pointcut对象、Advice对象,生成Advisor对象,扔进ProxyFactory中,进而产生对应的代理对象
- Aspect
- 表示切面,比如被@Aspect注解的类就是切面,可以在切面中去定义Pointcut、Advice等等,采用编译期织入和类加载期织入的方式织入切面
- Join point
- Join point:表示连接点,表示一个程序在执行过程中的一个点,比如一个方法的执行,比如一个异常的处理,在Spring AOP中,一个连接点通常表示一个方法的执行。
- Pointcut
- Pointcut:表示切点,用来匹配一个或多个连接点,Advice与切点表达式是关联在一起的,Advice将会执行在和切点表达式所匹配的连接点上
- Introduction
- 引入:引入允许我们向现有类添加新方法或属性。可以使用@DeclareParents来给所匹配的类添加一个接口,并指定一个默认实现
- Target object
- 被一个或者多个切面(aspect)所通知(advise)的对象。它通常是一个代理对象。也有人把它叫做 被通知(adviced) 对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxied) 对象。
- AOP proxy
- 表示代理工厂,用来创建代理对象的,在Spring Framework中,要么是JDK动态代理,要么是CGLIB代理
- Weaving
- 表示织入,表示创建代理对象的动作,这个动作可以发生在编译时期(比如Aspejctj),或者运行时,比如Spring AOP
- 同一个aspect,不同advice的执行顺序
- 没有异常情况下的执行顺序:around before advicebefore advicetarget method 执行around after adviceafter adviceafterReturning
- 有异常情况下的执行顺序:around before advicebefore advicetarget method 执行around after adviceafter adviceafterThrowing:异常发生java.lang.RuntimeException: 异常发生
- 代理对象创建过程
- jdk
- 在构造JdkDynamicAopProxy对象时,会先拿到被代理对象自己所实现的接口,并且额外的增加SpringProxy、Advised、DecoratingProxy三个接口,组合成一个Class[],并赋值给proxiedInterfaces属性并且检查这些接口中是否定义了equals()、hashcode()方法执行Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this),得到代理对象,JdkDynamicAopProxy作为InvocationHandler,代理对象在执行某个方法时,会进入到JdkDynamicAopProxy的invoke()方法中
- cglib
- 创建Enhancer对象设置Enhancer的superClass为通过ProxyFactory.setTarget()所设置的对象的类设置Enhancer的interfaces为通过ProxyFactory.addInterface()所添加的接口,以及SpringProxy、Advised、DecoratingProxy接口设置Enhancer的Callbacks为DynamicAdvisedInterceptor最后创建一个代理对象,代理对象在执行某个方法时,会进入到DynamicAdvisedInterceptor的intercept()方法中
- jdk
- 代理对象执行过程
- 在使用ProxyFactory创建代理对象之前,需要往ProxyFactory先添加Advisor代理对象在执行某个方法时,会把ProxyFactory中的Advisor拿出来和当前正在执行的方法进行匹配筛选把和方法所匹配的Advisor适配成MethodInterceptor把和当前方法匹配的MethodInterceptor链,以及被代理对象、代理对象、代理类、当前Method对象、方法参数封装为MethodInvocation对象调用MethodInvocation的proceed()方法,开始执行各个MethodInterceptor以及被代理对象的对应方法按顺序调用每个MethodInterceptor的invoke()方法,并且会把MethodInvocation对象传入invoke()方法直到执行完最后一个MethodInterceptor了,就会调用invokeJoinpoint()方法,从而执行被代理对象的当前方法
- AOP的底层原理
- 执行过程图
- AOP有几种实现方式
- 通过Spring API实现AOP,通过编写增强类来继承Spring API提供的接口,例如AfterReturningAdvice、MethodBeforeAdvice
- 通过自定义类来实现,自定义切面类,且封装公共业务
- 通过注解实现,例如@Aspect、@Before等
- 基本概念
- 配置类解析和扫描过程
- 获取配置类
- 在启动Spring时,需要传入一个AppConfig.class给ApplicationContext,ApplicationContext 会根据AppConfig类封装为一个BeanDefinition,这种BeanDefinition我们把它称为配置类 BeanDefinition。
- ConfigurationClassPostProcessor中会把配置类BeanDefinition取出来
- 构造一个ConfigurationClassParser用来解析配置类BeanDefinition,并且会生成一个配置类对 象ConfigurationClass
- 解析配置类
- 如果配置类上存在@Component注解,那么解析配置类中的内部类(这里有递归,如果内部类 也是配置类的话)
- 如果配置类上存在@PropertySource注解,那么则解析该注解,并得到PropertySource对象, 并添加到environment中去
- 如果配置类上存在@ComponentScan注解,那么则解析该注解,进行扫描,扫描得到一系列的 BeanDefinition对象,然后判断这些BeanDefinition是不是也是配置类BeanDefinition(只要存 在@Component注解就是配置类,所以基本上扫描出来的都是配置类),如果是则继续解析该 配置类,(也有递归),并且会生成对应的ConfigurationClass
- 如果配置类上存在@Import注解
- 如果是ImportSelector,那么调用执行selectImports方法得到类名,然后在把这个类当做 配置类进行解析
- 如果是一个普通类型,就直接放到ConfigurationClass中
- 如果是ImportBeanDefinitionRegistrar,那么则生成一个ImportBeanDefinitionRegistrar 实例对象,并添加到配置类对象中(ConfigurationClass)的 importBeanDefinitionRegistrars属性中。
- 如果配置类上存在@ImportResource注解,那么则把导入进来的资源路径存在配置类对象中的 importedResources属性中。
- 如果配置类中存在@Bean的方法,那么则把这些方法封装为BeanMethod对象,并添加到配置 类对象中的beanMethods属性中。
- 如果配置类实现了某些接口,则看这些接口内是否定义了@Bean的默认方法
- 如果配置类有父类,则把父类当做配置类进行解析
- 读取配置
- 每个ConfigurationClass解析完之后就会把对象添加到ConfigurationClasses中,接下来就利用reader来进一步解析ConfigurationClass
- 如果是通过@Import注解导入进来的,则把这个类生成一个 BeanDefinition,同时解析这个类上@Scope,@Lazy等注解信息,并注册BeanDefinition
- 如果存在一些BeanMethod,也就是定义了一些@Bean,那么则解 析这些@Bean,并生成对应的BeanDefinition,并注册
- 如果ConfigurationClass中导入了一些资源文件,比如xx.xml,那么则解析这些xx.xml文 件,得到并注册BeanDefinition
- 如果ConfigurationClass中导入了一些ImportBeanDefinitionRegistrar,那么则执行对应 的registerBeanDefinitions进行BeanDefinition的注册
- 总结
- 解析AppConfig类,生成对应的ConfigurationClass再扫描,扫描到的类都会生成对应的BeanDefinition,并且同时这些类也是ConfigurationClass再解析ConfigurationClass的其他信息,比如@ImportResource注解的处理,@Import注解的处理,@Bean注解的处理
- 获取配置类
- spring启动过程
- Spring启动过程主要做了什么?
- 1、构造一个BeanFactory对象2、解析配置类,得到BeanDefinition,并注册到BeanFactory中3、因为ApplicationContext还支持国际化,所以还需要初始化MessageSource对象4、因为ApplicationContext还支持事件机制,所以还需要初始化ApplicationEventMulticaster对象5、把用户定义的ApplicationListener对象添加到ApplicationContext中,等Spring启动完了就要发布事件了6、创建非懒加载的单例Bean对象,并存在BeanFactory的单例池中。7、调用Lifecycle Bean的start()方法8、发布ContextRefreshedEvent事件
- refresh()底层原理流程
- 可以参考:https://www.processon.com/view/link/5f60a7d71e08531edf26a919
- 在调用AnnotationConfigApplicationContext的构造方法之前,会调用父类GenericApplicationContext的无参构造方法,会构造一个BeanFactory,为DefaultListableBeanFactory。
- 构造AnnotatedBeanDefinitionReader(主要作用添加一些基础的PostProcessor,同时可以通过reader进行BeanDefinition的注册),同时对BeanFactory进行设置和添加PostProcessor(后置处理器)
- 构造ClassPathBeanDefinitionScanner(主要作用可以用来扫描得到并注册BeanDefinition),同时设置environment、resourceLoader
- 利用reader注册AppConfig为BeanDefinition,类型为AnnotatedGenericBeanDefinition
- 接下来就是调用refresh方法,先执行prepareRefresh(),可以允许子容器设置一些内容到Environment中
- obtainFreshBeanFactory():进行BeanFactory的refresh,在这里会去调用子类的refreshBeanFactory方法,具体子类是怎么刷新的得看子类,然后再调用子类的getBeanFactory方法,重新得到一个BeanFactory
- prepareBeanFactory(beanFactory)
- 设置表达式解析器
- 添加Bean的后置处理器,ApplicationContextAwareProcessor、ApplicationListenerDetector等
- postProcessBeanFactory(beanFactory) : 提供给AbstractApplicationContext的子类进行扩展,具体的子类,可以继续向BeanFactory中再添加一些东西
- invokeBeanFactoryPostProcessors(beanFactory):执行BeanFactoryPostProcessor
- registerBeanPostProcessors(beanFactory),可以自己定义了一些BeanPostProcessor,在这一步就会把BeanFactory中所有的BeanPostProcessor找出来并实例化得到一个对象,并添加到BeanFactory中去(属性beanPostProcessors)
- initMessageSource():让ApplicationContext拥有国际化的功能
- initApplicationEventMulticaster(),事件发布功能
- onRefresh():提供给AbstractApplicationContext的子类进行扩展,springboot就是用这个去启动容器的
- registerListeners():从BeanFactory中获取ApplicationListener类型的beanName,然后添加到ApplicationContext中的事件广播器applicationEventMulticaster中去,到这一步因为FactoryBean还没有调用getObject()方法生成Bean对象,所以这里要在根据类型找一下ApplicationListener,记录一下对应的beanName
- finishBeanFactoryInitialization(beanFactory):完成BeanFactory的初始化,主要就是实例化非懒加载的单例Bean,循环依赖也是在这里解决的
- finishRefresh():BeanFactory的初始化完后,就到了Spring启动的最后一步了
- 设置ApplicationContext的lifecycleProcessor
- 调用lifecycleProcessor的onRefresh()方法,如果是DefaultLifecycleProcessor,那么会获取所有类型为Lifecycle的Bean对象,然后调用它的start()方法,这就是ApplicationContext的生命周期扩展机制
- 发布ContextRefreshedEvent事件
- Spring启动过程主要做了什么?
- 循环依赖
- 什么是循环依赖?
- 所谓的循环依赖是指,A 依赖 B,B 又依赖 A,它们之间形成了循环依赖。或者是 A 依赖 B,B 依赖 C,C 又依 赖 A。
- 注意:原型(Prototype)的场景是不支持循环依赖的. 单例的场景才能存在循环依赖
- 循环依赖的划分
- 生成代理对象产生的循环依赖
- 解决方式:使用@Lazy注解,延迟加载使用@DependsOn注解,指定加载先后关系修改文件名称,改变循环依赖类的加载顺序
- 使用@DependsOn产生的循环依赖
- 多例循环依赖
- 解决方案:把bean改成单例
- 构造器循环依赖
- 通过使用@Lazy注解解决、objectFactory
- 生成代理对象产生的循环依赖
- 如何解决循环依赖?
- 三级缓存
- 一级缓存:singletonObjects中缓存的是已经经历了完整生命周期的bean对象。
- 二级缓存:earlySingletonObjects比singletonObjects多了一个early,表示缓存的是早期的bean对象。 早期是什么意思?表示Bean的生命周期还没走完就把这个Bean放入了earlySingletonObjects。它是一个不完整的bean
- 缓存未经过完整生命周期的bean,如果某个bean出现了循环依赖, 就会提前把这个暂时未经过完整生命周期的bean放入earlySingletonObjects中,这个bean如果 要经过AOP,那么就会把代理对象放入earlySingletonObjects中,否则就是把原始对象放入 earlySingletonObjects,但是不管怎么样,就是是代理对象,代理对象所代理的原始对象也是 没有经过完整生命周期的,所以放入earlySingletonObjects我们就可以统一认为是未经过完整 生命周期的bean。
- 三级缓存:singletonFactories中缓存的是ObjectFactory,表示对象工厂,表示用来创建早期bean对象的 工厂。它不是用来存bean的实例,而是用来存函数接口、钩子函数的!
- 缓存的是一个ObjectFactory,也就是一个Lambda表达式。在每个Bean 的生成过程中,经过实例化得到一个原始对象后,都会提前基于原始对象暴露一个Lambda表达 式,并保存到三级缓存中,这个Lambda表达式可能用到,也可能用不到,如果当前Bean没有出 现循环依赖,那么这个Lambda表达式没用,当前bean按照自己的生命周期正常执行,执行完后 直接把当前bean放入singletonObjects中,如果当前bean在依赖注入时发现出现了循环依赖 (当前正在创建的bean被其他bean依赖了),则从三级缓存中拿到Lambda表达式,并执行 Lambda表达式得到一个对象,并把得到的对象放入二级缓存((如果当前Bean需要AOP,那么 执行lambda表达式,得到就是对应的代理对象,如果无需AOP,则直接得到一个原始对象))。
- 三个缓存各自的作用
- 第一级缓存存的是对外暴露的对象,也就是我们应用需要用到的第二级缓存的作用是为了处理循环依赖的对象创建问题,里面存的是半成品对象或半成品对象的代理对象 第三级缓存的作用处理存在 AOP + 循环依赖的对象创建问题,能将代理对象提前创建
- 三级缓存
- 为什么需要二级缓存?
- 二级缓存只要是为了分离成熟Bean和纯净Bean(未注入属性)的存放, 防止多线程中在Bean还未创建完成时读取到的Bean时不完整的。所以也是为了保证我们getBean是完整最终的Bean,不会出现不完整的情况。
- 如果只用两级缓存行不行?
- 是可以的。假设使用一级缓存来存放完全初始化好的 bean,使用二级缓存来存放未初始化好的 bean(即 bean 的早期引用)。一级缓存在 initializeBean 之后进行存放,二级缓存在 createBeanInstance 之后进行记录。这样的话,就需要在之前暴露三级缓存的地方,提前将二级缓存生成后放入 Map<String, Object> earlySingletonObjects。也就是说,如果 bean 是需要被 AOP 增强的,就需要提前将 AOP 代理 bean 生成。理论上,这样处理是可以解决 AOP 代理 bean 被循环依赖的问题的,注意这里需要提前创建代理对象。(https://www.cnblogs.com/youzhibing/p/14337244.html)
- 为何需要三级缓存,而不是两级缓存?
- 主要是为了正常情况下,代理对象能在初始化完成后生成,而不用提前生成。如果每次都是在依赖注入之前会去进行AOP,这是不符合bean生命周期步骤的设计的。不提前创建好代理对象,在出现循环依赖被其它对象注入时,才实时生成代理对象。这样才符合spring的设计原则。
- 在三级缓存中,放的是生成具体对象的匿名内部类,它可以生成代理对象,也可以是普通的实例对象。使用三级缓存主要是为了保证不管什么时候使用的都是一个对象。假设只有二级缓存的情况,往二级缓存中放的显示一个普通的Bean对象,BeanPostProcessor去生产代理对象后,覆盖掉二级缓存中的普通Bean对象,那么多线程情况下可能取到的对象就不一致了。(注意:二级缓存才能如果在createBeanInstance之后处理,是可以解决AOP代理的问题)
- Spring有没有解决构造函数的循环依赖
- 不能,因为构造器是在实例化时调用的,此时bean还没有实例化完成,如果此时出现了循环依赖,一二三级缓存并没有Bean实例的任何相关信息,在实例化之后才放入三级缓存中,因此当getBean的时候缓存并没有命中,这样就抛出了循环依赖的异常了。
- Spring有没有解决多例下的循环依赖
- 没有。因为我们的bean是单例的,而且是字段注入(setter注入)的,单例意味着只需要创建一次对象,后面就可以从缓存 中取出来,字段注入,意味着我们无需调用构造方法进行注入。 如果是原型bean,那么就意味着每次都要去创建对象,无法利用缓存; 如果是构造方法注入,那么就意味着需要调用构造方法注入,也无法利用缓存。
- spring在创建bean的时候,在哪里创建的动态代理?
- ① 如果没有循环依赖的话,在bean初始化完成后创建动态代理②:如果有循环依赖,在bean实例化之后创建!
- 如何避免在并发情况下获取不完整的Bean?
- 1、将整个创建过程加一把锁,在创建实例bean的时候, 加了一把锁, 锁是一级缓存.
- public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) Assert.notNull(beanName, "Bean name must not be null"); synchronized (this.singletonObjects) Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) if (this.singletonsCurrentlyInDestruction) throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)"); if (logger.isDebugEnabled()) logger.debug("Creating shared instance of singleton bean '" + beanName + "'"); ..........
- 2、然后在从缓存取数据的getSingleton()上也加一把锁, 锁的也是一级缓存
- protected Object getSingleton(String beanName, boolean allowEarlyReference) Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) synchronized (this.singletonObjects) singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); return singletonObject;
- 1、将整个创建过程加一把锁,在创建实例bean的时候, 加了一把锁, 锁是一级缓存.
- 什么是循环依赖?
- 场景分析
- 自定义初始化和销毁方法有哪些?
- 1.通过@Bean指定init-method和destroy-method属性;2.Bean实现InitializingBean(定义初始化逻辑),DisposableBean(定义销毁逻辑); 3.@PostConstruct:在bean创建完成并且属性赋值完成;来执行初始化方法@PreDestroy:在容器销毁bean之前通知我们进行清理工作
- 怎么样可以在所有Bean创建完后做扩展代码?
- 方式一 基于SmartInitializingSingleton接口
- SmartInitializingSingleton接口是在所有的Bean实例化完成以后,Spring回调的方法, 所以这里也是一个扩展点,可以在单例bean全部完成实例化以后做处理。afterSingletonsInstantiated方法
- 方式二 基于Spring事件监听
- 监听ContextRefreshedEvent事件
- 方式一 基于SmartInitializingSingleton接口
- 自定义初始化和销毁方法有哪些?
- 事件机制
- 事件
- ContextRefreshedEvent
- 当容器被实例化或refreshed时发布.如调用refresh()方法, 此处的实例化是指所有的bean都已被加 载,后置处理器都被激活,所有单例bean都已被实例化, 所有的容器对象都已准备好可使用. 如果容器 支持热重载,则refresh可以被触发多次(XmlWebApplicatonContext支持热刷新,而 GenericApplicationContext则不支持)
- ContextStartedEvent
- 当容器启动时发布,即调用start()方法, 已启用意味着所有的Lifecycle bean都已显式接收到了start 信号
- ContextStoppedEvent
- 当容器停止时发布,即调用stop()方法, 即所有的Lifecycle bean都已显式接收到了stop信号 , 关闭 的容器可以通过start()方法重启
- ContextClosedEvent
- 当容器关闭时发布,即调用close方法, 关闭意味着所有的单例bean都已被销毁.关闭的容器不能被重启 或refresh
- RequestHandledEvent
- 这只在使用spring的DispatcherServlet时有效,当一个请求被处理完成时发布
- 自定义事件
- 事件类需要继承ApplicationEvent
- ContextRefreshedEvent
- 事件监听器
- 基于接口实现
- 事件监听器需要实现ApplicationListener接口,这是个泛型接口,泛型类类型就是事件类型,其次需要是spring容器托管的bean
- 基于注解实现
- @EventListener
- 基于接口实现
- 事件广播器
- applicationContext.publishEvent(new HelloEvent(this,"lgb"));
- Spring事件监听器的原理
- 在Spring的生命周期中,会自动初始化事件广播器(SimpleApplicationEventMulticaster)
- 在Spring的生命周期的初始化后的后置处理器阶段,会将每一个Bean都判断一下,当前Bean是不是实现了ApplicationListener接口,如果实现了此接口,那就将当前Bean放入监听器集合中
- 当在业务中使用上下文发布事务时,会使用事件广播器对象调用筛选方法,根据传入的事件,筛选出与之对应的所有事件监听器集合,然后遍历这个集合,在遍历集合的过程中,如果Executor对为null,那是就直接执行监听器的回调方法,如果Executor不为null,那么开启一个线程去执行监听器的回调方法(异步)
- 总之:Spring事件机制是观察者模式的一种实现,但是除了发布者和监听者者两个角色之外,还有一个EventMultiCaster的角 色负责把事件转发给监听者,发布者调用applicationEventPublisher.publishEvent(msg); 是会将事件发送给了EventMultiCaster, 而后由 EventMultiCaster注册着所有的Listener,然后根据事件类型决定转发给那个Listener。
- 事件
- 推断构造方法
- 没有加了@Autowired注解的构造方法
- 有多个构造方法
- 返回null
- 只有一个有参的构造方法
- 返回此构造方法
- 只有一个无参的构造方法
- 返回null
- 有多个构造方法
- 存在加了@Autowired注解的构造方法
- 只有一个required为true的构造方法
- 返回此构造方法
- 有多个required为true的构造方法
- 抛异常
- 有一个required为true和其他的required为false的构造方法
- 抛异常
- 没有required为true的构造方法
- 返回所有required为false的构造方法以及无参构造方法
- 只有一个required为true的构造方法
- 没有加了@Autowired注解的构造方法
- 事务
- EnableTransactionManagement
- 开启Spring事务本质上就是增加了一个Advisor,但我们使用@EnableTransactionManagement注解来开启Spring事务是,该注解代理的功能就是向Spring容器中添加了两个Bean:AutoProxyRegistrar,主要作用就是开启自动代理ProxyTransactionManagementConfiguration:他下面定义了另外三个bean,主要是用来判断某个类或方法上是否存在@Transactional注解
- TransactionInterceptor就是代理逻辑,当某个类中存在@Transactional注解时,到时就产生一个代理对象作为Bean,代理对象在执行某个方法时,最终就会进入到TransactionInterceptor的invoke()方法
- 可以参考:https://www.processon.com/view/link/5fab6edf1e0853569633cc06
- 事务基本执行原理
- 一个Bean在执行Bean的创建生命周期时,会经过InfrastructureAdvisorAutoProxyCreator的初始化后的方法,会判断当前当前Bean对象是否和BeanFactoryTransactionAttributeSourceAdvisor匹配,匹配逻辑为判断该Bean的类上是否存在@Transactional注解,或者类中的某个方法上是否存在@Transactional注解,如果存在则表示该Bean需要进行动态代理产生一个代理对象作为Bean对象。
- 该代理对象在执行某个方法时,会再次判断当前执行的方法是否和BeanFactoryTransactionAttributeSourceAdvisor匹配,如果匹配则执行该Advisor中的TransactionInterceptor的invoke()方法,执行基本流程为:
- 利用所配置的PlatformTransactionManager事务管理器新建一个数据库连接修改数据库连接的autocommit为false执行MethodInvocation.proceed()方法,简单理解就是执行业务方法,其中就会执行sql如果没有抛异常,则提交如果抛了异常,则回滚
- 简化的流程:事务初始化:EnableTransactionManagement ->PlatformTransactionManager->生成一个事务名称->创建事务:开始创建事务->TransactionStatus->getTransaction(txAttr)->doGetTransaction()得到一个数据库连接对象->事务传播机制的校验->doBegin() ->设置数据库的隔离级别->commit设置为false->TransactionSynchronizationManager#doGetResource设置到当前线程的ThreadLocal中。事务创建成功之后执行业务操作,如果异常了:触发同步器的beforeCompletion->调用数据库的rollback->触发同步器的afterCompletion->判断是否有事务挂起->设置到TransactionSynchronizationManager中,并执行同步器的resume方法。如果执行成功了:触发同步器的beforecommit->触发同步器的beforeCompletion->数据库的commit->aftercommit->afterCompletion->判断是否有事务挂起->设置到TransactionSynchronizationManager中,并执行同步器的resume方法。
- TransactionSynchronizationManager的结构
- public abstract class TransactionSynchronizationManager //线程上下文中保存着【线程池对象:ConnectionHolder】的Map对象。线程可以通过该属性获取到同一个Connection对象。 private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>("Transactional resources"); //事务同步器,是Spring交由程序员进行扩展的代码,每个线程可以注册N个事务同步器。 private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations = new NamedThreadLocal<>("Transaction synchronizations"); // 事务的名称 private static final ThreadLocal<String> currentTransactionName = new NamedThreadLocal<>("Current transaction name"); // 事务是否是只读 private static final ThreadLocal<Boolean> currentTransactionReadOnly = new NamedThreadLocal<>("Current transaction read-only status"); // 事务的隔离级别 private static final ThreadLocal<Integer> currentTransactionIsolationLevel = new NamedThreadLocal<>("Current transaction isolation level"); // 事务是否开启 actual:真实的 private static final ThreadLocal<Boolean> actualTransactionActive = new NamedThreadLocal<>("Actual transaction active");
- 事务传播机制和原理
- 传播机制
- 原理:Spring的事务管理是通过AOP代理实现的,对被代理对象的每个方法进行拦截,在方法执行前启动事务,在方法执行完成后根据是否有异常及异常的类型进行提交或回滚。Spring在运行期间会为目标对象生成一个代理对象,并在代理对象中实现对目标对象的增强。spring在底层数据源的基础上,利用 ThreadLocal,SavePoint等技术点实现了多种事务传播属性,便于实现各种复杂的业务
- 例如:PROPAGATION_REQUIRES_NEW 实现原理。a. 创建事务状态对象
以上是关于spring常用知识体系的主要内容,如果未能解决你的问题,请参考以下文章
- EnableTransactionManagement