Bean的生命周期就是指:在Spring中,一个Bean是如何生成的,如何销毁的
Spring最重要的功能就是帮助程序员创建对象(也就是IOC),而启动Spring就是为创建Bean对象做准备,所以我们先明白Spring到底是怎么去创建Bean的,也就是先弄明白Bean的生命周期。
Bean生命周期流程图:
根据我们前面的知识,这个spring启动,会做两件事情:扫描+创建非懒加载的单例Bean。那我们看看Spring1是如何完成这两部的
关于Spring启动流程,后续会单独的课详细讲,这里先讲一下Spring扫描的底层实现:
MetadataReader表示类的元数据读取器,主要包含了一个AnnotationMetadata,功能有
值得注意的是,CachingMetadataReaderFactory解析某个.class文件得到MetadataReader对象是利用的ASM技术,并没有加载这个类到JVM。并且,最终得到的ScannedGenericBeanDefinition对象,beanClass属性存储的是当前类的名字,而不是class对象。(beanClass属性的类型是Object,它即可以存储类的名字,也可以存储class对象)
最后,上面是说的通过扫描得到BeanDefinition对象,我们还可以通过直接定义BeanDefinition,或解析spring.xml文件的,或者@Bean注解得到BeanDefinition对象。(后续课程会分析@Bean注解是怎么生成BeanDefinition的)。
BeanDefinition表示bean的定义,在spring源码中BeanDefinition是一个接口,因此他有很多具体的实现类。Spring启动的时候会进行扫描,会先调用org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents(String basePackage)
扫描某个包路径,并得到BeanDefinition的Set集合。
通过扫描得到所有BeanDefinition之后,就可以根据BeanDefinition创建Bean对象了,但是在Spring中支持父子BeanDefinition,和Java父子类类似,但是完全不是一回事。
父子BeanDefinition实际用的比较少,使用是这样的,比如:
<bean id="parent" class="com.ding.service.Parent" scope="prototype"/>
<bean id="child" class="com.ding.service.Child"/>
这么定义的情况下,child是单例Bean。
<bean id="parent" class="com.ding.service.Parent" scope="prototype"/>
<bean id="child" class="com.ding.service.Child" parent="parent"/>
但是这么定义的情况下,child就是原型Bean了。
因为child的父BeanDefinition是parent,所以会继承parent上所定义的scope属性。如果自己定义了scope属性,就会覆盖。自己没定义就用父亲的,自己定义了就用自己的。
而在根据child来生成Bean对象之前,需要进行BeanDefinition的合并,得到完整的child的BeanDefinition。
下面这个方法很重要:我们先找到这个方法
第一步
第二步
第三步:
第四步:
第五步:
第六步:
第七
下一步:
他合并是将两个合并成一个第三个,不是在原来的基础上改的,是直接合并成一个新的,也就是第三个,也就是rootBeanDefinition对象,然后把rootBeanDefinition存到mergedBeanDefinitions里面。
下一步
下一步:
下一步:
下一步:
下一步:
下一步:
克隆也是生成一个新的,无论怎样都是要生成一个新的。克隆之后再把他的原来的属性啥的再复制过去。
如果是下面这种情况存在:就是传进来的userService就是有parent
如果上面这种情况怎么做呢?
pbd表示的是父亲的BeanDefinition。
现在是有两个map,一个是BeanDeifinitionMap,一个是mergedBeanDefinitions这个Map,后续要去创建某个bean的时候,就是用的合并之后的。
可以简单理解:如果不考虑合并,根据名字拿到beanDefinition,看是不是单例的,是不是非懒加载的,如果是且不失抽象bean,在判断这个bean是不是factoryBean,如果不是,普通bean就调用getBean,这个getBean里面会调用这个creatBean的,
总体的意思就是把所有扫描出来的BeanDefinition去进行遍历,去合并,看是不是非懒加载的单例,如果是就去创建bean。也就是下面那段代码for循环的作用。
我们再看整个方法是preInstantiateSingletons,实例化单例,准确是实例化非懒加载的单例,那个for循环就会去实例化,去创建单例bean,是把所有的单例实例化完才会继续往下走,下面这个for循环,又在遍历名字,如果我们一个单例创建完后会放到单例池里面的,这个singletonObjects就是单例池。
下一步
下一步:
上面其他讲的就是每个单例bean被创建的过程去执行的,但是上面这个方法是所有的非懒加载的单例bean都被创建之后才会去调用这个方法。
接下来我们再看这个FactoryBean
Bean只是一个概念,统称所有被SpringIOC工厂(容器)管理的对象,硬要说也要有一个规范,那就是Object。
FactoryBean是一个实际的接口,在spring-beans包中:org.springframework.beans.factory.FactoryBean
FactoryBean通过编码方式负责Bean的生成,由于同样会被SpringIOC工厂(容器)所管理,所以可以看成一个具有特殊功能的Bean。
我们怎么根据一个beanName名字去判断bean是不是一个FactoryBean
下面以这个举例说明
beanName,如果传进来的是userservice,那么得到的还是userservice,因为现在还不适合讲,先这么理解。
如果说我传进来的是DingJunXiaFactorybean这个名字,这个name就是DingJunXiaFactorybean,我们调这个getSingleton,就是从单例池拿,能不能拿到值?
其实是拿不到值,因为现在在最开始最开始的地方,但是这个逻辑,如果能拿到值,那拿到的是什么对象?我们是Factorybean会生成两个对象,一个是本身DingJunXiaFactorybean,一个是Object返回的对象,那放到单例池的是哪个对象,是DingJunXiaFactorybean这个对象,所以这个方法的逻辑,在别的地方也会调用,把这个DingJunXiaFactorybean这个对象传进来,我们拿到的就是DingJunXiaFactorybean这个对象,我们拿到就判断DingJunXiaFactorybean是不是一个Factorybean,如果没有拿到,就只能根据BeanDefinition去判断,这个逻辑就是当前我这个名字没有对应的BeanDefinition,回去看有没有父BeanDefinition,这个里面讲的是父Factorybean与子Factorybean,如果当前我没有,就去看父beanFactory里面有没有。
现在看一个例子:
我们现在相当于又new了一个spring容器,new了一个beanfactory,我们可以去指定另一个扫描的类。
知道这个意思就可以了
这个方法是拿到合并之后的BeanDefinition,mbd就是bean的属性。
一个名字只会对应一个BeanDefinition,但BeanDefinition存的类是我根据名字是DingJunXiaFactorybean,那么判断出来时FactoryBean了,接下来:
我先调他,FACTORY_BEAN_PREFIX = “&”;是一个符号,调这个方法就是会去创建,因为是两个bean,到底创建谁呢?创建的就是DingJunXiaFactorybean对象。
下一步:
下面是那个SmartFactoryBean接口实现的过程:
下一步:
对于FactoryBean,调用getBean的名字就会创建,到底在哪创建的呢?这个getBean有两种情况,一种是加了特殊符号的名字,一种是没加的,getBean的时候肯定会判断处理的,我们传的名字不一样,我们希望得到的结果是什么呢?
如果我们name传的是&XXX,那么beanName就是XXX,也就是DingJunXiaFactorybean的对象,就是getObject方法返回的对象
下一步:
下一步:
如果sharedInstance是FactoryBean,那么久调用getObject()返回对象
下一步:
下一步:
如果站在启动spring的角度看的话,这个getBean就可以理解为是创建一个bean。
spring内部是如何根据某一个BeanDefinition去创建一个bean的?
我们首先来看第一种方式:
下一步:
下一步:
我们找个例子进行演示那个bean定义别名:
取别名
取别名的第二种方式
测试:
那么,他这个别名,底层到底是怎么存储的?
底层有一个Map,这个Map的key就是别名,value就是beanName,在这个案例中具体意思是:
因为一个bean可以用很多别名,如果key,value反过来的话是很不好理解的。因为key是唯一的,所以key不能是beanName,只能是别名。
点进去这个transformedBeanName方法:
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);
}
public boolean hasBeanClass() {
return (this.beanClass instanceof Class);
}
如果beanClass属性的类型是Class,那么就直接返回,如果不是,则会根据类名进行加载(doResolveBeanClass方法所做的事情)
会利用BeanFactory所设置的类加载器来加载类,如果没有设置,则默认使用ClassUtils.getDefaultClassLoader()所返回的类加载器来加载。
ClassUtils.getDefaultClassLoader()
当前BeanDefinition对应的类成功加载后,就可以实例化对象了,但是…
在Spring中,实例化对象之前,Spring提供了一个扩展点,允许用户来控制是否在某个或某些Bean实例化之前做一些启动动作。这个扩展点叫InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()。比如:
@Component
public class ZhouyuBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if ("userService".equals(beanName)) {
System.out.println("实例化前");
}
return null;
}
}
如上代码会导致,在userService这个Bean实例化前,会进行打印。
值得注意的是,postProcessBeforeInstantiation()是有返回值的,如果这么实现:
@Component
public class ZhouyuBeanPostProcessor 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依赖注入也不会进行了,会跳过一些步骤,直接执行初始化后这一步。
在实例化之前有一个步骤:
beanPostProcessor支持干涉某些bean在创建过程中的动作,支持初始化前、初始化后做一些事情。那么在实例化之前实例化之后是不是也可以增一个扩展点(方法),让程序员去做呢去用。所以也是有的。
这个InstantiationAwareBeanPostProcessor继承了BeanPostProcessor
在BeanPostProcessor那两个初始化前初始化后基础上新增了其他的方法 :实例化之前与实例化之后。
我们直接上例子哈:(每一个BeanPostProcessor不是只针对一个bean的,只要是任何一个bean只要是被创建就要会被执行这个实例化的。但是我们可以针对某一个bean进行去判断。)
package com.ding.service;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
@Component
public class DingJunXiaBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化之前");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化之后");
return bean;
}
}
现在还可以只针对某一种bean打印:
假设我们现在换一个接口,换成InstantiationAwareBeanPostProcesson,换完接口之后,这两个方法不用动,其实这个接口继承了BeanPostProcessor,同时我们之前重写的方法不变,我们还可以重写一些新的方法,比如:实例化前、实例化后这样的。
实例化前传进来的参数是什么,和初始化方法不一样哎,因为我是实例化前,我还没对象,所以只能传类给你
我现在不想走你spring那实例化那一套逻辑,我想针对我的userService我自己去实例化。意思就是你类给我了,在userService实例化之前,我自己去实例化,那spring就不用去实例化了。你自己实例化得到一个对象之后,直接把这个对象返回,意思就是我在你userService实例之前我自己去实例化,我自己实例化的话,那你spring就不要去实例化了。
我们再回到原来的方法,首先是加载类,加载完之后会进入到resolveBeforeInstantiation方法
进入到resolveBeforeInstantiation方法之后,就会到这个hasInstantiationAwareBeanPostProcessors判断有没有那个BeanPostProcessor,怎么判断的呢?就是5.3版本新加的这个Cache缓存,对BeanPostProcessor进行缓存。回到我们刚刚那个方法哈
缓存。总共有四个BeanPostProcessor子类,要判断是哪一类然后村起来。
具体的四个类型:cache里面就是四个list,这四个全是BeanPostProcessor的子类。会这些子类进行一些分类,然后会遍历所有的BeanPostProcessor,判断他的类型。
在开始的时候只要是BeanPostProcessor他就存起来,并不关系到底是哪一类,然后再去掉方法,去遍历去判断分类,然后存起来。
判断当前spring容器中你到底有没有写这个实例化回调的这个beanPostProcessors。开始的时候不关系类型,所有的BeanPostProcessor是存在一个List里面的。
然后我们去遍历判断
我们现在有这个InstantiationAwareBeanPostProcessors类了,只有一个吗?不可能只有一个,也可能有多了,有多个就要循环了
如果有InstantiationAwareBeanPostProcessors类的多个beanPostProcessors,先调第一个再掉第二个,如果第一个就有返回值了,就不会再调第二个了,直接就返回了,如果第一个没有返回值则会再调用第二个。
实例化前的意思是,一旦有实例化前,并且方法里面返回了一个具体对象的话,那spring后续这个代码就都不会走了。
说白了,随便返回一个类型都可以。
首先判断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);
首先判断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);
如果没有设置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的,那么对应的就是方式一。
第一节已经讲过一遍大概原理了,后面有一节课单独分析源码实现。推断完构造方法后,就会使用构造方法来进行实例化了。
额外的,在推断构造方法逻辑中除开会去选择构造方法以及查找入参对象意外,会还判断是否在对应的类中是否存在使用@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;
}
}
Bean对象实例化出来之后,接下来就应该给对象的属性赋值了。在真正给属性赋值之前,Spring又提供了一个扩展点MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition(),可以对此时的BeanDefinition进行加工,比如:
@Component
public class ZhouyuMergedBeanDefinitionPostProcessor 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)。
在处理完BeanDefinition后,Spring又设计了一个扩展点:InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation(),比如:
@Component
public class ZhouyuInstantiationAwareBeanPostProcessor 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所实例化出来的对象进行处理。
这个扩展点,在Spring源码中基本没有怎么使用。
这里的自动注入指的是Spring的自动注入,后续依赖注入课程中单独讲
这个步骤中,就会处理@Autowired、@Resource、@Value等注解,也是通过InstantiationAwareBeanPostProcessor.postProcessProperties()扩展点来实现的,比如我们甚至可以实现一个自己的自动注入功能,比如:
@Component
public class ZhouyuInstantiationAwareBeanPostProcessor 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(ZhouyuInject.class)) {
field.setAccessible(true);
try {
field.set(bean, "123");
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
return pvs;
}
}
关于@Autowired、@Resource、@Value的底层源码,会在后续的依赖注入课程中详解。
完成了属性赋值之后,Spring会执行一些回调,包括:
初始化前,也是Spring提供的一个扩展点:BeanPostProcessor.postProcessBeforeInitialization(),比如
@Component
public class ZhouyuBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if ("userService".equals(beanName)) {
System.out.println("初始化前");
}
return bean;
}
}
利用初始化前,可以对进行了依赖注入的Bean进行处理。
在Spring源码中:
这是Bean创建生命周期中的最后一个步骤,也是Spring提供的一个扩展点:BeanPostProcessor.postProcessAfterInitialization(),比如:
@Component
public class ZhouyuBeanPostProcessor 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对象。
不用@Autowired注解,我们用spring1自带的这种方式照样可以完成依赖注入。但是有一个前提,你不用@Autowired用你自己写的注解,一定有set方法。这种方式和@Bean方式是一样的效果,那到底是什么意思呢?什么是btType,就是spring定义了一个bean,他的这种自动装配的模式是通过类型。找的是set方法。找UserService有哪些set方法,然后找当前参数的bean对象,传给set方法,调set方法。
,
byName又是什么呢?
正常的一个Bean实例化之前,后面就是实例化、属性填充、初始化、初始化前、如果实例化前就返回一个对象的话,会经过初始化后。直接从实例化前直接跳到了初始化后。为什么要经过这一步呢?因为这和AOP是有关系的。其实FactoryBean也有类似的逻辑,就是先得到一个对象,这个对象就是getobject返回的对象,spring调用getobject对象返回之后并不会经过初始化这些东西了,就是直接跳到初始化后。
初始化前如果你返回了对象,那我就直接return,如果你没有返回对象,那么就有spring去创建对象,怎么创建的还是实例化。实例化得到一个对象之后,就会有一个步骤,去控制
通过这个方法实例化去控制实例化之后,程序员你还想修改BeanDefinition,也是可以修改的。
那我现在这么写有什么用呢,bean销毁什么时候执行呢?销毁的时候执行,那他什么时候执行呢?垃圾回收调我这个方法吗?JVM肯定不会调用这个方法。spring中的销毁平时用的不太多,那他什么时候触发呢?当你整个spring容器关闭的时候才会触发销毁。
只有进程正常关闭才会触发销毁这个方法。
下面我们就好好看一看这个相关方面的源码,到底是在做什么事情。
我们虽然在看bean销毁,但是和bean创建也是有关系的:
比如说我们在创建某一个bean的时候会进入下面这个代码,把所有的生命周期都走完,把你刚刚创建好的这个bean对象给传进来
传进来之后,首先判断当前的这个bean是不是单例的,如果是单例的,就会判断这个方法,这个就是你这个bean在销毁的时候到底要不要执行某些逻辑,并不是所有的bean去销毁的时候都要去执行一个方法。
在某个bean在创建的时候会去判断当前这个bean到底有没有销毁的方法,
怎么判断的呢?就看你有没有实现DisposableBean、AutoCloseable这两个接口,如果有这两个接口,我直接返回true,代表我有这个销毁的方法,如果没有进到下面这个方法,首先判断你这个beanDefinition你自己有没有指定一个方法,会把你所配的方法的名字拿出来,假设方法的名字是等于这个inferred字符串,那你当前这个bean对相应的close方法,如果发现你这个类里面有close方法或者shutdown方法,会当作销毁方法执行
假设没有自定义这个销毁的方法 ,就会去判断,spring里面有没有这个DestructionAwareBeanPostProcessors接口,
然后看一下该接口的具体类型
叫这个名字
进入然后往下巴拉
如果有这个DestructionAwareBeanPostProcessors,就会把刚刚定义的DestructionAwareBeanPostProcessors拿出来传到getBeanPostProcessorCache().destructionAware这个方法里面.
这个方法就是去调用你现在容器里面销毁回调的BeanPostProcessor,我们程序员可以自己去实现一下DestructionAwareBeanPostProcessor这个接口的具体实现类的一个bean,spring到时候就会把你创建的这个bean传给这个方法requiresDestruction
进去之后是:
他不是针对某一个bean的,是针对所有的bean的,所有的bean都会拿到。
这个方法我们看到是销毁的逻辑,初始化前的时候也会调用这个方法。
他会把当前类里面的每一个方法去遍历,如果你有PostConstruct注解,那么我就把你当作是一个初始化的方法,如果你有preDestroy这个注解,那么我就把你当作一个销毁的方法把你存进来。
如果我找到了PostConstruct注解所修饰的方法,把当前这个类的所有的方法是初始化的方法。dowhile是遍历父类,就是看完自己还要看父类。
这两个地方不同,我自己的类遍历完了,会把自己初始化的方法加到initMethods方法里面来,紧接着会遍历父类里面的方法,我会把父类里面的初始化方法也会生成一个集合。把这个集合加到里面那个集合里面,把父类定义的初始化方法加到了前面。先执行父类的初始化方法,再执行子类的。
而对于销毁他是没有这个逻辑的,就是父类一定在子类之前执行。
如果我是有销毁逻辑的,我是怎么做的呢?把这个名字传进来,构造一个这个对象,传进来干嘛呢,只是存到一个map里面了
存map里面
容器关闭的时候,就是把map里面的bean拿出来,然后去调用相对应销毁的逻辑。我们有很多种方式去定义销毁的逻辑,所以他这里需要一个适配,这是一种设计模式。
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- igat.cn 版权所有 赣ICP备2024042791号-1
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务