写点什么

Spring 核心流程分析

作者:IT巅峰技术
  • 2022 年 4 月 21 日
  • 本文字数:9889 字

    阅读完需:约 32 分钟

Spring 使用 BeanFactory 来产生和管理 Bean,它是工厂模式的实现。BeanFactory 使用控制反转模式(IOC)将应用的配置和依赖性规范与实际的应用程序代码分开。BeanFactory 使用依赖注入(DI)的方式给组件提供依赖 。

一、Bean 的注册



Spring 对于 Bean 的配置有两种方式:


  1. 基于资源文件解析的方式,其中最常用的是 XML 配置优点:可以在后期维护的时候适当地调整 Bean 管理模式,并且只要遵循一定的命名规范,可以让程序员不必关心 Bean 之间的依赖关系 。缺点:系统越庞大,XML 配置文件就越大,关系错综复杂,容易导致错误 。

  2. 基于 JavaConfig 的注解配置方式优点:配置比较方便,程序员只要在 service 层代码设置即可实现,不需要知道系统需要多少个 Bean,交给容器来注入就好了。 缺点:当你要修改或删除一个 Bean 的时候,你无法确定到底有多少个其他的 Bean 依赖于这个 Bean。(解决方法 : 需要有严格的开发文档,在修改实现时尽可能继续遵守相应的接口规则,避免使其他依赖于此的 Bean 不可用)


其中整个 Bean 加载的核心部分是:DefaultListableBeanFactory,它还是 Spring 注册及加载 Bean 的默认实现。它继承了:AbstractAutowireCapableBeanFactory 并实现了:ConfigurableListableBeanFactory 以及 BeanDefinitionRegistry 接口 。



可以看出来,层次还是相当清晰的,我们先粗略的看看这些类都具备哪些功能。


  • AliasRegistry: 定义对 alias 的简单增删改等操作。

  • SimpleAliasRegistry: 主要使用 map 作为 alias 的缓存,并对接口 AliasRegistry 进行实现。

  • SingletonBeanRegistry:定义对单例的注册及获取 。

  • BeanFactory:定义获取 Bean 及 Bean 的各种属性 。

  • DefauItSingletonBeanRegistry:对接口 SingletonBeanRegistry 各函数的实现。

  • HierarchicalBeanFactory:继承 BeanFactory,也就是在 BeanFactory 定义的功能的基础上增加了对 parentFactory 的支持 。

  • BeanDefinitionRegistry: 定义对 BeanDefinition 的各种增删改操作 。

  • FactoryBeanRegistrySupport:在 DefaultSingletonBeanRegistry 基础上增加了对 FactoryBean 的特殊处理功能 。

  • ConfigurableBeanFactory:提供配置 Factory 的各种方法 。

  • ListableBeanFactory:根据各种条件获取 Bean 的配置清单 。

  • AbstractBeanFactory:综合 FactoryBeanRegistrySupport 和 ConfigurableBeanFactory 的功能。

  • AutowireCapableBeanFactory:提供创建 Bean、自动注入、初始化以及应用 Bean 的后处理器 。

  • AbstractAutowireCapableBeanFactory:综合 AbstractBeanFactory 并对接口

  • AutowireCapableBeanFactory 进行实现。

  • ConfigurableListableBeanFactory: Beanfactory 配置清单,指定忽略类型及接口等。

  • DefaultListableBeanFactory: 综合上面所有功能,主要是对 Bean 注册后的处理 。


无论是使用配置文件的方式还是使用注解的方式,我们其实只是将未来需要使用的对象的类路径,名称,以及创建的方式等等信息告诉了 Spring 容器,经过加载,解析之后封装成了 BeanDefinition 对象并缓存到 BeanFactory 中。当我们真正需要使用的时候 Spring 会根据缓存中的 BeanDefinition 对象,为我们创建出我们实际需要的对象。

二、Bean 的实例化

1.触发实例化的时机

我们知道 Spring 是通过 BeanFactory 创建 Bean 的,当我们需要使用对象时,直接通过 BeanFactory.getBean()方法即可获取对象。我们发现 Spring 将我们实例化后的对象(单例)缓存了起来。



  1. Spring 容器在初始化 BeanFactory 的过程中会主动实例化单例非懒加载的 Bean

  2. 在容器的启动过程中如果需要使用其它容器中的 Bean 会提前出发实例化(主要是执行的方法是:invokeBeanFactoryPostProcessors 以及在其它对象实例化过程中执行属性填充 populateBean 方法需要注入对象的时候)。

2.实例化过程

Spring 整个加载过程都在 AbstractApplicationContext.refresh()中去完成。



public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. 准备刷新 prepareRefresh();
// Tell the subclass to refresh the internal bean factory. /* * 刷新内部BeanFactory * ClassPathXmlApplicationContext:1.新建BeanFactory,2.解析xml,3.封装成BeanDefintion对象 * AnnotationConfigApplicationContext: 获取GenericApplicationContext中的beanFactory */ ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context. // 为BeanFactory进行必要的准备工作 prepareBeanFactory(beanFactory);
try { // Allows post-processing of the bean factory in context subclasses. // 进行额外的后置处理 postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context. // 执行1.BeanDefinitionResgistryPostProcessor、2.BeanFactoryPostProcessor的回调 invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation. // 实例化所有实现了BeanPostProcessor接口的类并注册到容器中去 registerBeanPostProcessors(beanFactory);
// Initialize message source for this context. 国际化 initMessageSource();
// Initialize event multicaster for this context. 初始化事件类 initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses. 子容器自定义实现 onRefresh();
// Check for listener beans and register them. 注册事件 registerListeners();
// Instantiate all remaining (non-lazy-init) singletons. //1.bean实例化,2.ioc 3.注解支持 4.BeanPostProssor执行 5.AOP入口 finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); }
// Destroy already created singletons to avoid dangling resources. destroyBeans();
// Reset 'active' flag. cancelRefresh(ex);
// Propagate exception to caller. throw ex; }
finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); }}
复制代码


我们着重关注一下这个方法:finishBeanFactoryInitialization 它是 Spring 实例化的入口方法。


  • 获取 BeanFactory 中所有的 beanDefinition 名称

  • 合并 RootBeanDefinition

  • 非抽象的,单例的,非懒加载的就实例化

  • 是否实现了 FactoryBean 接口,如果是加一个 &前缀调用内部的 getObject,否则直接获取

  • 首先尝试从缓存中获取 getSingleton(beanName),(首次获取必然获取不到)接着进入创建方法 单例创建之前的操作:加入到正在创建的一个 set 集合中 singletonsCurrentlyInCreation

  • 调到外部的匿名类中的实例化方法,如果有值已经创建成功 singletonFactory.getObject();

  • 调到 doCreateBean 创建实例 BeanWrapper

  • 允许早期引用加入单例工厂直接返回这个 bean 的引用。addSingletonFactory(beanName,()->getEarlyBeanReference(beanName, mbd, bean));

  • 填充属性的值 populateBean

  • initializeBean

3.单例的循环依赖

什么是循环依赖?循环依赖就是循环引用,就是两个或多个 bean 相互之间的持有对方,比如 CircleA 引用 C ircleB , CircleB 引用 CircleC, CircleC 引用 CircleA,则它们最终反映为一个环。


@Componentpublic class CircleClassA {    public CircleClassA() {        System.out.println("====CircleClassA====");    }
@Autowired private CircleClassB circleClassB;}
@Componentpublic class CircleClassB { public CircleClassB() { System.out.println("====CircleClassB===="); }
@Autowired private CircleClassA circleClassA;}
复制代码


首先我们需要明确的一点是:Spring 只会处理上述类型的循环依赖(单例,非构造函数注入)其它情况直接报错。Spring 在处理 Bean 实例化的过程中是如何解决循环依赖的呢?我们需要着重关注如下 3 个 Map。


  • singletonObjects

  • earlySingletonObjects

  • singletonFactories


具体步骤如下:


  1. CircleClassA 在实例化的时候 首先从缓存中获取不到,然后进入创建方法,接着将 CircleClassA 加入到 singletonsCurrentlyInCreation 中,并在 singletonFactories 加入一个 getEarlyBeanReference 表示当前 CircleClassA 正在创建中。

  2. 当 CircleClassA 填充属性的值 populateBean 时,发现依赖了 CircleClassB,触发 CircleClassB 的实例化。

  3. 实例化 CircleClassB,首先从缓存中获取不到,然后进入创建方法,接着将 CircleClassB 加入到 singletonsCurrentlyInCreation 中,并在 singletonFactories 加入一个 getEarlyBeanReference 表示当前 CircleClassB 正在创建中。

  4. 当 CircleClassB 填充属性的值 populateBean 时,发现依赖了 CircleClassA,触发 CircleClassA 的实例化。

  5. 再次进入 CircleClassA 的实例化方法,此时虽然 singletonObjects 中获取不到 CircleClassA,但是检测到 CircleClassA 存在早期暴露的实例因此尝试从 earlySingletonObjects 中获取,首次调用获取不到从 singletonFactories 中获取,取到之后将 CircleClassA 放入 earlySingletonObjects,并提供给 CircleClassB 填充属性的值 populateBean 时使用。(此时的 CircleClassA 只是个引用的地址,实际上并不是一个完整的 CircleClassA)。

  6. 此时 CircleClassB 已经完成了(内部依赖的 CircleClassA 是个不完整的实例)并提供给 CircleClassA 填充属性的值 populateBean 时使用。CircleClassA 完成了 CircleClassB 的注入,它变成了一个完整的实例。

  7. 又由于 CircleClassB 中引用了 CircleClassA 的一个地址。所以它也同时变成了一个完整的。

  8. 实例化完成之后删除早期引用 map,并放入单例 map 中缓存 singletonObjects。

4.AOP 的实现原理

4.1 使用


我们在日常开发的过程中常常会需要监控业务方法的耗时或者打印业务执行前后的日志以方便在出现业务故障的时候快速定位问题。但是我们又不希望在已经封包的业务代码中进行额外的编程,针对此种场景 Spring 为我们提供了切面编程。如下代码案例就是一个我们在没有改变原来方法的前提下实现了打印方法耗时的功能。


@Component@Aspect@Slf4jpublic class MyAspect {    @Pointcut("execution( * org.example.springaop.*.*(..))")    public void pointCut(){};
@Around("pointCut()") public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { long start=System.currentTimeMillis(); Object result = joinPoint.proceed(); long end = System.currentTimeMillis(); log.info("{} invoke before at {} , after at {} cost: {}ms",joinPoint.getSignature().getName(),start,end,(end-start)); return result; }}
复制代码


4.2 三要素


  • 切面类 @Aspect

  • 切点 @Pointcut

  • 通知 @Around@Before@After@AfterReturning@AfterThrowing


4.3 实现


那么 Spring 是如何实现这么好用的功能的呢?  我们着重看一下这个注解类:@EnableAspectJAutoProxy


@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(AspectJAutoProxyRegistrar.class)public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
复制代码


@Import(AspectJAutoProxyRegistrar.class)


class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //注册AnnotationAwareAspectJAutoProxyCreator,AOP注解解析类 AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class); if (enableAspectJAutoProxy != null) { /* * false:1。实现了接口的使用JDK的Proxy实现代理。2.没有接口的使用cglib实现代理 * true:全部使用cglib实现代理 */ if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } // 是否暴露代理对象 if (enableAspectJAutoProxy.getBoolean("exposeProxy")) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } } }}
复制代码


可以看到加了此注解之后注册的类是:AspectJAutoProxyRegistrar,该类实现了的接口是:ImportBeanDefinitionRegistrar


registerBeanDefinitions: 该方法中向 Spring 中注册了:AnnotationAwareAspectJAutoProxyCreator



通过类关系图可以发现它实现了 BeanPostProcessor 接口势必在每个 Bean 实例化的时候会调用到 postProcessAfterInitialization 方法。


public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {  if (bean != null) {      Object cacheKey = getCacheKey(bean.getClass(), beanName);      if (this.earlyProxyReferences.remove(cacheKey) != bean) {        //如果它适合被代理,则需要封装指定 bean            return wrapIfNecessary(bean, beanName, cacheKey);        }    }    return bean;}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { //如果已经处理过,直接返回 if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { return bean; } //如果无须代理,直接返回 if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } //给定的 bean 类是否代表一个基础设施类 ,基础设施类不应代理 ,或者配置了指定 bean 不需要自动代理 if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } //如果当前的Bean有advice则创建当前类的代理类 // Create proxy if we have advice. Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); //创建代理 Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; }
this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean;}
复制代码


它主要实现的功能就是在 Spring 实例化 Bean 的时候,调用了 BeanPostProcessor 的 postProcessAfterInitialization 方法,判断当前实例化的 Bean 是否需要被代理,如果发现需要被代理,那么会创建一个代理对象并返回保存到 Spring 的 Bean 对象缓存里面去。而使用代理调用方法时,就可以实现不更改原对象中的方法的同时,又实现了我们期望的一些功能。Spring 中创建代理有两种方式:


  1. JdkDynamicAopProxy

  2. ObjenesisCglibAopProxy


JdkDynamicAopProxy:代理对象必然会实现 InvocationHandler 接口并实现 invoke 方法。因此我们将目光转移到此处:


public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {    MethodInvocation invocation;    Object oldProxy = null;    boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource; Object target = null;
try { //忽略equals、hashcode if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { // The target does not implement the equals(Object) method itself. return equals(args[0]); } else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { // The target does not implement the hashCode() method itself. return hashCode(); } else if (method.getDeclaringClass() == DecoratingProxy.class) { // There is only getDecoratedClass() declared -> dispatch to proxy config. return AopProxyUtils.ultimateTargetClass(this.advised); } else if (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) { // Service invocations on ProxyConfig with the proxy config... return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args); }
Object retVal; //暴露代理类 if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; }
// Get as late as possible to minimize the time we "own" the target, // in case it comes from a pool.获取被代理类 target = targetSource.getTarget(); Class<?> targetClass = (target != null ? target.getClass() : null);
// Get the interception chain for this method.从代理工厂中拿到拦截器链 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct // reflective invocation of the target, and avoid creating a MethodInvocation. //如果该方法没有拦截器,直接反射调用 if (chain.isEmpty()) { // We can skip creating a MethodInvocation: just invoke the target directly // Note that the final invoker must be an InvokerInterceptor so we know it does // nothing but a reflective operation on the target, and no hot swapping or fancy proxying. Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } else { // We need to create a method invocation... //如果该方法有拦截器,则调用拦截器的proceed invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // Proceed to the joinpoint through the interceptor chain. retVal = invocation.proceed(); }
// Massage return value if necessary. Class<?> returnType = method.getReturnType(); if (retVal != null && retVal == target && returnType != Object.class && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { // Special case: it returned "this" and the return type of the method // is type-compatible. Note that we can't help if the target sets // a reference to itself in another returned object. retVal = proxy; } else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) { throw new AopInvocationException( "Null return value from advice does not match primitive return type for: " + method); } return retVal; } finally { if (target != null && !targetSource.isStatic()) { // Must have come from TargetSource. targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } }}
复制代码


需要注意的是一旦代理方法是需要被增强的。Spring 就会构建一个切面:Advisor,它必须包含一个切点:Pointcut,还有一个增强:Advice。


  • Advisor:标注了 @Aspect 的一个切面;

  • Pointcut:我们可以将它理解为就是一个匹配的条件,一般是一个表达式或者某个注解 ;

  • Advice:常用的有 @Around@Before@After@AfterReturning@AfterThrowing 分别代表我们需要在目标方法执行前后可以附加的额外功能。


Spring 会将一个方法所有的切面组成一个链路,并依次调用,从而实现功能的增强。


public Object proceed() throws Throwable {    //  We start with an index of -1 and increment early.    //如果执行链全部执行完直接调用目标类的目标方法    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {        return invokeJoinpoint();    }    //链式调用    Object interceptorOrInterceptionAdvice =            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {        // Evaluate dynamic method matcher here: static part will already have        // been evaluated and found to match.        InterceptorAndDynamicMethodMatcher dm =                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;        Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());        if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {            return dm.interceptor.invoke(this);        }        else {            // Dynamic matching failed.            // Skip this interceptor and invoke the next in the chain.            return proceed();        }    }    else {        // It's an interceptor, so we just invoke it: The pointcut will have        // been evaluated statically before this object was constructed.        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);    }}
复制代码




程序员的核心竞争力其实还是技术,因此对技术还是要不断的学习,关注 “IT 巅峰技术” 公众号 ,该公众号内容定位:中高级开发、架构师、中层管理人员等中高端岗位服务的,除了技术交流外还有很多架构思想和实战案例。


作者是 《 消息中间件 RocketMQ 技术内幕》 一书作者,同时也是 “RocketMQ 上海社区”联合创始人,曾就职于拼多多、德邦等公司,现任上市快递公司架构负责人,主要负责开发框架的搭建、中间件相关技术的二次开发和运维管理、混合云及基础服务平台的建设。

发布于: 刚刚阅读数: 3
用户头像

一线架构师、二线开发、三线管理 2021.12.07 加入

Redis6.X、ES7.X、Kafka3.X、RocketMQ5.0、Flink1.X、ClickHouse20.X、SpringCloud、Netty5等热门技术分享;架构设计方法论与实践;作者热销新书《RocketMQ技术内幕》;

评论

发布
暂无评论
Spring核心流程分析_IT巅峰技术_InfoQ写作社区