美文网首页面试
Spring 底层基本原理

Spring 底层基本原理

作者: 坚果jimbowhy | 来源:发表于2020-08-24 02:25 被阅读0次

    Spring 底层基本原理

    H

    Java 标注技术参考 https://github.com/jimboyeah/demo/blob/java/demos/AnnotationsDemo.java

    学习一个框架的最佳姿势就是 READ THE FXXK SOURCE CODE!

    但是,如果细读每一行代码,去跟踪程序的执行流程,这是不可取的,新手通常都会犯这种不是错误的错误,但又确实比较要命,很容易将自己的脑袋丢进代码丛林,只见森林不见树木,迷途不归路。

    所以,我更愿意使用一种蜻蜓点水的态度去做这件事,首先,立下两个目标:

    • Spring XML 配置方式的加载基本原理;
    • Spring 注解配置方式的加载基本原理;

    为了解密以上两个问题,首先需要搞到 Spring 的源代码,我使用的是 Spring-5.1.0.RELEASE-sources,下载到的发行包目录里有各个模块的源码独立打包,解压到同一目录下。

    代码阅读工具使用 Sublime Text,它的多功能跳转、文件快速定位打开、符号跳转、光标位置跳转等功能十分适合用来快速定位源代码的关注点。

    Spring 框架源代码以模块化管理:

    • Spring Core 核心容器 Core Container 包含 Core 、Beans、Context、和 Expression Language 模块。核心容器提供 Spring 框架的基本功能,核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转 IOC 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。Context 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。

    • Spring AOP 将面向方面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。

    • Spring Data Access/Integration 包含有 JDBC、ORM、OXM、JMS 和 Transaction 模块。:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。

    • Spring Web Web 层包含了 Web、Web-Servlet、Web-Struts、Web-Porlet 模块。Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。

    • Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。

    • Spring Test 测试模块包括 JUnit 和 TestNG 对 Spring 组件进行测试。

    如果按配置文件的使用方式来划分,Spring 应用可以简单按以下方式分类:

    • XML 配置类型应用:

      • ClassPathXmlApplicationContext
      • FileSystemXmlApplicationContext
      • XmlWebApplicationContext
      • GenericXmlApplicationContext
    • 加载 Groovy 语言的文件:

      • GroovyWebApplicationContext
      • GenericGroovyApplicationContext
    • 加载标注配置:

      • AnnotationConfigApplicationContext
      • AnnotationConfigWebApplicationContext
    • 手动注入 bean:

      • GenericApplicationContext
      • ResourceAdapterApplicationContext
      • StaticApplicationContext
      • StaticWebApplicationContext

    根据目前 Web 开发的流行,或者也可以将 Spring 应用分为 Web 应用与其它类型应用,这个分类是比较实用的。

    • AnnotationConfigWebApplicationContext
    • AnnotationConfigApplicationContext
    • GenericWebApplicationContext
    • StaticWebApplicationContext

    还可以根据 Refreshable 特性进行分类:

    • AnnotationConfigWebApplicationContext
    • GroovyWebApplicationContext
    • XmlWebApplicationContext
    • ClassPathXmlApplicationContext
    • FileSystemXmlApplicationContext

    ApplicationContext 接口是所用 Spring 应用的基础,在此之上是 ConfigurableApplicationContext,和 WebApplicationContext Web 应用是扩展接口:

    AbstractXmlApplicationContext 
     => 🧡 AbstractRefreshableConfigApplicationContext 
         |=> 🧡 AbstractRefreshableApplicationContext
         |    |=> 🧡 AbstractApplicationContext
         |         |=> 🧡 DefaultResourceLoader
         |         |=> 🤍 ConfigurableApplicationContext
         |              |=> 🤍 ApplicationContext
         |              |    |=> 🤍 EnvironmentCapable
         |              |    |=> 🤍 ListableBeanFactory
         |              |    |=> 🤍 HierarchicalBeanFactory
         |              |    |=> 🤍 MessageSource
         |              |    |=> 🤍 ApplicationEventPublisher
         |              |    |=> 🤍 ResourcePatternResolver
         |              |=> 🤍 Lifecycle
         |              |=> 🤍 Closeable
         |=> 🤍 BeanNameAware
         |=> 🤍 InitializingBean
    

    作为一个基本的抽象类实现,AbstractApplicationContext 通过 LifecycleProcessor 接口为各种应用实现了基本的应用生命周期管理,应用的启停方法。

    以 XML 配置运行的常规 Spring 应用有很多,以下是继承自 AbstractXmlApplicationContext 的其中两个,分别从 classpath 和指定配置文件路径加载:

    new ClassPathXmlApplicationContext("beans-config.xml");
    new FileSystemXmlApplicationContext("beans-config.xml");
    

    典型的 Web 应用有三种,XmlWebApplicationContext 默认以 /WEB-INFO 为配置目录:

    XmlWebApplicationContext 
     => 🧡 AbstractRefreshableWebApplicationContext
         |=> 🧡 AbstractRefreshableConfigApplicationContext
         |=> 🤍 ConfigurableWebApplicationContext
         |    |=> 🤍 WebApplicationContext
         |    |    |=> 🤍 ApplicationContext
         |    |=> 🤍 ConfigurableApplicationContext
         |=> 🤍 ThemeSource
    

    默认配置属性设置:

    public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
    
    public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";
    
    public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";
    

    除了 XmlWebApplicationContext,还有另外两个 Web 应用类型,AnnotationConfigWebApplicationContext, GroovyWebApplicationContext,看名字大概猜到应用类型和标注、Groovy 脚本有密切关。

    Spring 4.0 的一个重要特征就是完全支持 Groovy,这是 Spring 主导的一门基于 JVM 的脚本语言。在 Spring 2.x,脚本语言通过 Java scripting engine 得到支持。而在 Spring 4.0 中,Groovy 的变得更重要,Groovy 可以替换 XML 和注解用来作为 bean 配置。所以 GroovyWebApplicationContext 和 GenericGroovyApplicationContext 在使用 Groovy 脚本进行开发时非常好用。

    另外一支是通用弄 Spring 应用,以 GenericApplicationContext 为基础类,以静态站点应用为例,它的类层次结构如下:

    StaticWebApplicationContext
    |=> 🧡 StaticApplicationContext
    |    |=>🧡 GenericApplicationContext
    |        |=> 🧡 AbstractApplicationContext
    |        |    |=> 🧡 DefaultResourceLoader
    |        |    |    |=> 🤍 ResourceLoader
    |        |    |=> 🤍 ConfigurableApplicationContext
    |        |=> 🤍 BeanDefinitionRegistry
    |             |=> 🤍 AliasRegistry
    |=> 🤍 ConfigurableWebApplicationContext
    |=> 🤍 ThemeSource
    

    除了以上的 StaticApplicationContext,通用类型的应用还有以下这些,它们都各自实现了对应的接口:

    • AnnotationConfigApplicationContext (AnnotationConfigRegistry)
    • GenericGroovyApplicationContext (GroovyObject)
    • GenericWebApplicationContext (ConfigurableWebApplicationContext)
    • GenericXmlApplicationContext
    • ResourceAdapterApplicationContext J2EE 连接器架构资源适配器实现 JCA - Java Connector Architecture

    Java 的反射和起源于 JDK 1.5 的标注编程技术在 Spring 有重度的应用,管理注解配置的两个容器 AnnotationConfigApplicationContext、AnnotationConfigWebApplicationContext 是我比较关注的,它们可以实现基于 Java 配置类和各种注解配置加载 Spring 的应用上下文,避免使用 application.xml 进行配置带来的繁琐,相比 XML 配置,标注配置更加便捷。

    两者虽然实现方式略有差别,但处理注解的逻辑一样,先扫描 scan(String... basePackages) 类路径 classpath 收集 bean 类信息,然后一一注册 register(annotatedClasses)

    Variable number of arguments 是 Java 5 带来的新功能,使用省略号 ... 代替多参数。

    这里给两段摘抄的示范代码:

    SCENARIO 1

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.*;
    
    public class MainApp {
       public static void main(String[] args) {
          ApplicationContext ctx = 
             new AnnotationConfigApplicationContext(HelloWorldConfig.class);
    
          HelloWorld helloWorld = ctx.getBean(HelloWorld.class);
          helloWorld.setMessage("Hello World!");
          helloWorld.getMessage();
       }
    }
    

    SCENARIO 2:

    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRegistration;
    import org.springframework.web.WebApplicationInitializer;
    import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
    import org.springframework.web.servlet.DispatcherServlet;
    public class WebServletConfiguration implements WebApplicationInitializer{
        public void onStartup(ServletContext ctx) throws ServletException {
            AnnotationConfigWebApplicationContext webCtx = new AnnotationConfigWebApplicationContext();
            webCtx.register(SpringConfig.class);
            webCtx.setServletContext(ctx);
            ServletRegistration.Dynamic servlet = ctx.addServlet("dispatcher", new DispatcherServlet(webCtx));
            servlet.setLoadOnStartup(1);
            servlet.addMapping("/");
        }
    }
    

    配置类 HelloWorldConfig 或 SpringConfig 可以使用 Spring 的标注,如 @Configuation、@Bean、@Component、@ComponentScan 等进行修饰。

    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class HelloWorldConfig {
        public HelloWorldConfig() {
            System.out.println("HelloWorldConfig ...");
        }
    }
    

    每一种 Bean 类型在 Spring 中存在着不同的 scope,默认是 singleton,还有 prototype、request 等等。

    直接使用 BeanFactory 容器对于 Spring 的使用来说并不多见,因为在企业级的应用中大多数都会使用 ApplicationContext。这里只是用于测试,简单实例化一个 bean 用于分析 Spring 的内部原理:

    BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("spring/spring-test.xml"));
    MySpringBean bean = (MySpringBean) beanFactory.getBean("mySpringBean");
    

    单例在 Spring 的同一个容器内只会被创建一次,后续再获取 bean 直接从单例缓存中获取,当然这里也只是尝试加载,首先尝试从缓存中加载,然后再次尝试从 singletonFactorry 加载。因为在创建单例 bean 的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,Spring 会先行创建 bean 的 ObjectFactory 提早曝光加入到缓存中,一旦下一个 bean 创建时需要依赖上个 bean,则直接使用 ObjectFactory;就算没有循环依赖,只是单纯的依赖注入,如 B 依赖 A,如果 A 已经初始化完成,B 进行初始化时,需要递归调用 getBean 获取 A,这是 A 已经在缓存里了,直接可以从这里取到。

    加载 Bean 使用到的几个重要的相关类或接口:

    • DefaultDocumentLoader 加载 bean 的 XML 配置文件,使用 Java DocumentBuilderFactory 解析 XML 是 JAXP-configured 解析器;

    • DefaultBeanDefinitionDocumentReader 根据 spring-beans DTD 和 XSD 定义读入配置,定义了以下这些基本配置属性:

        public static final String BEAN_ELEMENT = BeanDefinitionParserDelegate.BEAN_ELEMENT;
        public static final String NESTED_BEANS_ELEMENT = "beans";
        public static final String ALIAS_ELEMENT = "alias";
        public static final String NAME_ATTRIBUTE = "name";
        public static final String ALIAS_ATTRIBUTE = "alias";
        public static final String IMPORT_ELEMENT = "import";
        public static final String RESOURCE_ATTRIBUTE = "resource";
        public static final String PROFILE_ATTRIBUTE = "profile";
      

      标签的解析在 parseDefaultElement 函数,它分别处理解析 4 种 ELEMENT 标签节点,import、alias、bean、beans,其中对 bean 标签的解析最为复杂也最为重要,解析完成后在 processBeanDefinition 进行注册。

      注册有两种关联方式,beanName 与 alias,若两者名称相同则不需要处理并删除原有的 alias。若 aliasName 已经使用并已经指向了另一 beanName 则需要用户的设置进行处理。 alias 循环检查,当 A -> B 存在时,若再次出现 A -> C -> B 时候则会抛出异常。

    • BeanDefinition 接口描述一个 bean,如属性、构造参数等,前面解析标签后得到的数据就存储在这里,所以了解一个 bean 会存储什么样的属性数据,可以参考实现这个接口的 AbstractBeanDefinition。获取 beanDefiniton 接下来就是注册到容器中供应用使用;

    前面是 XML 配置文件的解析,接下来将会面临更大的挑战,就是 bean 加载的探索。bean 加载的功能实现远比 bean 的解析要复杂得多。

    对于加载 bean 实例,可以在 Spring 中调用以下方法:

    MySpringBean bean = (MySpringBean) beanFactory.getBean("mySpringBean");
    

    那么这一过程中涉及的重要接口 BeanFactory Bean 工厂接口,加载 bean 定义以及获取实例;

    AbstractAutowireCapableBeanFactory
        |=> AbstractBeanFactory
        |    |=> FactoryBeanRegistrySupport
        |    |    |=> DefaultSingletonBeanRegistry
        |    |         |=> SimpleAliasRegistry
        |    |         |    |=> implements AliasRegistry
        |    |         |=> implements SingletonBeanRegistry
        |    |=> implements ConfigurableBeanFactory
        |         |=> HierarchicalBeanFactory
        |         |    |=> BeanFactory
        |         |=> SingletonBeanRegistry
        |=> implements AutowireCapableBeanFactory {
    

    在抽象类 AbstractBeanFactory 封装了一个相当复杂的 doGetBean,它就是实际加载 bean 实例的方法:

    • 转换对应的 beanName,AbstractBeanFactory -> transformedBeanName(name)。
    • 尝试从缓存中加载单例 DefaultSingletonBeanRegistry -> getSingleton(beanName)。
    • bean 的实例化 createBean(beanName, mbd, args)。
    • 原型模式的依赖检查。
    • 检测 parentBeanFactory。
    • 将存储 XML 配置文件的 GenericBeanDefinition 转换为 RootBeanDefinition。
    • 寻找依赖。
    • 针对不同的 scope 进行 bean 的创建。
    • 类型转换。

    一般情况下,Spring 通过反射机制根据 bean 配置的 class 属性指定的实现类来实例化 bean。在某些情况下,实例化 bean 过程比较复杂,如果按照传统的方式,则需要在 bean 配置文件节点中提供大量 的配置信息,配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。 Spring 为此提供了 FactoryBean 的工厂类接口,注意名字和 BeanFactory 是相反的单词组合,用户可以通过实现该接口定制实例化 bean 的逻辑。

    FactoryBean 接口地位很重要,Spring 自身就提供了 70 多个 FactoryBean 的实现。它们隐藏了实例化一些复杂 bean 的细节,给上层应用带来了便利。从 Spring 3.0 开始,FactoryBean 开始支持泛型,即接口声明改为 FactoryBean<T> 的形式:

    Spring 容器初始化流程大致流程如下:

    • this():注册内置的 BeanPostProcessor 的 BeanDefinition 到容器
    • register(annotatedClasses):注册配置类 BeanDefinition 到容器
    • prepareRefresh():初始化前的准备工作,列如对系统属性或者环境变量进行验证
    • obtainFreshBeanFactory():初始化 BeanFactory
    • prepareBeanFactory(beanFactory):对 BeanFactory 进行各种功能的填充,比如对表达式的支持等
    • postProcessBeanFactory(beanFactory):留给子类扩展用
    • invokeBeanFactoryPostProcessors(beanFactory):执行BeanFactoryPostProcessor后置处理器
    • registerBeanPostProcessors(beanFactory):创建并注册所有的BeanPostProcessor后置处理
    • initMessageSource():初始化消息组件(国际化,消息绑定,消息解析)
    • initApplicationEventMulticaster():初始化容器的事件机制
    • onRefresh():初始化其他特殊Bean(留给子类做扩展用)
    • registerListeners():注册监听器(ApplicationListener)
    • finishBeanFactoryInitialization(beanFactory):创建并注册所有的单例且非懒加载的Bean
    • finishRefresh():完成刷新过程,通知生命周期处理器 lifecycleProcessor 刷新过程,同时发出 ContextRefreshEvent 通知别人。
    • resetCommonCaches():重置缓存

    Spring Bean的生命周期:

    • InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation():实例化Bean的前置处理
    • createBeanInstance(beanName, mbd, args):实例化Bean
    • MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition():合并Bean定义信息(自动装配元素)
    • InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation():实例化Bean的后置处理
    • InstantiationAwareBeanPostProcessor#postProcessPropertyValues():Bean的自动装配
    • 部分 Aware 接口的自动装配:BeanNameAware#setBeanName()、BeanClassLoaderAware#setBeanClassLoader()、BeanFactoryAware#setBeanFactory()
    • BeanPostProcessor#postProcessBeforeInitialization():Bean初始化的前置处理
    • 剩余部分 Aware 接口的自动装配:EnvironmentAware#setEnvironment()、EmbeddedValueResolverAware#setEmbeddedValueResolver()、ResourceLoaderAware#setResourceLoader ()、ApplicationEventPublisherAware#setApplicationEventPublisher ()、MessageSourceAware#setMessageSource ()、ApplicationContextAware# setApplicationContext ()
    • @PostConstruct:执行初始化方法
    • InitializingBean#afterPropertiesSet():执行初始化方法
    • @Bean(initMethod = "initMethod"):执行初始化方法
    • BeanPostProcessor#postProcessAfterInitialization():Bean初始化的后置处理
    • DestructionAwareBeanPostProcessor#postProcessBeforeDestruction():销毁Bean的后置处理(@PreDestroy)
    • DisposableBean#destroy():执行销毁方法
    • @Bean(destroyMethod = "destroyMethod"):执行销毁方法

    Spring Bean的自动装配过程:

    • 根据 Class 对象,通过反射获取所有的 Field 和 Method 信息
    • 通反射获取 Field 和 Method 的注解信息,并根据注解类型,判断是否需要自动装配
    • 将需要自动装配的元素,封装成 AutowiredFieldElement 或 AutowiredMethodElement 对象
    • 调用 AutowiredFieldElement 或 AutowiredMethodElement 的 inject 方法,唤起后续步骤
    • 通过调用容器的 getBean() 方法找到需要注入的源数据 Bean
    • 通过反射将找到的源数据 Bean 注入到目标 Bean 中

    Spring AOP执行流程:

    • @EnableAspectJAutoProxy开启对AOP的支持,注册后置处理器 AnnotationAwareAspectJAutoProxyCreator
    • 根据Class对象找出所有切面类(有@Aspect注解的类)
    • 解析切面类中的增强器(解析@Before等注解),并放入缓存中
    • 根据ProxyFactory工厂类创建AopProxy代理器
    • 根据AopProxy代理器创建代理类
    • 通过增强器执行入口执行增强器 JdkDynamicAopProxy#invoke()或CglibAopProxy.DynamicAdvisedInterceptor#intercept()
    • 获取链接链 AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice()
    • 以递归方式执行拦截链 ReflectiveMethodInvocation.proceed()

    参考

    相关文章

      网友评论

        本文标题:Spring 底层基本原理

        本文链接:https://www.haomeiwen.com/subject/wurgjktx.html