深入理解spring生命周期与BeanPostProcessor的实现原理

2023-11-13

上面两篇文章分别介绍了spring生命周期中初始化和销毁的几种方式以及统一后置BeanPostProcessor接口的使用,可以点击以下链接查看:

 

  1. 三分钟了解spring-bean生命周期之初始化和销毁的三种方式

  2. 一分钟学会spring-bean的统一前后置处理器BeanPostProcessor

     

 

今天我们就来一起看看spring底层是如何实现这些的,在看它的实现原理之前,我们再来把之前几种方式结合放一个例子中演示下

 

新建一个User1对象如下:

 

/**
 * 定义一个实现InitializingBean DisposableBean的bean
 *
 * @author zhangqh
 * @date 2018年5月4日
 */
public class User1 implements InitializingBean,DisposableBean{
    public User1(){
        System.out.println("实例化User1的构造方法......");
    }
    public void destroy() throws Exception {
        System.out.println("调用实现DisposableBean的destroy方法....");
    }
    public void afterPropertiesSet() throws Exception {
        System.out.println("调用实现InitializingBean的afterPropertiesSet方法......");
    }
    public void initUser(){
        System.out.println("执行initMethod方法.....");
    }
    public void destroyUser(){
        System.out.println("执行destroyMethod方法......");
    }
}

 

新建一个MyBeanPostProcessor实现BeanPostProcessor如下:

 

/**
 * 定义一个前置后置处理器
 *
 * @author zhangqh
 * @date 2018年5月6日
 */
public class MyBeanPostProcessor implements BeanPostProcessor {
    public Object postProcessBeforeInitialization(Object bean, String beanName)
            throws BeansException {
        // 这边只做简单打印   原样返回bean
        System.out.println("postProcessBeforeInitialization===="+beanName);
        return bean;
    }
    public Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {
        // 这边只做简单打印   原样返回bean
        System.out.println("postProcessAfterInitialization===="+beanName);
        return bean;
    }
}

 

主配置文件如下:

 

/**
 * 定义一个注解配置文件 必须要加上@Configuration注解
 *
 * @author zhangqh
 * @date 2018年4月30日
 */
@Configuration
public class MainConfig {
    @Bean(initMethod="initUser",destroyMethod="destroyUser")
    public User1 getUser1(){
        return new User1();
    }
    @Bean
    public MyBeanPostProcessor getMyBeanPostProcessor(){
        return new MyBeanPostProcessor();
    }
}

 

测试代码如下:

 

public static void main(String[] args) {
        // 使用AnnotationConfigApplicationContext获取spring容器ApplicationContext2
        AnnotationConfigApplicationContext applicationContext2 = new AnnotationConfigApplicationContext(MainConfig.class);
        applicationContext2.close();
}

 

运行结果如下:

 

实例化User1的构造方法......
postProcessBeforeInitialization====getUser1
调用实现InitializingBean的afterPropertiesSet方法......
执行initMethod方法.....
postProcessAfterInitialization====getUser1
调用实现DisposableBean的destroy方法....
执行destroyMethod方法......

 

从结果中可以看出他们之前执行的顺序如下:注意其中的位置序号下边文章会用到

1,首先执行bean的构造方法,
2,BeanPostProcessor的postProcessBeforeInitialization方法
3,InitializingBean的afterPropertiesSet方法
4,@Bean注解的initMethod方法
5,BeanPostProcessor的postProcessAfterInitialization方法
6,DisposableBean的destroy方法
7,@Bean注解的destroyMethod方法

接下来我们再来看看spring底层的实现,首先进入程序的启动类AnnotationConfigApplicationContext方法如下:

 

public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
        this();
        register(annotatedClasses);
        refresh();
}

 

里边有两个方法一个是register注册对应的java配置类和另一个是refresh方法,我们重点来看这个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.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);
            try {
                // 省略大部分代码
                // 实例化所有的不是延迟加载(延迟加载的只有在使用的时候才会实例化)的bean实例
                finishBeanFactoryInitialization(beanFactory);
                // Last step: publish corresponding event.
                finishRefresh();
            }catch (BeansException ex) {
                // 省略部分代码
            }finally {
                resetCommonCaches();
            }
        }
}

 

接下来我们重点来看下finishBeanFactoryInitialization实例化bean的方法,进去之后我们发现最后有一个preInstantiateSingletons方法如下:

 

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
        // 省略大部分代码
        // Instantiate all remaining (non-lazy-init) singletons.
        beanFactory.preInstantiateSingletons();
}

 

继续查看preInstantiateSingletons对应实现如下:

 

@Override
    public void preInstantiateSingletons() throws BeansException {
        // Iterate over a copy to allow for init methods which in turn register new bean definitions.
        // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
        List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
        // 循环所有的bean实例化
        for (String beanName : beanNames) {
            RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                if (isFactoryBean(beanName)) {
                    final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
                    boolean isEagerInit;
                    if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                        isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
                            @Override
                            public Boolean run() {
                                return ((SmartFactoryBean<?>) factory).isEagerInit();
                            }
                        }, getAccessControlContext());
                    }
                    else {
                        isEagerInit = (factory instanceof SmartFactoryBean &&
                                ((SmartFactoryBean<?>) factory).isEagerInit());
                    }
                    if (isEagerInit) {
                        // 获取bean方法
                        getBean(beanName);
                    }
                }
                else {
                    getBean(beanName);
                }
            }
        }
        // 省略部分代码
}

 

我们发现里边的关键方法getBean如下:

 

@Override
public Object getBean(String name) throws BeansException {
        return doGetBean(name, null, null, false);
}

 

继续跟进去如下:

 

protected <T> T doGetBean(
            final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
            throws BeansException {
        final String beanName = transformedBeanName(name);
        Object bean;
        // 检查缓存中是否已经存在了bean实例.
        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null && args == null) {
            if (logger.isDebugEnabled()) {
                if (isSingletonCurrentlyInCreation(beanName)) {
                    logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                            "' that is not fully initialized yet - a consequence of a circular reference");
                }
                else {
                    logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
                }
            }
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }else {
            // 省略部分代码。。。。。
            try {
                // 省略部分代码。。。。。
                // 判断bean是否配置的是单实例
                if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                        @Override
                        public Object getObject() throws BeansException {
                            try {
                                return createBean(beanName, mbd, args);
                            }
                            catch (BeansException ex) {
                                destroySingleton(beanName);
                                throw ex;
                            }
                        }
                    });
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }// bean配置的是多实例
                else if (mbd.isPrototype()) {
                    // It's a prototype -> create a new instance.
                    Object prototypeInstance = null;
                    try {
                        beforePrototypeCreation(beanName);
                        prototypeInstance = createBean(beanName, mbd, args);
                    }
                    finally {
                        afterPrototypeCreation(beanName);
                    }
                    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                }
                else {// 既不是单实例也不是多实例的逻辑
                    // 省略部分代码。。。。。
                }
            }
            catch (BeansException ex) {
                cleanupAfterBeanCreationFailure(beanName);
                throw ex;
            }
        }
        // 省略部分代码。。。。。
        return (T) bean;
    }

 

接下来重点看一下其中创建bean的方法createBean如下:

 

protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
        RootBeanDefinition mbdToUse = mbd;
        // 省略部分代码
        // 重点注意doCreateBean方法
        Object beanInstance = doCreateBean(beanName, mbdToUse, args);
        if (logger.isDebugEnabled()) {
            logger.debug("Finished creating instance of bean '" + beanName + "'");
        }
        return beanInstance;
}

 

继续跟进可以看到doCreateBean方法如下:

 

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
            throws BeanCreationException {
        // 省略部分代码
        // Initialize the bean instance.
        Object exposedObject = bean;
        try {
            populateBean(beanName, mbd, instanceWrapper);
            if (exposedObject != null) {
                exposedObject = initializeBean(beanName, exposedObject, mbd);
            }
        }
        catch (Throwable ex) {
            if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
                throw (BeanCreationException) ex;
            }
            else {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
            }
        }
        // 省略部分代码......
        // Register bean as disposable.
        // 注意这个地方  下面讲销毁的时候说讲到
        try {
            registerDisposableBeanIfNecessary(beanName, bean, mbd);
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
        }
        return exposedObject;
}

 

可以发现其中有一个initializeBean方法如下:

 

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
        // 省略部分代码。。。。
        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
            // 重点来了BeanPostProcessor的postProcessBeforeInitialization方法执行的地方
            // 这也是为什么他执行所有的初始化之前的原因了
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }
        try {
            // 初始化bean
            invokeInitMethods(beanName, wrappedBean, mbd);
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    (mbd != null ? mbd.getResourceDescription() : null),
                    beanName, "Invocation of init method failed", ex);
        }
        if (mbd == null || !mbd.isSynthetic()) {
            // BeanPostProcessor的PostProcessorsAfterInitialization方法执行的地方
            // 初始化完成之后执行BeanPostProcessor的postProcessorsAfterInitialization
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }
        return wrappedBean;
}

 

到这BeanPostProcessor的实现已经很清晰了吧BeanPostProcessorpostProcessBeforeInitialization(方法位置2)BeanPostProcessorpostProcessAfterInitialization(方法位置5)的执行位置我们搞清楚了,那上面的位置3位置4又是怎么执行的呢,让我们继续到invokeInitMethods里边看看如下:

 

 

protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
            throws Throwable {
        boolean isInitializingBean = (bean instanceof InitializingBean);
        if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
            if (logger.isDebugEnabled()) {
                logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
            }
            if (System.getSecurityManager() != null) {
                try {
                    AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                        @Override
                        public Object run() throws Exception {
                            ((InitializingBean) bean).afterPropertiesSet();
                            return null;
                        }
                    }, getAccessControlContext());
                }
                catch (PrivilegedActionException pae) {
                    throw pae.getException();
                }
            }
            else {
                // 位置3的 InitializingBean的afterPropertiesSet方法
                ((InitializingBean) bean).afterPropertiesSet();
            }
        }
        if (mbd != null) {
            // 位置4的 @Bean注解的initMethod方法
            String initMethodName = mbd.getInitMethodName();
            if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                    !mbd.isExternallyManagedInitMethod(initMethodName)) {
                invokeCustomInitMethod(beanName, bean, mbd);
            }
        }
}

 

聪明的你应该一眼就能看到位置3,以及位置4的执行顺序了吧

 

好了初始化的逻辑部分搞清楚了,接下来我们一起来看看销毁的流程,销毁开始于applicationContext.close();方法,点进去看如下:

 

@Override
public void close() {
        synchronized (this.startupShutdownMonitor) {
            doClose();
            // If we registered a JVM shutdown hook, we don't need it anymore now:
            // We've already explicitly closed the context.
            if (this.shutdownHook != null) {
                try {
                    Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
                }
                catch (IllegalStateException ex) {
                    // ignore - VM is already shutting down
                }
            }
        }
}

 

看到其中有一个doClose继续跟进去看会发现有一个destroyBeans方法,再进去直到找到destroySingletons方法如下:

 

public void destroySingletons() {
        // 省略部分代码
        // 循环所有的disposableBean执行对应的destroy方法
        for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
            destroySingleton(disposableBeanNames[i]);
        }
        // 省略部分代码
}

 

在继续从destroySingleton方法进去可以找到destroyBean方法如下:

 

protected void destroyBean(String beanName, DisposableBean bean) {
        // 省略部分代码
        // 重点的地方到了  执行DisposableBean 中的destroy 也就是位置6 中对应的打印
        if (bean != null) {
            try {
                bean.destroy();
            }
            catch (Throwable ex) {
                logger.error("Destroy method on bean with name '" + beanName + "' threw an exception", ex);
            }
        }
        // 省略部分代码
}

 

到这里我们已经完成了90%的原理分析了,这个时候细心的你一定会发现那位置7中的@Bean注解中配置的destroyMethod是在什么时候调用的呢,好像上面都没有讲到,要想知道destroyMethod的调用让我们回到上面讲bean初始化中的doCreateBean方法中其中有一个registerDisposableBeanIfNecessary方法进去看如下:

 

 

protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
        AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
        if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
            if (mbd.isSingleton()) {
                // Register a DisposableBean implementation that performs all destruction
                // work for the given bean: DestructionAwareBeanPostProcessors,
                // DisposableBean interface, custom destroy method.
                registerDisposableBean(beanName,
                        new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
            }
            else {
                // A bean with a custom scope...
                Scope scope = this.scopes.get(mbd.getScope());
                if (scope == null) {
                    throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
                }
                // 关键代码
                scope.registerDestructionCallback(beanName,
                        new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
            }
        }
}

 

继续进去到DisposableBeanAdapter类如下:

 

public DisposableBeanAdapter(Object bean, String beanName, RootBeanDefinition beanDefinition,
            List<BeanPostProcessor> postProcessors, AccessControlContext acc) {
        Assert.notNull(bean, "Disposable bean must not be null");
        this.bean = bean;
        this.beanName = beanName;
        this.invokeDisposableBean =
                (this.bean instanceof DisposableBean && !beanDefinition.isExternallyManagedDestroyMethod("destroy"));
        this.nonPublicAccessAllowed = beanDefinition.isNonPublicAccessAllowed();
        this.acc = acc;
        String destroyMethodName = inferDestroyMethodIfNecessary(bean, beanDefinition);
        if (destroyMethodName != null && !(this.invokeDisposableBean && "destroy".equals(destroyMethodName)) &&
                !beanDefinition.isExternallyManagedDestroyMethod(destroyMethodName)) {
            this.destroyMethodName = destroyMethodName;
            this.destroyMethod = determineDestroyMethod();
            if (this.destroyMethod == null) {
                if (beanDefinition.isEnforceDestroyMethod()) {
                    throw new BeanDefinitionValidationException("Couldn't find a destroy method named '" +
                            destroyMethodName + "' on bean with name '" + beanName + "'");
                }
            }
            else {
                Class<?>[] paramTypes = this.destroyMethod.getParameterTypes();
                if (paramTypes.length > 1) {
                    throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" +
                            beanName + "' has more than one parameter - not supported as destroy method");
                }
                else if (paramTypes.length == 1 && boolean.class != paramTypes[0]) {
                    throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" +
                            beanName + "' has a non-boolean parameter - not supported as destroy method");
                }
            }
        }
        this.beanPostProcessors = filterPostProcessors(postProcessors, bean);
    }

 

怎么样,到现在是不是就很清晰了,位置7的打印其实也是DisposableBean方法中打印出来的,@bean注解的destroyMethod其实是在初始化的时候转换成DisposableBean的实现放入到了disposableBeans中

 

到此今天所有内容就到此结束了,最后以一张流程图总结下如下:

 

以上是今天文章的所有内容,欢迎大家吐槽

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深入理解spring生命周期与BeanPostProcessor的实现原理 的相关文章

随机推荐

  • Axios 入门教程

    Axios Get Post 别名方式 Get Post Axios 引入 axios 的 js 文件 使用 axios 发送请求 并获取响应结果axios method get url then function resp alert r
  • 【NOIP2012】开车旅行(倍增)

    题面 Description 小A 和小B决定利用假期外出旅行 他们将想去的城市从1到N 编号 且编号较小的城市在编号较大的城市的西边 已知各个城市的海拔高度互不相同 记城市 i的海拔高度为Hi 城市 i 和城市 j 之间的距离 d i j
  • var、let、const的区别

    var let const的区别 大体区别 const定义的变量不可以修改 而且必须初始化 var定义的变量可以修改 如果不初始化会输出undefined 不会报错 var定义的变量没有块的概念 可以跨酷爱访问 不能跨函数访问 let是块级
  • 【华为OD机试真题】投篮大赛(C++&java&python)100%通过率 超详细代码注释 代码优化

    华为OD机试真题 2022 2023 真题目录 点这里 华为OD机试真题 信号发射和接收 试读 点这里 华为OD机试真题 租车骑绿道 试读 点这里 投篮大赛 知识点字符串 时间限制 1s空间限制 256MB限定语言 不限 题目描述 你现在是
  • dbnet训练_1215

    https blog csdn net hhhhhhhhhhwwwwwwwwww article details 123904386 ops request misc 257B 2522request 255Fid 2522 253A 25
  • java使用jdbc操作数据库

    一 概述 为了持久化 方便管理数据 出现了mysql sqlserver等多种数据库 我们可以直接这些数据库中使用sql语言增删改查管理数据 但是对于业务人员来说不懂sql 没法通过sql直接在数据库操作 所以我们要通过程序提供对数据库的操
  • 用Qt实现QQ好友列表界面伸缩功能(完全一模一样)(伸展和收缩、抽屉效果、类似树形控件)(鼠标划过QSS效果)

    本文主要总结用Qt的自定义按钮和QWidget界面实现QQ好友列表的界面伸展和收缩功能 以及鼠标滑过 鼠标单击的QSS样式表效果 全文分为两大部分 分别是原理讲解和效果实现 抽缩界面效果图 源代码下载地址 https download cs
  • 增量测试:自顶向下测试&自底向上测试

    本博客主要内容 自顶向下测试和自底向上测试的优缺点 软件开发周期流程 不同的测试方法针对不同的测试阶段 一 自顶向下测试 优点 1 如果主要的缺陷发生在程序的顶层将非常有利 2 一旦引入I O功能 提交测试或更容易 3 早期的程序框架可以进
  • VS2013,MFC,在视图类里添加鼠标左键响应函数OnLButtonDown

    以CVoronoi2D为例子 点击类视图的View 右击选择类向导 选择WM LBUTTONDOWN 鼠标左击响应函数 然后点击添加处理程序 代码会自动生成一个响应函数 如图 如果对您有帮助 可以评论一下 谢谢
  • 失败的人生图片_人到中年,做事失败了,很可能是遇到了以下五种情况

    人至中年 也到了迈入成功大门的时刻 但并非每个人都能在中年获得成功 相反 有不少人却在中年的时候失败 人至中年面临失败 其实原因有很多 但大多数情况下 可能是遇到了以下五种情况 究竟有哪五种情况呢 如果您想知道 就让小编来为您揭秘 本文所有
  • hash与map的区别联系应用

    一 hashtable原理 哈希表又名散列表 其主要目的是用于解决数据的快速定位问题 考虑如下一个场景 一列键值对数据 存储在一个table中 如何通过数据的关键字快速查找相应值呢 不要告诉我一个个拿出来比较key啊 呵呵 大家都知道 在所
  • 设计模式GitHub找的好东西

    https github com DovAmir awesome design patterns https github com JakubVojvoda design patterns cpp https github com Wale
  • Appium自动化框架从0到1之 业务模块封装(登录页面业务操作)

    我们这次来封装登录页面业务操作 在上代码之前 我们先了解一下登录场景 用户名 密码 小鱼1号 fish1 小鱼2号 fish2 小鱼3号 fish3 然后 我们在登录的时候 会进行一下几个操作 我们先输入账号 密码 点击 登录按钮 登录后
  • 【UE4】TSubclassOf的使用

    TSubclassOf TSubclassOf 是提供 UClass 类型安全性的模板类 例如您在创建一个投射物类 允许设计者指定伤害类型 您可只创建一个 UClass 类型的 UPROPERTY 让设计者指定派生自 UDamageType
  • phpcms(1)phpcms V9 MVC模式 与 URL访问解析(转)

    1 URL访问解析 观察访问网页时的网址 可以得出模块访问方法 如下示例 http www abcd com cn phpcms index php m content c index a show id 1 关于此URL解析如下 m co
  • Android Studio 链接外部项目的Module

    Android Studio 链接外部项目的Module 前言 引用外部Module 操作教程 最后我还有一句话要说 两情若是久长时 又岂在 朝朝暮暮 前言 有的时候自己写的Module要在多个项目同步使用 但是使用Android Stud
  • 九.修改AD用户属性-账户-账户选项

    LDAP修改ad用户账户选项 这里只提供了两种常用的 更多的请参考专栏 帮助类中的枚举 region 修改用户选项
  • 安装C/C++插件一直显示正在安装如何处理?

    有一位小伙伴在看我的一篇文章 VScode使用教程 菜鸟版 本文链接 VScode使用教程 菜鸟版 中二病的易哥哥的博客 CSDN博客问我安装C C 插件一直显示正在安装如何处理 因为我实在没有遇到过这种情况 我唯一可以想得到的办法时重启V
  • 关于IntelliJ IDEA找不到getServletContext()的问题

    在Eclipse里面使用Tomcat7 0以上 HttpServletRequest request的getServletContext完全没有问题 但是在IntelliJ Idea里面却没有提示 而且getRealPath 还显示过期 网
  • 深入理解spring生命周期与BeanPostProcessor的实现原理

    上面两篇文章分别介绍了spring生命周期中初始化和销毁的几种方式以及统一后置BeanPostProcessor接口的使用 可以点击以下链接查看 三分钟了解spring bean生命周期之初始化和销毁的三种方式 一分钟学会spring be