Spring Bean的生命周期源码解析

news2024/11/18 11:40:37

文章目录

  • 前言
  • 零、Bean生命周期流程图
  • 一、加载类
  • 二、实例化前
    • 2.1、使用案例
    • 2.2、源码---InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()
  • 三、实例化
    • 3.1、Supplier创建对象
    • 3.2、工厂方法创建对象
    • 3.3、推断构造方法
    • 3.4、源码
  • 四、BeanDefinition的后置处理
    • 4.1、使用案例
    • 4.2、源码
  • 五、实例化后
    • 5.1、使用案例
    • 5.2、源码
  • 六、自动注入(spring自带)
    • 6.1、源码
  • 七、属性填充(依赖注入)
    • 7.1、使用案例
    • 7.2、源码
  • 八、执行Aware
    • 8.1、源码
  • 九、初始化前
    • 9.1、使用案例
    • 9.2、源码
  • 十、初始化
  • 十一、初始化后
    • 11.1、使用案例
    • 11.2、源码


前言

Spring最重要的功能就是帮助程序员创建对象(也就是IOC),而启动Spring就是为创建Bean对象做准备,所以我们先明白Spring到底是怎么去创建Bean的,也就是先弄明白Bean的生命周期。

Bean的生命周期就是指:在Spring中,一个Bean是如何生成的,如何销毁的

从createBean开始解析

refresh->finishBeanFactoryInitialization->AbstractBeanFactory#doGetBean->createBean

零、Bean生命周期流程图

先看个图有个大概印象,然后在看每个步骤具体干了啥

在这里插入图片描述


一、加载类

BeanDefinition合并之后,就可以去创建Bean对象了,而创建Bean就必须实例化对象,而实例化就必须先加载当前BeanDefinition所对应的class,在AbstractAutowireCapableBeanFactory类的createBean()方法中,一开始就会调用:

Class<?> resolvedClass = resolveBeanClass(mbd, beanName);

这行代码就是去加载类,该方法是这么实现的:

if (mbd.hasBeanClass()) {
 return mbd.getBeanClass();
}
if (System.getSecurityManager() != null) {
 return AccessController.doPrivileged((PrivilegedExceptionAction<Class<?>>) () ->
  doResolveBeanClass(mbd, typesToMatch), getAccessControlContext());
 }
else {
 return doResolveBeanClass(mbd, typesToMatch);
}

1、如果beanClass属性的类型是Class,那么就直接返回。

2、如果不是,则会根据类名进行加载(doResolveBeanClass方法所做的事情)会利用BeanFactory所设置的类加载器来加载类

3、如果没有设置,则默认使用**ClassUtils.getDefaultClassLoader()**所返回的类加载器来加载。

ClassUtils.getDefaultClassLoader()
1、优先返回当前线程中的ClassLoader

2、线程中类加载器为null的情况下,返回ClassUtils类的类加载器

3、如果ClassUtils类的类加载器为空,那么则表示是Bootstrap类加载器加载的ClassUtils类,那么则返回系统类加载器


二、实例化前

当前BeanDefinition对应的类成功加载后,就可以实例化对象了,但是…

在Spring中,实例化对象之前,Spring提供了一个扩展点,允许用户来控制是否在某个或某些Bean实例化之前做一些启动动作。


2.1、使用案例

@Component
public class LjcBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {

        if ("userService".equals(beanName)) {
            System.out.println("实例化前");
            return new UserService();
        }
        return null;
    }
}

userService这个Bean,在实例化前会直接返回一个由我们所定义的UserService对象。

如果是这样,表示不需要Spring来实例化了,并且后续的Spring依赖注入也不会进行了,会跳过一些步骤,直接执行初始化后这一步。


2.2、源码—InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()

			// 让BeanPostProcessors有机会返回一个代理,而不是目标bean实例。
			Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
			if (bean != null) {
				return bean;
			}

这个扩展点叫InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()。

protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
			if (bp instanceof InstantiationAwareBeanPostProcessor) {
				InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
				// 如果实现了这个接口则在实例化之前,进行回调
				Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
				if (result != null) {
					return result;
				}
			}
		}
		return null;
	}

三、实例化

在这个步骤中就会根据BeanDefinition去创建一个对象了。


3.1、Supplier创建对象

首先判断BeanDefinition中是否设置了Supplier,如果设置了则调用Supplier的get()得到对象。

得直接使用BeanDefinition对象来设置Supplier,比如:

AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setInstanceSupplier(new Supplier<Object>() {
 @Override
 public Object get() {
  return new UserService();
 }
});
context.registerBeanDefinition("userService", beanDefinition);

3.2、工厂方法创建对象

如果没有设置Supplier,则检查BeanDefinition中是否设置了factoryMethod,也就是工厂方法,有两种方式可以设置factoryMethod,比如:

方式一:

<bean id="userService" class="com.zhouyu.service.UserService" factory-method="createUserService" />

对应的UserService类为:

public class UserService {

 public static UserService createUserService() {
  System.out.println("执行createUserService()");
  UserService userService = new UserService();
  return userService;
 }

 public void test() {
  System.out.println("test");
 }

}

方式二:

<bean id="commonService" class="com.zhouyu.service.CommonService"/>
<bean id="userService1" factory-bean="commonService" factory-method="createUserService" />

对应的CommonService的类为:

public class CommonService {

 public UserService createUserService() {
  return new UserService();
 }
}

Spring发现当前BeanDefinition方法设置了工厂方法后,就会区分这两种方式,然后调用工厂方法得到对象。

值得注意的是,我们通过@Bean所定义的BeanDefinition,是存在factoryMethod和factoryBean的,也就是和上面的方式二非常类似,@Bean所注解的方法就是factoryMethod,AppConfig对象就是factoryBean。如果@Bean所所注解的方法是static的,那么对应的就是方式一。


3.3、推断构造方法

推断完构造方法后,就会使用构造方法来进行实例化了。

在推断构造方法逻辑中除开会去选择构造方法以及查找入参对象以外,会还判断是否在对应的类中是否存在使用**@Lookup注解**了方法。

如果存在则把该方法封装为LookupOverride对象并添加到BeanDefinition中。

实例化时,如果判断出来当前BeanDefinition中没有LookupOverride,那就直接用构造方法反射得到一个实例对象

如果存在LookupOverride对象,也就是类中存在@Lookup注解了的方法,那就会生成一个代理对象

@Lookup注解就是方法注入,使用demo如下:

@Component
public class UserService {

 private OrderService orderService;

 public void test() {
  OrderService orderService = createOrderService();
  System.out.println(orderService);
 }

 @Lookup("orderService")
 public OrderService createOrderService() {
  return null;
 }

}

3.4、源码

调用AbstractAutowireCapableBeanFactory–》doCreateBean --》createBeanInstance

BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			// 真正实例化的地方
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}

四、BeanDefinition的后置处理

Bean对象实例化出来之后,接下来就应该给对象的属性赋值了。在真正给属性赋值之前,Spring又提供了一个扩展点

MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition()可以对此时的BeanDefinition进行加工,比如

4.1、使用案例

@Component
public class LjcMergedBeanDefinitionPostProcessor implements MergedBeanDefinitionPostProcessor {

 @Override
 public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
  if ("userService".equals(beanName)) {
   beanDefinition.getPropertyValues().add("orderService", new OrderService());
  }
 }
}

在Spring源码中,AutowiredAnnotationBeanPostProcessor就是一个MergedBeanDefinitionPostProcessor,它的postProcessMergedBeanDefinition()中会去查找注入点,并缓存在AutowiredAnnotationBeanPostProcessor对象的一个Map中(injectionMetadataCache)。


4.2、源码

synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
				try {
					// BeanDefinition的后置处理
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				}
				mbd.postProcessed = true;
			}
		}

遍历BeanPostProcessors集合、如果有实现MergedBeanDefinitionPostProcessor,则调用postProcessMergedBeanDefinition

protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
			if (bp instanceof MergedBeanDefinitionPostProcessor) {
				MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
				bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
			}
		}
	}

五、实例化后

在处理完BeanDefinition后,Spring又设计了一个扩展点:InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation(),比如:

5.1、使用案例

@Component
public class LjcInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

 @Override
 public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {

  if ("userService".equals(beanName)) {
   UserService userService = (UserService) bean;
   userService.test();
  }

  return true;
 }
}

上述代码就是对userService所实例化出来的对象进行处理。


5.2、源码

调用地址:AbstractAutowireCapableBeanFactory#populateBean

if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
						return;
					}
				}
			}
		}


这个扩展点,在Spring源码中基本没有怎么使用。


六、自动注入(spring自带)

这里的自动注入指的是Spring的自动注入,后续依赖注入文章中单独讲

主要是指Bytype和byname,只是现在基本大家都不怎么用了,都是用@autowried了,所以就不展示案例了 。


6.1、源码

调用地址:AbstractAutowireCapableBeanFactory#populateBean

PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

		int resolvedAutowireMode = mbd.getResolvedAutowireMode();
		if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
			MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
			// Add property values based on autowire by name if applicable.
			if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
				autowireByName(beanName, mbd, bw, newPvs);
			}
			// Add property values based on autowire by type if applicable.
			if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
				autowireByType(beanName, mbd, bw, newPvs);
			}
			pvs = newPvs;
		}

七、属性填充(依赖注入)

这个步骤中,就会处理@Autowired、@Resource、@Value等注解,也是通过
InstantiationAwareBeanPostProcessor.postProcessProperties()扩展点来实现的

7.1、使用案例

实现一个自己的自动注入功能

@Component
public class LjcInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

 @Override
 public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
  if ("userService".equals(beanName)) {
   for (Field field : bean.getClass().getFields()) {
    if (field.isAnnotationPresent(LjcInject.class)) {
     field.setAccessible(true);
     try {
      field.set(bean, "123");
     } catch (IllegalAccessException e) {
      e.printStackTrace();
     }
    }
   }
  }

  return pvs;
 }
}

关于@Autowired、@Resource、@Value的底层源码,会在后续的依赖注入文章中单独说明


7.2、源码

源码位置:AbstractAutowireCapableBeanFactory#populateBean

PropertyDescriptor[] filteredPds = null;
		if (hasInstAwareBpps) {
			if (pvs == null) {
				pvs = mbd.getPropertyValues();
			}
			// 遍历调用
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
					if (pvsToUse == null) {
						if (filteredPds == null) {
							filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
						}
						pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
						if (pvsToUse == null) {
							return;
						}
					}
					pvs = pvsToUse;
				}
			}
		}

八、执行Aware

完成了属性赋值之后,Spring会执行一些回调,包括:

BeanNameAware:回传beanName给bean对象。

BeanClassLoaderAware:回传classLoader给bean对象。

BeanFactoryAware:回传beanFactory给对象。


8.1、源码

private void invokeAwareMethods(String beanName, Object bean) {
		if (bean instanceof Aware) {
			if (bean instanceof BeanNameAware) {
				((BeanNameAware) bean).setBeanName(beanName);
			}
			if (bean instanceof BeanClassLoaderAware) {
				ClassLoader bcl = getBeanClassLoader();
				if (bcl != null) {
					((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
				}
			}
			if (bean instanceof BeanFactoryAware) {
				((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
			}
		}
	}

九、初始化前

初始化前,也是Spring提供的一个扩展点:BeanPostProcessor.postProcessBeforeInitialization()

9.1、使用案例

@Component
public class LjcBeanPostProcessor implements BeanPostProcessor {

 @Override
 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  if ("userService".equals(beanName)) {
   System.out.println("初始化前");
  }

  return bean;
 }
}

利用初始化前,可以对进行了依赖注入的Bean进行处理。


9.2、源码

调用地方

Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

在Spring源码中:

1、InitDestroyAnnotationBeanPostProcessor会在初始化前这个步骤中执行@PostConstruct的方法;

2、ApplicationContextAwareProcessor会在初始化前这个步骤中进行其他Aware的回调:

2.1、EnvironmentAware:回传环境变量

2.2、EmbeddedValueResolverAware:回传占位符解析器

2.3、ResourceLoaderAware:回传资源加载器

2.4、ApplicationEventPublisherAware:回传事件发布器

2.5、MessageSourceAware:回传国际化资源

2.6、ApplicationStartupAware:回传应用其他监听对象,可忽略

2.7、ApplicationContextAware:回传Spring容器ApplicationContext

十、初始化

查看当前Bean对象是否实现了InitializingBean接口,如果实现了就调用其afterPropertiesSet()方法
执行BeanDefinition中指定的初始化方法

invokeInitMethods(beanName, wrappedBean, mbd);

十一、初始化后

这是Bean创建生命周期中的最后一个步骤,也是Spring提供的一个扩展点:BeanPostProcessor.postProcessAfterInitialization(),比如:

11.1、使用案例

@Component
public class LjcBeanPostProcessor implements BeanPostProcessor {

 @Override
 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  if ("userService".equals(beanName)) {
   System.out.println("初始化后");
  }

  return bean;
 }
}

可以在这个步骤中,对Bean最终进行处理,Spring中的AOP就是基于初始化后实现的,初始化后返回的对象才是最终的Bean对象。


11.2、源码

if (mbd == null || !mbd.isSynthetic()) {
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

在初始化之前还会调用InitializingBean#afterPropertiesSet

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/11490.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在Ubuntu下安装地球物理学专业软件madagascar

1.下载安装文件 git clone https://github.com/ahay/src RSFSRC这一步搞好后会在当前路径下生成RSFSRC 文件夹 2.预安装 安装依赖软件 sudo apt-get install libxaw7-dev freeglut3-dev libnetpbm10-dev libgd-dev \ libplplot-dev libavcodec-dev libcairo2-dev libjpeg-d…

(设计模式)工厂模式

工厂模式 工厂模式&#xff08;Factory Pattern&#xff09;是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。 在工厂模式中&#xff0c;我们在创建对象时不会对客户端暴露创建逻辑&#xff0c;并且是通过使用…

【C++】C++基础知识(五)---数组

C基础知识&#xff08;五&#xff09;1. 一维数组1.1 定义方式1.2 数组名用途1.3 案例分析2. 二维数组2.1 定义方式2.2 数组名用途2.3 案例分析3. 多维数组1. 一维数组 数组就是一个集合&#xff0c;里面存放了相同类型的数据元素。 特点&#xff1a; 1、数组中每个元素的数据…

探究并发和并行、同步和异步、进程和线程、阻塞和非阻塞、响应和吞吐等

一. 并发和并行 操作系统扫盲&#xff1a; 1. 对于单核cpu而言(不管单核单线程也好&#xff0c;单核多线程也罢)&#xff0c;同一时间只能干一件事&#xff01;&#xff01;为了看起像可以“同时干多件事”&#xff0c;windows操作系统把cpu的时间划分为长短基本相同的时间区间…

Golang入门笔记(3)—— 运算符

本来这块代码&#xff0c;作为有了一点语言基础的人是真不想写的&#xff0c;因为和其他语言没有什么不同的地方&#xff0c;但是就怕 真的还有人看我的帖子&#xff0c;还真怕真的有初学者在看我帖子&#xff0c;所以还是贴心的抄了一遍。 和Java不同的地方 1. &变量取地址…

【Spring】一文带你吃透基于XML的DI技术

个人主页&#xff1a; 几分醉意的CSDN博客_传送门 文章目录&#x1f496;基于XML的DI✨set注入&#x1f4ab;基本语法&#x1f4ab;和属性名无关&#x1f4ab;给非自定义类属性赋值&#x1f4ab;引用类型注入✨构造注入&#x1f4ab;name属性&#x1f4ab;index属性✨引用类型的…

哈希表(hash_table)的原理

一、hash_table的介绍 hash_table可提供对任何键值对的存取和删除操作。由于操作对象是键值对&#xff0c;所以hash table也可被视为一种字典结构(dictionary)。这种结构的用意在于提供常数时间的基本操作&#xff0c;就像stack或queue那样。乍听之下这几乎是不可能的任务&…

SpringBoot中使用Redis实现分布式锁

文章目录一。Redis实现分布式锁原理二。代码实现Redis分布式锁一。Redis实现分布式锁原理 以下内容为转载部分&#xff0c;主要介绍Redis实现分布式锁的背景&#xff1a; 转载博客&#xff1a;https://blog.csdn.net/fuzhongmin05/article/details/119251590 为什么需要分布式…

[附源码]计算机毕业设计JAVA高校创新创业项目管理系统

[附源码]计算机毕业设计JAVA高校创新创业项目管理系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM…

数字化转型重塑企业竞争优势,SaaS电商系统助力锂电池行业实现降本增效

作为我国核心基础工业的关键材料之一&#xff0c;锂电池在汽车、医疗器械、航天航空、消费类电子产品等领域均可发挥重要作用。近年来&#xff0c;随着新能源汽车的快速发展有力带动了锂电池行业的市场需求&#xff0c;锂电池已成为化学电源应用领域中最具竞争力的电池&#xf…

Vue | Vue.js 实现过渡动画

&#x1f5a5;️ Vue .js专栏&#xff1a;Vue .js 实现过渡动画 &#x1f9d1;‍&#x1f4bc; 个人简介&#xff1a;一个不甘平庸的平凡人&#x1f36c; ✨ 个人主页&#xff1a;CoderHing的个人主页 &#x1f340; 格言: ☀️ 路漫漫其修远兮,吾将上下而求索☀️ &#x1f44…

基于51单片机的智能路灯控制系统proteus仿真原理图PCB

功能&#xff1a; 0.本系统采用STC89C52作为单片机 1.LCD1602液晶实时显示当前时间/环境光强/工作模式 2.支持路灯故障检测 3.工作时间内(17~24时)&#xff0c;两个路灯同时点亮&#xff0c;24时以后&#xff0c;B路灯关闭&#xff0c;若检测到由物体通过&#xff0c;路灯B点亮…

R17 redcap

微信同步更新欢迎关注同名modem协议笔记 在5G时代&#xff0c;某些场景的终端并不需要特别复杂&#xff0c;只要满足成本低&#xff0c;功耗小&#xff0c;尺寸小等要求即可&#xff0c;例如视频监控&#xff0c;可穿戴设备&#xff0c;工业无线传感器等&#xff0c;redcap就是…

electron vue 模仿qq登录界面

1、使用vuecli创建vue项目 我用的vue2 vue create qq_test2、安装electron npm install electron -g //or npm install electron12.0.11 //老版本3、vue项目安装Electron-builder打包工具 版本我选择的是12 vue add electron-builder4、在vue项目的src下有个background.…

收藏|多指标时序预测方式及时序特征工程总结

背景 现如今&#xff0c;随着企业业务系统越来越复杂&#xff0c;单指标时间序列预测已不能满足大部分企业需求。在复杂的系统内&#xff0c;如果采用单一的指标进行时间序列预测&#xff0c;由于各个指标相互作用的关系&#xff0c;因此会因为漏掉部分指标因素导致出现预测精…

进程间的通信 - 剪切板

剪切板是系统维护管理的一块内存区域&#xff0c;本机的所有进程都可以访问。当一个进程复制数据时&#xff0c;先将数据放在该内存区&#xff0c;当另一个进程粘贴时&#xff0c;则是从该内存区块取出数据 剪切板操作&#xff1a; 其实在剪切板中也就那几个API在使用&#x…

tf模型落地安卓之旧事重提

之前将tf模型落地安卓了&#xff0c;其实就是clone一下官方的代码&#xff0c;然后配置下环境就打包了&#xff0c;没啥技术含量&#xff0c;现在再看&#xff0c;问题就在环境配置了。 1&#xff0c;Unable to start the daemon process. Q Group 277356808 The project use…

西安某1000M3浮顶油罐设计(成品油库1000m³油罐设计与制造工艺)

目 录 1 浮顶油罐及其发展概况 2 2 设计方案 3 2.1 各种设计方法 3 2.2 各种方法优缺点比较 3 2.3 油罐的基础 4 3 罐壁设计 5 3.1 罐壁的强度计算 5 3.2 浮顶油罐的风力稳定计算 6 3.3 浮顶油罐的抗震计算 9 3.4 罐壁结构 14 4 罐底设计 18 4.1 罐底结构设计 18 4.2 罐底的应…

【离散数学】第三章 测试

1.单选题 A&#xff1d;{1,2,3},A上关系R{<1,2>,<2,2>,<2,3>,<3,3>}&#xff0c;则t(R) A. {<1,2>,<2,2>,<2,3>,<3,3>} B. {<1,2>,<1,3>,<2,2>,<2,3>,<3,3>} C. {<1,1>,<2,2>,<…

【面试宝典】Spring Boot 系列面试题

1、什么是 Spring Boot? 多年来&#xff0c;随着新功能的增加&#xff0c;spring 变得越来越复杂。如果必须启动一个新的 Spring 项目&#xff0c;我们必须添 加构建路径或添加 Maven 依赖关系&#xff0c;配置应用程序服务器&#xff0c;添加 spring 配置。 因此&#xff0c…