Spring面试题

Spring框架

什么是Spring

Spring是一个支持快速开发JavaEE应用程序的框架。它提供了一系列底层容器和基础设施,可以和常用的开源框架无缝集成

什么是容器

容器是一种为某种特定组件的运行提供必要支持的软件环境,例如Tomcat就是一个Servlet容器,它可以为Servlet提供运行环境。Docker也是容器,它提供了必要的Linux系统环境,以便运行一个Linux进程。

Spring的核心就是提供了一个IOC容器,它可以管理所有的轻量级JavaBean组件,提供的底层服务包括组件的生命周期管理,配置管理,组装服务,Aop支持,包括建立在AOP基础之上的声明式事务管理

IOC的底层原理

三大核心接口

  • BeanDefinition: 定义了一个Bean相关配置的各种信息,比如当前Bean的构造器参数,属性以及一些其他的信息

  • BeanDefinitionReader: 定义了一些读取配置文件的方法,支持使用Resource和String的位置参数加载方法

  • BeanFactory: 访问Bean容器的顶层接口,我们经常用到的ApplicationContext接口也实现了BeanFactory

    --javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown
    ApplicationContext ac  = new ClassPathXmlApplicationContext("applicationContext.xml");
    ac.getBean(); // BeanFactory.getBean(resource/string);  //classpath: resource  string:name id

IOC初始化三大步骤

IOC的整个初始化流程大概可以分为三大步

  1. 1.

    定位: 寻找需要初始化的Bean

  2. 2.

    加载: 将需要初始化的Bean进行解析封装

  3. 3.

    注册: 将记载好的Bean放入IOC容器,也就是放到集合(Map)里

定位

Spring中配置支持以下六种来源:

  1. 1.

    classpath

  2. 2.

    annotation

  3. 3.

    network

  4. 4.

    filesystem

  5. 5.

    servletContext

接下来我们以最常用的一种方式作为入口来分析一下定位的流程

ApplicationContext ac  = new ClassPathXmlApplicationContext("applicationContext.xml");
ac.getBean("beanName");
ac.getBean(MyBean.class);

通过进入构造方法得知其最终会调用一个父类的 refresh() 方法

    public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
        super(parent);
        this.setConfigLocations(configLocations);
        if (refresh) {
            this.refresh();
        }
    }

我们进入refresh方法 ,可以看到 refresh方法会构建一个BeanFactory对象

    public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();

进入 obtainFreshBeanFactory 方法 又会调用一个 refreshBeanFactory()的方法

 protected final void refreshBeanFactory() throws BeansException {
        if (this.hasBeanFactory()) {
            this.destroyBeans();
            this.closeBeanFactory();
        }

        try {
            DefaultListableBeanFactory beanFactory = this.createBeanFactory();
            beanFactory.setSerializationId(this.getId());
            this.customizeBeanFactory(beanFactory);
            this.loadBeanDefinitions(beanFactory);
            this.beanFactory = beanFactory;
        } catch (IOException var2) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var2);
        }
    }

该方法最终会调用 AbstractRefreshableApplicationContext的 refreshBeanFactory方法,该方法最终会构建 DefaultListableBeanFactory 对象, 构建完成后,调用 loadBeanDefinitions方法开始加载Bean对象,该方法会调用AbstractXmlApplicationContextloadBeanDefinitions方法, 在该方法中会构建 XmlBeanDefinitionReader 对象

    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
      // xml bean对应读取工具 ---》解析XML  
      // beanDefinitionReader: 专门用来读取bean定义, xml
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
        this.initBeanDefinitionReader(beanDefinitionReader);
        this.loadBeanDefinitions(beanDefinitionReader);
    }

继续进入loadBeanDefinitions()方法,该方法判断了两种情况,一种是根据resource类型来判断 一种是根据String类型来判断,这里其实跟我们传递的参数有关系,如果我们传入的是一个String的参数 例如 new ClassPathXmlApplicationContext("spring.xml"); // 传入的类型是Resource 或者是String

    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        Resource[] configResources = this.getConfigResources();
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }

        String[] configLocations = this.getConfigLocations();
        if (configLocations != null) {
            reader.loadBeanDefinitions(configLocations);
        }

    }

注意看到这里又要构建两个对象

            Document doc = this.doLoadDocument(inputSource, resource);
            int count = this.registerBeanDefinitions(doc, resource);

第一个就是把resource转换成document对象,然后调用另一个方法准备注册Bean

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if (delegate.nodeNameEquals(ele, "import")) {  //解析import节点
            this.importBeanDefinitionResource(ele);  // 最终会重新调用loadBeanDefnitions
        } else if (delegate.nodeNameEquals(ele, "alias")) {
            this.processAliasRegistration(ele);
        } else if (delegate.nodeNameEquals(ele, "bean")) {
            this.processBeanDefinition(ele, delegate);  //  解析bean并且进行注册
        } else if (delegate.nodeNameEquals(ele, "beans")) {
            this.doRegisterBeanDefinitions(ele);
        }

    }

小结

IOC的底层原理,IOC的源码实现流程

我理解的IOC的主流程主要是三个步骤,定位(找配置文件),加载(解析配置文件), 注册(将bean添加到beanDefinitionMap)

详细版

在创建容器对象的时候,例如构建ClassPathXmlApplicationContext对象的时候,会调用一个refresh()方法,该方法会初始化并构建BeanFactory,在构建factory的过程中,会创建一个 XmlBeanFactoryReader对象,该对象会将当前传递的文件参数构建成resource对象开始加载,加载的时候会使用 BeanDefinitionParserDelegate类中定义好的 元数据 加载配置信息,构建BeanDefnition对象,且最终执行 registerBeanDefinition方法完成注册,注册时将对象添加到DefaultListableBeanFactory类中的 beanDefinitionMap中,该map的key是beanName 值是 BeanDefnition,且添加的时候会判断当前Bean是否已经存在,根据情况判断是否可以覆盖。

Aop的底层原理

Aop的底层原理是基于JDK的动态代理, 但是如果需要代理的类没有实现任何接口则无法使用jdk动态代理,这个时候Spring会自动切换成cglib模式,该模式基于字节码技术实现,其原理就是通过在运行过程中使用字节码生成一个子类去继承我们需要进行代理的类,有点类似于静态代理的实现,该字节码生成的类就是代理,代理原来的类去执行业务。

  • 16
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值