谈谈对spring的理解
spring内容很广,是一个开源框架集合,代表产品有spring framework,spring boot,spring cloud。
spring 容器主要提供的功能是对象容器管理,依赖注入,更容易管理应用的开发,spring常用的模块还有mvc,aop,事务模块,web mvc方便web接口的开发,aop可以实现一个共同操作的封装,比如日志,权限控制,自定义缓存,自定义注解。
spring boot的亮点就是自动配置,极大提高了开发效率,比如在application.properties文件里加几行数据库配置就能自动配置数据库,其原理也是基于spring的扩展开发,在spring容器初始化的某一步(beanFactoryPostProcess)类似java的spi功能加载写好的配置类处理自动配过程,总的来说springboot就是对spring的一个扩展开发,更方便地使用。
spring cloud在spring boot高效构建开发项目的基础上,提供几个模块,使得更容易管理多个服务,也就是微服务,主要的组件有spring cloud gateway,config配置中心,eureka服务注册中心,feign服务见调用框架,robbin负载均衡组件,hystrix熔断降级组件
spring boot配置文件读取优先级
vm参数(启动参数)
系统环境变量
application-(profile).properties
application.properties
@PropertySource注解
总结:启动参数 > 文件 > 注解
什么是IOC容器
管理bean生命周期的一个容器,负责bean的创建,依赖注入,里面还有事件模块,国际化等
多少种方式完成依赖注入
构造函数
setter
属性
@Configuration的里面的@bean方法的参数
配置bean的方式
xml
@Component 基于注解(@Service @Controller @Configuration @Repository)
@Bean注解方法
@Import注解
1. 可以import一个bean
2. 可以import一个实现register之类的类,在注册方法里面自动处理注册逻辑
3. 可以import一个类,返回多个class名字数组
@Autowired和@Resource的区别
autowird默认是基于type类型的自动注入,可以用@Qualifier指定bean名字
resource默认是基于名字的的自动注入,可以根据type类型注入,注解里面的配置
BeanFactory和ApplicationContext
beanFactory是bean工厂,主要管理bean的创建/获取之类的
applicationContext继承beanFactory,实现更多的功能,初始化过程
spring有多少种bean scope
- singleton 默认
- prototype 每次获取都新建一个bean
- session session内有效
- request 每次http请求有效
- global session ServletContext范围内
说说spring相关的注解
@Component @Service @Repository @Controller @Configuration
@Bean
@Autowired
@Qualifier
@Import
mvc requestMapping RequestParam pathVairable
transaction
applicationContext初始化过程
核心
-
prepareBeanFactory (创建环境environment,注册一些默认内置的bean,beanProcessor)
-
postProcessBeanFactory bean工厂后置处理 可以忽略
-
invokeBeanFactoryPostProcessors 执行/调用beanFactoryPostProcessor方法
在beanFactory里面找到/实例化所有的beanFactoryPostProcessor类,并且执行里面的postProcessBeanFactory方法,其中最重要的一个类是ConfigurationClassPostProcessor,里面实现的方法是扫描类路径,封装成beanDefinition加到beanDefinitionMap,import注解就是在这一步解析类加到beanDefinition,springboot自动配置也是在这一步,import一个类,利用spi,扫面加入更多类
-
registerBeanPostProcessors
在beanFactory里面找到/实例化所有的beanPostProcessor类,按优先级顺序排好序,在bean的创建过程中使用到
-
初始化国际化组件 初始化事件分发器 初始化事件listener 忽略
-
finishBeanFactoryInitialization 实例化剩余的bean
获取所有beanNames,实例化,这里面执行依赖注入/循环依赖的处理过程,缓存,创建bean之后的一系列处理方法
-
发布finish事件 忽略
spring bean生命周期
AbstractAutowireCapableBeanFactory.createBean / doCreateBean
bean的创建和销毁过程中经历的步骤 / 也就是createBean方法
-
扫描指定路径,根据某些注解如@Component找到bean的定义,加到beanDefinitionMap这个bean定义map里
-
实例化bean,通过反射创建出来bean
-
为bean设置自动注入的属性(populateBean)
-
初始化bean(initializeBean)
-
看是否继承Aware相关接口,执行相关的Aware方法,比如setBeanName,setBeanFactory
-
执行bean后置处理器的postProcessBeforeInitialization方法
其中一个ApplicationContextAwareProcessor检查Aware设置了ApplicationAware相关方法
其中一个InitDestroyAnnotationBeanPostProcessor 执行@PostConstruct方法
-
执行初始化方法
1. 检查是否是InitializingBean执行其中的afterPropertiesSet方法
2. 执行自定的initMethod -
执行bean后置处理器的postProcessAfterInitialization方法
-
-
检查注册到DisposableBeans,可以在销毁时执行相关的销毁处理方法
-
bean使用中。。。。。
-
销毁相关方法
- 执行bean后置处理器postProcessBeforeDustruction方法
- 判断是否实现DisposableBean执行destroy方法
- 执行自定义的destory方法
spring何时执行销毁方法的问题
context手动调用registerShutdownHook方法,增加一个系统shutdownHook,里面执行context的doClose方法
registerShutdownHook和refresh方法性质一样,要手动调用,比如spring boot在调用context的refresh方式之后就调用了registerShutdownHook方法,加了一个容器销毁时的回调
一系列的beanPostProcessor 、
-
BeanPostProcessor
- postProcessBeforeInitialization 初始化前置方法
- postProcessAfterInitialization 初始化后置方法
-
InstantiationAwareBeanPostProcessor extends BeanPostProcessor
- postProcessBeforeInstantiation 实例化前置处理 一般不会生成对象,都是解析信息缓存
- postProcessAfterInstantiation 实例化后置处理
- postProcessProperties bean 属性后置处理(自动注入)
- postProcessPropertyValues 属性后置处理(自动注入/过期方法,使用上一个)
-
DestructionAwareBeanPostProcessor
- postProcessBeforeDestruction 销毁前的前置方法
-
SmartInstantiationAwareBeanPostProcessor 智能实例化后置处理器/很深奥的一个后置处理器
- predictBeanType 决定beanName的class类型
- determineCandidateConstructors 在实例化bean的时候返回构造器的后置处理器
- getEarlyBeanReference 获取创建中的bean的后置处理方法
-
MergedBeanDefinitionPostProcessor bean定义后置处理器
- postProcessMergedBeanDefinition 实例化后修改beanDefinition后置处理器(设置解析@PostConstruct @Autowired @Value之类注解)
- resetBeanDefinition 移除beanDefinition后置处理方法
spring bean安全问题
spring容器自己生成单例bean的时候不会有安全问题,加了synchronized锁,安全问题发生在多线程使用bean里面的属性的时候,一个标志性的场景就是controller里面处理属性,会存在线程安全问题,request访问是在线程池里面同时处理的,同时操作controller里面的属性,也就是heap里面的共享内存,不加锁的情况下会有线程安全问题和可见性问题
解决方法:
- 加一个ThreadLocal变量,每个线程的变量隔离
- 改变bean的作用域为prototype,每次都生成新的bean,一个线程处理一个,所以不会有安全问题
spring创建bean相关
spring如何解决循环依赖问题
核心:缓存
通过缓存解决循环依赖的问题,比如A依赖B,B依赖A,A实例化之后先加到缓存,然后在依赖注入的之后(populateBean)创建B bean,B实例化之后也执行populateBean,获取A的时候直接从缓存获取就行了,不用重新创建bean A
获取/创建bean流程
AbstractBeanFactory.doGetBean()
getBean 入口/简单获取单例bean流程 也就是getBean方法
- getSingleton(beanName) 从map或者缓存获取
- singletonObjects 一级缓存获取
- 没有再从earlySingletonObjects 二级缓存获取
- 没有再从 singletonFactories 三级缓存获取
- 如果缓存获取不到,说明没有创建好或者创建中,则开始创建过程
- 先获取依赖,先做完创建依赖的过程 getBean(dependName)
- getSingleton(beanName, ObjectFactory) 调用创建bean
- bean创建过程中synchronized加锁
- 标记bean正在创建中 singletonsCurrentlyInCreation.add(beanName)
- objectFactory.getObject() -> createBean() -> doCreateBean 实际创建bean逻辑
- createBeanInstatnce 实例化bean
- 加入三级缓存 singletonFactories.add(beanName, singletonFactory),并且在获取三级缓存的时候加了一个后置处理 getEarlyBeanReference,可以在获取缓存的时候自定义处理一下bean
- 自动注入
- 初始化 / 详细看创建bean流程
- addSingleton(beanName, bean) 把最终态的bean加入到一级缓存(beanMap),移除二级三级缓存
相关代码截图
创建bean开始前加锁
加入创建中缓存bean
最终加入bean
为什么要三级缓存
其实三级缓存也是二级缓存的作用,因为spring想让我们开发人员在获取早期正在创建bean的时候做一个后置处理扩展,如果只有两级缓存,则在每次获取早期正在创建bean的时候都会执行一遍处理方法,如果在扩展方法里面返回一个新对象,这样的话可以会造成每次获取早期正在创建bean的时候都会不一样,就不是单例了。所以spring在获取完三级缓存之后把后置处理过的bean加入到二级缓存中,下次在获取bean的时候就直接从二级缓存获取就行了,不用再通过三级缓存获取,也就不用再执行一次后置处理了
核心:其实就是确保只执行一次早期正在创建bean的后置处理,缓存处理结果,下次直接拿结果
作用例子:AOP代理依赖注入应用,也就是这个三级缓存,在A bean创建过程中要注入到B bean的时候,通过三级缓存获取,原来原生的实例A bean在getEarlyBeanReference 后置处理之后加上代理的包装,使得B注入了A增强之后的对象,不然A要初始化的之后postProcessAfterInitialization才能进行代理包装增强,B只能注入A增强前的对象,这就不对了
Bean依赖注入设置属性原理(populateBean)
@Autowired 自动注入bean过程
核心类:AutowiredAnnotationBeanPostProcessor
populateBean 方法入口 AbstractAutowireCapableBeanFActory
-
AutowiredAnnotationBeanPostProcessor.postProcessProperties()
-
findAutowiringMetadata 找到@Autowired/@Value的属性metadata
-
AutowiredFieldElement.inject()
-
beanFactory.resolveDependency 找到需要注入的value
根据type找到bean
doResolveDependency -> descriptor.resolveCandidate
-
通过反射注入到field
-
-
spring用到的设计模式
- 单例模式:context中的bean默认时单例的
- 工厂模式:通过beanFactory工厂创建bean对象
- 代理模式:AOP的实现基于动态代理
- 模板模式:jdbcTemplate,redisTemplate使用了模板模式
- 观察者模式:spring中的事件机制,listener使用的时观察者模式
- 适配器模式:spring mvc ,handlerAdapter,根据handler找到相应的处理方式,里面使用suport/handle方式
- 装饰者模式:wrapper decorator
https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485303&idx=1&sn=9e4626a1e3f001f9b0d84a6fa0cff04a&chksm=cea248bcf9d5c1aaf48b67cc52bac74eb29d6037848d6cf213b0e5466f2d1fda970db700ba41&token=255050878&lang=zh_CN#rd
spring mvc过程
-
请求先到servlet的service方法
-
spring的DispatcherServlet继承了servlet类,实现了service方法,在里面的doDispatcher方法里面处理mvc逻辑
-
根据request请求信息遍历handlerMapping组件找到合适request的handler(也就是controller的方法)
-
遍历handlerAdapter组件找到合适处理handler的handlerAdapter
applyPreHandler(调用interceptor的前置处理方法preHandle)
-
handlerAdapter调用handler里面的处理方法,业务实际的处理逻辑(controller)
-
处理完后返回一个ModelAndView对象(返回数据和视图名字or视图)
applyPostHandle(调用interceptor的后置处理方法postHandle)
-
处理handle之后的结果
- 判断是否有异常,进行异常处理(@ControlerAdvice @ExceptionHanlder注解相关的配置)
- viewResolver根据结果modelAndView里面的viewName找到view
- view根据结果modelAndView里面的model(数据)渲染视图到response
triggerAfterCompletion(触发完成处理的操作,一般在这里remove threadLocal里面的request记录信息)
核心处理流程代码截图
说一下aop
aop面向切面编程,是一种编程思想,把业务无关的操作,如日志,权限控制提取到一个地方同一个处理,减少系统重复代码,降低模块间的耦合度,提高可读性和可维护性
可以基于动态代理实现
@Configuration的动态代理是修改beanDefinition的class,其class变成cglib生成之后的class
如果是使用的@EnableAspectJAutoProxy,如果满足的代理条件,生成的beanDefnition的class还是老的原始的class,但是创建之后的object变成代理后的object
Spring AOP 和 AspectJ AOP 有什么区别
spring aop在运行时增强(生成字节码),sapectj aop在编译时增强(生成字节码 class)
spring aop原理
@EnableAspectJAutoProxy -> @Import(AspectJAutoProxyRegisterar.class)
在里面注册一个AnnotationAwareAspectJAutoProxyCreator 后置处理器
核心就是postProcessAfterInitialization - > wrapIfNecessary
-
getAdvicesAndAdvisorsForBean(class, beanName) 根据bean的class找到合适的aop advisor
AbstractAdvisorAutoProxyCreator findEligibleAdvisors
-
advisorRetrievalHelper.findAdvisorBeans 找到所有实现Advisor的bean 一般都不是这样做的,所以一般返回空
-
aspectJAdvisorsBuilder.buildAspectJAdvisors
- 找到@Aspect注解的bean
- 解析Aspect class内容,找到所有的Advisor,加入到缓存
-
findAdvisorsThatCanApply(advisors, beanClass) 从所有advisor中找出满足bean class的advisors
-
排序advisors
-
-
createProxy(class, beanNmae, advisors, bean) 实际生成增强bean过程
- new ProxyFactory();
- factory设置advisors
- evaluateProxyInterfaces 根据proxyTargetClass属性决定是否用cglib动态代理,如果proxyTargetClass=false,要实现接口才能用jdk动态代理
- proxyFactory.getProxy() 代理工厂创建代理逻辑
- 根据配置获取 CglibAopProxy 或者 JdkDynamicAopProxy
- CglibAopProxy.getProxy 正常ctglib创建代理过程
- new Enhancer();
- 根据advisors获取callbacks
- enhancer.create()
把符合bean的advisors保存到AdvisedSuport,也就是ProxyFactory,通过factory创建代理
spring boot自动配置过程
SpringApplication 一个容器启动工具类
SpringApplication.run(class);
-
new SpringApplication(class)
- 决定webApplicationType NONE 或者 SERVLET /看classpath有没有Servlet这个类
- 加载META-INF/spring.factorie中所有ApplicationContextInitializer的实现类 (loadProperties方式 类似spi但不是spi)
- 加载META-INF/spring.factorie中所有ApplicationListener的实现类
-
run(args)
-
加载META-INF/spring.factorie中所有SpringApplicationRunListener的实现类 目前只有一个EventPublishingRunListener,处理spring配置启动的几个过程
-
listeners.starting() 标记应用开始
-
发布ApplicationStartingEvent应用正在启动事件
LoggingApplicationListener监听了这个事件,也监听了下面environmentPrepared事件,配置slf4j日志
-
-
prepareEnvironment 准备环境
-
getOrCreateEnvironment() 创建环境
-
listeners.environmentPrepared(environment);
-
在里面发布applicationContext的ApplicationEnvironmentPreparedEvent事件
其中一个ConfigFileApplicationListener 监听到这个事件,又加载所有的EnvironmentPostProcessor,去处理Enviroment,实现加载配置到Enviroment
-
-
createApplicationContext() 创建IoC容器,就是简单的实例化Context
-
prepareContext() 准备容器/ 给容器设置上Environment、listeners、配置类等
- context.setEnvironment(environment) 设置环境
- applyInitializers 执行之前加载的ApplicationContextInitializer中initialize处理context的方法
- listeners.contextPrepared() 容器准备完成事件
- 发布ApplicationContextInitializedEvent事件
- 为applicationContext设置配置类(之前只是创建context,没有设置配置类)
- listeners.contextLoaded() 容器加载完成事件
- 给applicationContext设置上之前加载的applicationListeners
- 发布ApplicationPreparedEvent事件
-
refreshContext() 刷新容器
-
applicationContext.refresh() 刷新容器
在refresh中的onRefresh中createWebServer,创建TomcatWebServer,给TomcatContext设置ServletContextInitializer初始化方法,启动Tomcat,设置ROOT_WEB_APPLICATION属性,执行自定义的ServletContextInitializer里的onStartup方法
-
context.registerShutdownHook() 注册容器关闭处理方法
-
-
listeners.started 应用已经启动
- 发布ApplicationReadyEvent事件
-
listeners.running 应用正在运行
- 发布ApplicationReadyEvent事件
-
-
spring boot父子容器
在传统的spring mvc中,会区分父子容器,如果是用spring boot的DispatcherServletAutoConfiguration自动配置servlet的话,DispatcherServlet当作一个bean注入到父容器,并且DispatcherServlet实现的ApplicationContextAware接口,里面的webApplicaitionContext会被设置上父容器,所以spring boot自动配置的servlet不区分父子容器了,都是用一开始配置创建的applicationContext
DispatcherServlet当作bean注入
手动创建AnnotationConfigWebApplication子容器,dispatcherServlet没有当作bean注入,注入的是RegistrationBean