spring相关知识整理

谈谈对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
  1. singleton 默认
  2. prototype 每次获取都新建一个bean
  3. session session内有效
  4. request 每次http请求有效
  5. global session ServletContext范围内
说说spring相关的注解

@Component @Service @Repository @Controller @Configuration

@Bean

@Autowired

@Qualifier

@Import

mvc requestMapping RequestParam pathVairable

transaction

applicationContext初始化过程

核心

  1. prepareBeanFactory (创建环境environment,注册一些默认内置的bean,beanProcessor)

  2. postProcessBeanFactory bean工厂后置处理 可以忽略

  3. invokeBeanFactoryPostProcessors 执行/调用beanFactoryPostProcessor方法

    在beanFactory里面找到/实例化所有的beanFactoryPostProcessor类,并且执行里面的postProcessBeanFactory方法,其中最重要的一个类是ConfigurationClassPostProcessor,里面实现的方法是扫描类路径,封装成beanDefinition加到beanDefinitionMap,import注解就是在这一步解析类加到beanDefinition,springboot自动配置也是在这一步,import一个类,利用spi,扫面加入更多类

  4. registerBeanPostProcessors

    在beanFactory里面找到/实例化所有的beanPostProcessor类,按优先级顺序排好序,在bean的创建过程中使用到

  5. 初始化国际化组件 初始化事件分发器 初始化事件listener 忽略

  6. finishBeanFactoryInitialization 实例化剩余的bean

    获取所有beanNames,实例化,这里面执行依赖注入/循环依赖的处理过程,缓存,创建bean之后的一系列处理方法

  7. 发布finish事件 忽略

image-20210515234828763

spring bean生命周期

AbstractAutowireCapableBeanFactory.createBean / doCreateBean

bean的创建和销毁过程中经历的步骤 / 也就是createBean方法

  1. 扫描指定路径,根据某些注解如@Component找到bean的定义,加到beanDefinitionMap这个bean定义map里

  2. 实例化bean,通过反射创建出来bean

  3. 为bean设置自动注入的属性(populateBean)

  4. 初始化bean(initializeBean)

    1. 看是否继承Aware相关接口,执行相关的Aware方法,比如setBeanName,setBeanFactory

    2. 执行bean后置处理器的postProcessBeforeInitialization方法

      其中一个ApplicationContextAwareProcessor检查Aware设置了ApplicationAware相关方法

      其中一个InitDestroyAnnotationBeanPostProcessor 执行@PostConstruct方法

    3. 执行初始化方法
      1. 检查是否是InitializingBean执行其中的afterPropertiesSet方法
      2. 执行自定的initMethod

    4. 执行bean后置处理器的postProcessAfterInitialization方法

  5. 检查注册到DisposableBeans,可以在销毁时执行相关的销毁处理方法

  6. bean使用中。。。。。

  7. 销毁相关方法

    1. 执行bean后置处理器postProcessBeforeDustruction方法
    2. 判断是否实现DisposableBean执行destroy方法
    3. 执行自定义的destory方法

image-20210513231352390

image-20210513231424498

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里面的共享内存,不加锁的情况下会有线程安全问题和可见性问题

解决方法:

  1. 加一个ThreadLocal变量,每个线程的变量隔离
  2. 改变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),移除二级三级缓存

相关代码截图

image-20210515235121049

创建bean开始前加锁

image-20210515235913359

加入创建中缓存bean

image-20210515235641758

最终加入bean

image-20210515235729653

为什么要三级缓存

其实三级缓存也是二级缓存的作用,因为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用到的设计模式
  1. 单例模式:context中的bean默认时单例的
  2. 工厂模式:通过beanFactory工厂创建bean对象
  3. 代理模式:AOP的实现基于动态代理
  4. 模板模式:jdbcTemplate,redisTemplate使用了模板模式
  5. 观察者模式:spring中的事件机制,listener使用的时观察者模式
  6. 适配器模式:spring mvc ,handlerAdapter,根据handler找到相应的处理方式,里面使用suport/handle方式
  7. 装饰者模式: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过程
  1. 请求先到servlet的service方法

  2. spring的DispatcherServlet继承了servlet类,实现了service方法,在里面的doDispatcher方法里面处理mvc逻辑

  3. 根据request请求信息遍历handlerMapping组件找到合适request的handler(也就是controller的方法)

  4. 遍历handlerAdapter组件找到合适处理handler的handlerAdapter

    applyPreHandler(调用interceptor的前置处理方法preHandle)

  5. handlerAdapter调用handler里面的处理方法,业务实际的处理逻辑(controller)

  6. 处理完后返回一个ModelAndView对象(返回数据和视图名字or视图)

    applyPostHandle(调用interceptor的后置处理方法postHandle)

  7. 处理handle之后的结果

    1. 判断是否有异常,进行异常处理(@ControlerAdvice @ExceptionHanlder注解相关的配置)
    2. viewResolver根据结果modelAndView里面的viewName找到view
    3. view根据结果modelAndView里面的model(数据)渲染视图到response

    triggerAfterCompletion(触发完成处理的操作,一般在这里remove threadLocal里面的request记录信息)

image-20210514142503971

核心处理流程代码截图

image-20210516000126785

说一下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创建代理

image-20210516165601213

image-20210516173458428

image-20210516171638682

image-20210516174712922

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事件

image-20210516224108225

image-20210516224056413

spring boot父子容器

在传统的spring mvc中,会区分父子容器,如果是用spring boot的DispatcherServletAutoConfiguration自动配置servlet的话,DispatcherServlet当作一个bean注入到父容器,并且DispatcherServlet实现的ApplicationContextAware接口,里面的webApplicaitionContext会被设置上父容器,所以spring boot自动配置的servlet不区分父子容器了,都是用一开始配置创建的applicationContext

DispatcherServlet当作bean注入

image-20210517152906724

手动创建AnnotationConfigWebApplication子容器,dispatcherServlet没有当作bean注入,注入的是RegistrationBean

image-20210517152547964

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值