Spring相关知识整理

375 阅读12分钟

谈谈对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熔断降级组件

配置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范围内

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

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