bean 订阅
EJB是Enterprise Java Bean的缩写,一个Bean扮演着应用程序素材的角色。它包含有一个functional interface,一个life-cycle interface,以及一个实现它所支援的商业方法的类别。 展开全文
EJB是Enterprise Java Bean的缩写,一个Bean扮演着应用程序素材的角色。它包含有一个functional interface,一个life-cycle interface,以及一个实现它所支援的商业方法的类别。
信息
缩    写
Bean
定    义
描述Java的软件组件模型
类    型
应用程序素材的角色
目    的
将可以重复使用的软件代码打包
应用范围
应用于服务器的部件
外文名
Enterprise Java Bean
Bean定义
JavaBean是描述Java的软件组件模型,有点类似于Microsoft的COM组件概念。在Java模型中,通过JavaBean可以无限扩充Java程序的功能,通过JavaBean的组合可以快速的生成新的应用程序。对于程序员来说,最好的一点就是JavaBean可以实现代码的重复利用,另外对于程序的易维护性等等也有很重大的意义。比如说一个购物车程序,要实现购物车中添加一件商品这样的功能,就可以写一个购物车操作的JavaBean,建立一个public的AddItem成员方法,前台Jsp文件里面直接调用这个方法来实现。如果后来又考虑添加商品的时候需要判断库存是否有货物,没有货物不得购买,在这个时候我们就可以直接修改JavaBean的AddItem方法,加入处理语句来实现,这样就完全不用修改前台jsp程序了。一个简单的JavaBeanFirstJavaBean.javaimportjava.io.*;public class FirstJavaBean {private String FirstProperty = new String("");public FirstJavaBean() {}public String getFirstProperty() {return FirstProperty;}public void setFirstProperty(String value) {FirstProperty = value;}public static void main(String[] args){System.out.println("My First JavaBean!");}}如果运行这个程序,就会出现下面的结果:My First JavaBean!这是一个很典型的JavaBean的代表,简单地解释一下,FirstProperty是其中的一个属性(Property),外部通过get/set方法可以对这个属性进行操作,如果您写过VB的class的话,对这个就再也熟悉不过了。Main方法是为了测试程序用的,写JavaBean可以先不必加入到Jsp程序中调用,而直接用main方法来进行调试,调试好以后就可以在Jsp程序中调用了。
收起全文
精华内容
下载资源
问答
  • bean
    千次阅读
    2021-11-12 09:17:49

    Spring bean是Spring框架在运行时管理对象。Spring bean是任何Spring应用程序的基本构建块。你编写的大多数应用程序逻辑代码都将放在Spring bean中。

    Spring bean的管理包括:

    • 创建一个对象
    • 提供依赖项(例如其他bean,配置属性)
    • 拦截对象方法调用以提供额外的框架功能
    • 销毁一个对象

    Spring bean是框架的基本概念。作为Spring的用户,你应该对这个核心抽象有深刻的理解。

    如何定义Spring bean?

    如你所知,Spring负责创建bean对象。但首先,你需要告诉框架它应该创建哪些对象。

    你是怎么做到的?

    通过提供bean定义

    Bean定义告诉Spring框架应该将哪些类用作bean。但那还不是全部。Bean定义就像食谱一样。它们还描述了bean的属性。我们将在本文后面讨论属性。但在我们进入之前,让我们关注bean的定义。

    可以通过三种不同的方式定义Spring bean:

    使用构造@Component注释(或其衍生物)注释你的类

    编写在自定义Java配置类中使用@Bean注释的bean工厂方法

    在XML配置文件中声明bean定义

    在现代项目中,你将仅使用组件注释和bean工厂方法。如果涉及到XML配置,那么Spring现在主要允许它向后兼容。

    bean定义方法之间选择主要取决于对要用作Spring bean的类的源代码的访问。

    Spring bean为@Component

    如果你拥有源代码,则通常直接在类上使用@Component注释。

    在运行时,Spring会找到所有使用@Component或其派生类进行注释的,并将它们用作bean定义。查找带注释的类的过程称为组件扫描

    你可能想知道@Component的衍生物是什么。

    @Conpontent衍生物是Spring构造型注释,它们本身用@Component注释。这个事实允许我们用它们代替@Component

    @Component衍生列表包括:

    @Service

    @Repository

    @Controller

    注释之间的区别纯粹是信息性的。它们允许你根据通用职责轻松对bean进行分类。你可以使用这些注释将bean类标记为特定应用程序层的成员,Spring框架会将它们全部视为@Components

    使用@Bean作为工厂方法

    如果某个类属于某个外部库而你无法使用@Component进行注释,你必须在自定义bean的配置类中使用@ Bean注释创建工厂方法。如果你不想使类依赖于Spring,你也可以将此选项用于你拥有的类。

    这是一个带有单个bean工厂方法的示例配置类:

    @Configuration
    class MyConfigurationClass {
     
     @Bean
     public NotMyClass notMyClass() {
     return new NotMyClass();
     }
     
    }
    

    Spring使用工厂方法在运行时创建实际对象。如你所见,该方法只返回一个类的新实例。如果某个类属于某个外部库而你无法使用@Component进行注释,则使用@Bean创建工厂方法是唯一的选择。

    @Configuration注释也来自Spring。实际上,它是@Component的衍生物,但具有特殊用途。注释将类标记为@Bean定义的容器。你可以在单个配置类中编写多个工厂方法。

    Spring bean属性

    此时,你已经知道如何将类标记为Spring bean。下一步是学习如何自定义bean的功能。

    无论你选择哪种bean定义方法,它们都允许描述同一组bean属性。属性提供有关Spring应如何创建对象的详细信息。

    Spring bean属性列表包括:

    • 名称
    • 依赖
    • 范围
    • 初始化模式
    • 初始化回调
    • 破坏回调

    为了简化bean定义,Spring为几乎所有属性提供了默认值。但是,了解如何自定义默认值非常重要。让我们逐个研究Spring bean属性。

    1.Bean类

    创建bean定义时,将其与应用程序中的单个具体类连接。这个类本身是bean的主要属性。

    当Spring查找依赖项时,class属性是bean的默认标识符。这是否意味着你不能为单个类提供多个bean定义?不,这是可能的。但在这种情况下,为避免歧义,你应该使用另一个bean标识符:一个name名称。

    2. Bean名称name

    Spring bean名称是Spring用于标识bean的自定义字符串。与bean类不同,名称在整个应用程序中必须是唯一的。你不能定义两个具有相同名称的bean,即使它们的类型不同。

    幸运的是,你不必为你创建的每个bean设置名称。Spring在运行时为其内部使用生成名称。除非你想按名称识别bean,否则可以安全地使用默认设置。

    你需要使用bean名称的主要情况是为使用@Bean批注定义的同一个类提供了几个bean。在这种情况下,名称允许你标识要用作另一个bean的依赖项的特定实例

    3. 如何命名Spring bean?

    使用@Bean批注的name属性。这是两个具有相同类型的bean的示例。

    @Configuration
    class MyConfigurationClass {
     
     @Bean(name = "myBeanClass")
     MyBeanClass myBeanClass() {
     return new MyBeanClass();
     }
     
     @Bean(name = "anotherMyBeanClass")
     MyBeanClass anotherMyBeanClass() {
     return new MyBeanClass();
     }
     
    }
    

    实际上,你不经常定义bean名称。对于单个类具有多个bean是相当罕见的情况。然而,如果能了解命名bean的各种可能性也是不错的。

    4. Bean依赖项

    用作bean的对象可以使用其他bean来执行其作业。当Spring创建一个定义某些依赖项的对象时,框架需要首先创建这些依赖项。这些依赖项也可以有自己的依赖项。

    在面向对象的应用程序中,我们通常使用相关对象巨大图表。幸运的是,我们不必考虑如何构建此图。我们不必考虑应该创建对象的顺序。Spring为我们做了所有这些。

    Spring对你的唯一期望是特定bean的依赖项列表。

    5.如何定义bean依赖?

    Bean依赖关系定义是一个复杂的主题,值得单独一篇文章。请考虑以下段落作为该主题的介绍。

    当你有一个用@Component标记的并且只有一个构造函数时,Spring使用构造函数参数列表作为必需依赖项列表。默认情况下,框架使用构造函数参数类型来提供适当的对象。

    @Component
    class BeanWithDependency {
     
     private final MyBeanClass beanClass;
     
     BeanWithDependency(MyBeanClass beanClass) {
     this.beanClass = beanClass;
     }
     
    }
    

    过去,我们在构造函数上使用@Autowired注释。但是,从Spring 4.3开始,如果只有一个构造函数,则不是强制性的。但是,如果bean类定义了多个构造函数,则应使用@Autowired标记一个。这样Spring知道哪个构造函数包含bean依赖项列表。

    出于同样的原因,bean工厂方法可以定义其依赖关系。Spring使用适当的对象调用方法。

    @Bean
    BeanWithDependency beanWithOptionalDependency(MyBeanClass beanClass) {
     return new BeanWithDependency(beanClass);
    }
    

    6. Bean作用域

    Spring bean的范围定义了框架在运行时创建的特定类的实例数。作用域还描述创建新对象的条件

    Spring为你的bean提供了几个作用域。框架的核心有两个:

    • 单例 - 单个实例
    • 原型 - 多个实例

    此外,Spring还附带了专门用于Web应用程序的bean作用域:

    • 请求
    • 会话
    • 全局会话
    • 应用级别Application

    所有bean的默认作用域是单例。当bean具有单例作用域时,Spring只创建一个实例并在整个应用程序中共享它。单例是无状态对象的完美选择。如今,我们应用程序中的绝大多数bean都是无状态单例。

    另一方面,如果对象包含状态,则应考虑其他作用域。要选择正确的一个,你应该问自己框架应该将该状态保留在内存中多长时间。但这是另一篇文章。

    7. 如何设置作用域?

    无论是使用@Component直接注释还是使用@Bean创建工厂方法,该方法都是相同的。使用@Scope批注及其字符串属性选择范围。

    @Component
    @Scope("prototype")
    class MyPrototypeClass {
     //...
    }
    @Bean
    @Scope("prototype")
    MyPrototypeClass myPrototypeClass() {
     return new MyPrototypeClass();
    }
    
    • 更重要的是,对于Web作用域,Spring附带了额外的别名注释。你可以使用这些注释代替@Scope
    • @RequestScope
    • @SessionScope
    • @ApplicationScope

    Bean初始化模式

    当你的应用程序启动时,Spring会在启动时会立即创建所有单例bean。此默认行为允许我们快速检测bean定义中的错误。另一方面,立即性eager bean初始化会使应用程序的启动变慢

    幸运的是,你可以将bean的创建延迟到实际需要的时刻。你可以使用@Lazy注释执行此操作。

    @Component
    @Lazy
    class MyLazyClass {
     //...
    }
    

    Bean初始化回调

    一旦Spring根据bean定义创建新实例,你可能希望运行一些对象初始化逻辑。

    如果此逻辑不依赖于框架,则可以在对象的构造函数中运行它。但是,为了确保在Spring初始化对象之后运行逻辑(例如,在可选的依赖注入之后),你应该使用初始化回调。

    如何设置bean初始化回调?

    如果使用@Component定义bean ,则有两个选项:

    使bean类实现InitializingBean。接口将强制你实现初始化方法。

    编写自定义初始化方法并使用javax @PostContruct注释进行标记。

    在这两种情况下,Spring都会为你运行初始化回调。

    用工厂方法定义的bean怎么样?

    你可以使用@Bean及其名为initMethod的属性设置初始化回调。该属性需要一个具有初始化方法名称的字符串。

    @Bean(initMethod = "someInitMethodName")
    MySpringBeanClass meBeanClass() {
     return new MySpringBeanClass();
    }
    

    有趣的是,用作初始化回调的方法可以具有私有访问控制。Spring使用反射机制来调用该方法。

    Bean销毁回调

    与初始化回调类似,你可以定义Spring销毁bean时应调用的方法。Predestroy回调的使用要少得多,但要注意它们的存在是很好的。

    如何设置bean销毁回调?

    同样,如果你可以访问bean类的源代码,则可以使用以下两个选项之一:

    实现DisposableBean接口。Spring使用其唯一的方法进行销毁回调。

    • 编写自定义方法并使用Javax API中的@PreDestroy注释它。
    • 对于工厂方法,使用@Bean批注及其destroyMethod属性。
    @Bean(name = "myBeanClass", destroyMethod = "cleanUpMethod")
    MySpringBeanClass meBeanClass() {
     return new MySpringBeanClass();
    }
    

    Spring如何从bean定义创建对象?

    当你启动Spring应用程序时,框架首先会创建一个名为ApplicationContext的特殊对象。ApplicationContext,也称为控制反转(IoC)容器,是框架的核心

    ApplicationContext是存在bean对象的容器。

    上下文负责检测和读取Spring bean定义。一旦ApplicationContext加载了bean定义,它就可以根据提供的bean属性及其依赖关系开始为应用程序创建bean对象。

    更多相关内容
  • Spring Bean生命周期,好像人的一生。。

    万次阅读 多人点赞 2022-03-15 11:43:41
    Spring原来是一个社会,Bean就是身不由己的“社会人”……

    大家好,我是老三,上节我们手撸了一个简单的IOC容器五分钟,手撸一个Spring容器!,这节我们来看一看Spring中Bean的生命周期,我发现,和人的一生真的很像。

    简单说说IoC和Bean

    IoC,控制反转,想必大家都知道,所谓的控制反转,就是把new对象的权利交给容器,所有的对象都被容器控制,这就叫所谓的控制反转。

    控制反转

    Bean,也不是什么新鲜玩意儿,它们就是一帮身不由己的Java对象,生命周期受到容器控制。

    Bean生命周期和人生

    Bean生命周期四大阶段

    我们知道,bean的作用域有好几种,这篇文章只讨论完全被IoC容器控制的单例Bean。

    对于普通的Java对象来说,它们的生命周期就是:

    • 实例化
    • 对象不再被使用时通过垃圾回收机制进行回收

    这就像是生活在大自然里的动物,悄然出生,悄然死亡。

    大象-图片来源网络

    而对于Spring Bean的生命周期来说,可以分为四个阶段,其中初始化完成之后,就代表这个Bean可以使用了:

    • 实例化 Instantiation
    • 属性赋值 Populate
    • 初始化 Initialization
    • 销毁 Destruction

    人和动物不一样,存在非常复杂的社会。

    高楼大厦中的行人

    我们来看看社会里的人,一生要经历哪些阶段,是不是和Bean的生命周期很像呢?

    • 出生:作为一个自然人降临在这个世界
    • 登记:登记身份证号,姓名,正式成为人类社会的一份子
    • 成长:接受教育,成为对社会有用的人
    • 工作:为社会创造价值
    • 死亡:人死如灯灭,不过人这盏灯灭了,还要把灯台埋起来

    image-20220303101042089

    Bean实例化的时机也分为两种,BeanFactory管理的Bean是在使用到Bean的时候才会实例化Bean,ApplicantContext管理的Bean在容器初始化的时候就回完成Bean实例化。

    BeanFactory就是相对不那么健全的原始一些的社会,ApplicantContext是发达健全的现代社会。

    BeanFactory和Applicantcontext

    Bean详细生命周期

    我们讲到了Bean容器四个阶段,会有一些容器级的方法,进行前置和后置的处理,比如InstantiationAwareBeanPostProcessor、BeanPostProcessor接口方法。这些方法独立于Bean之外,并且会注册到Spring容器中,在Spring容器创建Bean的时候,进行一些处理。

    后处理器

    这就好像,孩子出生之前,需要做一些准备,比如备孕、养胎、备产什么的,出生之后,需要做一些护理。孩子上学前后,也需要做一些学籍的管理。

    那么有了各种各样的扩展之后,我们再接着看看Bean的详细的生命周期。首先,我们面临一个问题——Bean的生命周期从什么时候开始的呢?

    上面写了,Bean实例化前后,可以进行一些处理,但是如果从Bean实例化前算开始,那么就要追溯到容器的初始化、beanDefiinition的加载开始。

    所以这篇文章里,我们取生命周期直接从Bean实例化开始,但是大家也要知道,Bean实例化前后,可以使用后处理器进行处理,例如BeanFactoryPostProcessor、InstantiationAwareBeanPostProcessor。

    大家也不要困扰,就像计算人生的起点,是从母亲怀孕算起,还是从孩子出生算起?我们这里取了出生开始而已。

    Bean生命周期

    • 实例化:第 1 步,实例化一个 Bean 对象
    • 属性赋值:第 2 步,为 Bean 设置相关属性和依赖
    • 初始化:初始化的阶段的步骤比较多,5、6步是真正的初始化,第 3、4 步为在初始化前执行,第 7 步在初始化后执行,初始化完成之后,Bean就可以被使用了
    • 销毁:第 8~10步,第8步其实也可以算到销毁阶段,但不是真正意义上的销毁,而是先在使用前注册了销毁的相关调用接口,为了后面第9、10步真正销毁 Bean 时再执行相应的方法

    我们发现Bean生命周期的详细过程,是不是也像人生的历程,出生、登记,不过是很短的事情。慢慢长大成人,要经历人生的四分之一,而成长,来源于教育,不管是学校的还是社会的,接受教育前,要登记学籍,上学的时候,自己还要努力……,到最后,要发一纸薄薄的文凭,标志着我们成为可以捶打的“社会人”。

    然后,为社会奉献四十年。最后老去,离世。不过Bean的世界,没有退休——当然,也许,人的世界也没有退休。

    人的曲线

    我们发现中间的一些扩展过程也可以分四类:

    Bean周期四类过程

    • 一:获取社会资源/Aware接口:Aware接口的作用是让Bean能拿到容器的一些资源,例如BeanNameAware可以拿到BeanName。就好像上学之前,要取一个学名——不知道多少人上学之前不知道自己大名叫什么,是吧?二毛。

    • 二:必备各种手续和证/后处理器:在Bean的生命周期里,会有一些后处理器,它们的作用就是进行一些前置和后置的处理,就像上学之前,需要登记学籍,上学之后,会拿到毕业证。

    • 三:个人选择/生命周期接口:人可能无法选择如何出生,但也许可以选择如何活着和如何死去,InitializingBean和DisposableBean 接口就是用来定义初始化方法和销毁方法的。

    • 四:主观能动/配置生命周期方法:环境影响人,人也在影响环境,成长的时候认真努力,衰亡的时候也可以豁达乐观。可以通过配置文件,自定义初始化和销毁方法。

    PersonBean的一生

    话不多说,接下来我们拿一个例子,来看看PersonBean的一生,我们先来看一下它的流程!

    PersonBean的一生

    用文字描述一下这个过程:

    1. Bean容器在配置文件中找到Person Bean的定义,这个可以说是妈妈怀上了。
    2. Bean容器使用Java 反射API创建Bean的实例,孩子出生了。
    3. Person声明了属性no、name,它们会被设置,相当于注册身份证号和姓名。如果属性本身是Bean,则将对其进行解析和设置。
    4. Person类实现了BeanNameAware接口,通过传递Bean的名称来调用setBeanName()方法,相当于起个学名。
    5. Person类实现了BeanFactoryAware接口,通过传递BeanFactory对象的实例来调用setBeanFactory()方法,就像是选了一个学校。
    6. PersonBean实现了BeanPostProcessor接口,在初始化之前调用用postProcessBeforeInitialization()方法,相当于入学报名。
    7. PersonBean类实现了InitializingBean接口,在设置了配置文件中定义的所有Bean属性后,调用afterPropertiesSet()方法,就像是入学登记。
    8. 配置文件中的Bean定义包含init-method属性,该属性的值将解析为Person类中的方法名称,初始化的时候会调用这个方法,成长不是走个流程,还需要自己不断努力。
    9. Bean Factory对象如果附加了Bean 后置处理器,就会调用postProcessAfterInitialization()方法,毕业了,总得拿个证。
    10. Person类实现了DisposableBean接口,则当Application不再需要Bean引用时,将调用destroy()方法,简单说,就是人挂了。
    11. 配置文件中的Person Bean定义包含destroy-method属性,所以会调用Person类中的相应方法定义,相当于选好地儿,埋了。

    我们来看看代码!

    PersonBean类

    创建一个PersonBean,让它实现几个特殊的接口,我们来观察一下它的生命周期的流转。

    public class PersonBean implements InitializingBean, BeanFactoryAware, BeanNameAware, DisposableBean {
    
        /**
         * 身份证号
         */
        private Integer no;
    
        /**
         * 姓名
         */
        private String name;
    
        public PersonBean() {
            System.out.println("1.调用构造方法:我出生了!");
        }
    
        public Integer getNo() {
            return no;
        }
    
        public void setNo(Integer no) {
            this.no = no;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
            System.out.println("2.设置属性:我的名字叫"+name);
        }
    
        @Override
        public void setBeanName(String s) {
            System.out.println("3.调用BeanNameAware#setBeanName方法:我要上学了,起了个学名");
        }
    
        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            System.out.println("4.调用BeanFactoryAware#setBeanFactory方法:选好学校了");
        }
    
        @Override
        public void afterPropertiesSet() throws Exception {
            System.out.println("6.InitializingBean#afterPropertiesSet方法:入学登记");
        }
    
        public void init() {
            System.out.println("7.自定义init方法:努力上学ing");
        }
    
        @Override
        public void destroy() throws Exception {
            System.out.println("9.DisposableBean#destroy方法:平淡的一生落幕了");
        }
    
        public void destroyMethod() {
            System.out.println("10.自定义destroy方法:睡了,别想叫醒我");
        }
    
        public void work(){
            System.out.println("Bean使用中:工作,只有对社会没有用的人才放假。。");
        }
    
    }
    
    
    • 实现了InitializingBean, BeanFactoryAware, BeanNameAware, DisposableBean四个接口
    • 定义了no、name两个属性和对应的getter、setter方法
    • 定义了一个实例方法work

    MyBeanPostProcessor

    自定义了一个后处理器MyBeanPostProcessor:

    public class MyBeanPostProcessor implements BeanPostProcessor {
    
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("5.BeanPostProcessor.postProcessBeforeInitialization方法:到学校报名啦");
            return bean;
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("8.BeanPostProcessor#postProcessAfterInitialization方法:终于毕业,拿到毕业证啦!");
            return bean;
        }
    }
    

    配置文件

    定义一个配置文件spring-config.xml:

    • 使用setter注入
    • 定义init-method和destroy-method
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean name="myBeanPostProcessor" class="cn.fighter3.spring.life.MyBeanPostProcessor" />
        <bean name="personBean" class="cn.fighter3.spring.life.PersonBean"
              init-method="init" destroy-method="destroyMethod">
            <property name="idNo" value= "80669865"/>
            <property name="name" value="张铁钢" />
        </bean>
    
    </beans>
    

    测试

    最后测试一下,观察PersonBean的生命周期的流转:

    public class Main {
    
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
            PersonBean personBean = (PersonBean) context.getBean("personBean");
            personBean.work();
            ((ClassPathXmlApplicationContext) context).destroy();
        }
    }
    

    运行结果:

    1.调用构造方法:我出生了!
    2.设置属性:我的名字叫张铁钢
    3.调用BeanNameAware#setBeanName方法:我要上学了,起了个学名
    4.调用BeanFactoryAware#setBeanFactory方法:选好学校了
    5.BeanPostProcessor#postProcessBeforeInitialization方法:到学校报名啦
    6.InitializingBean#afterPropertiesSet方法:入学登记
    7.自定义init方法:努力上学ing
    8.BeanPostProcessor#postProcessAfterInitialization方法:终于毕业,拿到毕业证啦!
    Bean使用中:工作,只有对社会没有用的人才放假。。
    9.DisposableBean#destroy方法:平淡的一生落幕了
    10.自定义destroy方法:睡了,别想叫醒我
    

    看看,是不是和我们图中的流程一致。

    这篇文章就不带大家跟进更多的源码了,如果大家对源码级别的Bean的生命周期感兴趣,可以看看AbstractApplicationContext类里的refresh方法,这个方法是AplicationContext容器初始化的关键点。在这个方法里,调用了finishBeanFactoryInitialization方法,这个方法里调用了getBean方法,getBean方法里调用了AbstractBeanFactorygetBean方法。

    Bean生命周期源码追踪

    最终经过一阵七拐八绕,到达了我们的目标——Bean创建的方法:doGetBean方法,在这个方法里可以看到Bean的实例化,赋值、初始化的过程,至于最终的销毁,可以看看ConfigurableApplicationContext#close()

    结语

    到这,这篇Bean的生命周期文章就走向destory了,自定义destory方法——回顾一下这篇文章的“一生”。

    • Bean的生命周期大致可以分为四个阶段:实例化、属性赋值、初始化、销毁,对应人生的出生、登记、成长、离世。
    • Bean生命周期中可以有很多扩展,就像人生的走向,会受很多影响,社会的环境、自身的选择、自己的努力。


    参考:

    [1]. 《Spring揭秘》

    [2]. Spring官网

    [3].《精通Spring4.X企业应用开发实战》

    [4] .Spring Bean 生命周期 (实例结合源码彻底讲透)

    [5].一文读懂 Spring Bean 的生命周期

    [6].如何记忆 Spring Bean 的生命周期


    干货文章首发,欢迎关注👇👇👇
    展开全文
  • 一文读懂 Spring Bean 的生命周期

    万次阅读 多人点赞 2021-07-05 23:02:56
    今天我们来说一说 Spring Bean 的生命周期,小伙伴们应该在面试中经常遇到,这是正常现象。因为 Spring Bean 的生命周期是除了 IoC、AOP 几个核心概念之外最重要概念,大家务必拿下。可 Spring 源代码又比较复杂,...

    欢迎大家关注我的微信公众号【老周聊架构】,Java后端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。

    一、前言

    今天我们来说一说 Spring Bean 的生命周期,小伙伴们应该在面试中经常遇到,这是正常现象。因为 Spring Bean 的生命周期是除了 IoC、AOP 几个核心概念之外最重要概念,大家务必拿下。可 Spring 源代码又比较复杂,跟着跟着就不知道跟到哪里去了,不太好拿下呀。这倒是真的,而且网上一上来就各种贴流程源码,对初学者来说是真的一脸懵逼,就像字都看的懂,但连在一块就不知道意思了,太绕了。

    本文老周试着讲的通俗易懂些,让更多的小伙伴们轻松的读懂 Spring Bean 的生命周期,并有对它有继续研究学习的想法,那我写此文的目的也就达到了。

    我们讲 Spring Bean 的生命周期之前先来了解两个概念:

    1.1 什么是 Bean

    我们来看下 Spring Framework 的官方文档:

    In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container. Otherwise, a bean is simply one of many objects in your application. Beans, and the dependencies among them, are reflected in the configuration metadata used by a container.

    简而言之,bean 是由 Spring IoC 容器实例化、组装和管理的对象。

    1.2 什么是 Spring Bean 的生命周期

    对于普通的 Java 对象,当 new 的时候创建对象,然后该对象就能够使用了。一旦该对象不再被使用,则由 Java 自动进行垃圾回收。

    而 Spring 中的对象是 bean,bean 和普通的 Java 对象没啥大的区别,只不过 Spring 不再自己去 new 对象了,而是由 IoC 容器去帮助我们实例化对象并且管理它,我们需要哪个对象,去问 IoC 容器要即可。IoC 其实就是解决对象之间的耦合问题,Spring Bean 的生命周期完全由容器控制。

    二、Spring Bean 的生命周期

    这里老周必须要提一下,这里我们说的 Spring Bean 的生命周期主要指的是 singleton bean,对于 prototype 的 bean ,Spring 在创建好交给使用者之后则不会再管理后续的生命周期。

    我们也来复习下 Spring 中的 bean 的作用域有哪些?

    • singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。
    • prototype : 每次请求都会创建一个新的 bean 实例。
    • request : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP request 内有效。
    • session : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效。
    • global-session: 全局 session 作用域,仅仅在基于 Portlet 的 web 应用中才有意义,Spring5 已经没有了。Portlet 是能够生成语义代码(例如:HTML)片段的小型 Java Web 插件。它们基于 portlet 容器,可以像 servlet 一样处理 HTTP 请求。但是,与 servlet 不同,每个 portlet 都有不同的会话。

    我们知道对于普通的 Java 对象来说,它们的生命周期就是:

    • 实例化
    • 该对象不再被使用时通过垃圾回收机制进行回收

    而对于 Spring Bean 的生命周期来说:

    • 实例化 Instantiation
    • 属性赋值 Populate
    • 初始化 Initialization
    • 销毁 Destruction

    实例化 -> 属性赋值 -> 初始化 -> 销毁

    只有四个步骤,这样拆解的话是不是感觉也不难?不像其他人写的那样直接一上来就各种 BeanPostProcessor、BeanFactoryPostProcessor 全部怼进流程里去,别说读者看着头大,自己写的可能短时间内还记得流程,隔个一段时间,你可能都不知道自己写了个啥。

    本来老周想通过 Bean 创建流程入口
    AbstractApplicationContext#refresh() 方法的 finishBeanFactoryInitialization(beanFactory) 处带大家跟一下源码,想了想还是不带入过多的代码进来,直接给到最终的主要逻辑。

    protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
            instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName);
        }
    
        if (instanceWrapper == null) {
        	// 实例化阶段
            instanceWrapper = this.createBeanInstance(beanName, mbd, args);
        }
    
        ...
    
        Object exposedObject = bean;
    
        try {
        	// 属性赋值阶段
            this.populateBean(beanName, mbd, instanceWrapper);
            // 初始化阶段
            exposedObject = this.initializeBean(beanName, exposedObject, mbd);
        } catch (Throwable var18) {
            ...
        }
    
        ...
    }
    

    至于销毁,是在容器关闭时调用的,详见 ConfigurableApplicationContext#close()

    是不是很清爽了?至于 BeanPostProcessor、BeanFactoryPostProcessor 以及其他的类,在老周看来,只不过是对主流程四个步骤的一系列扩展点而已。

    三、Spring Bean 的生命周期的扩展点

    Spring Bean 的生命周期的扩展点超级多,老周这里不可能全部列出来,只说核心的扩展点。这也就是为什么 Spring 的扩展性很好的原因,开了很多的口子,尽可能让某个功能高内聚松耦合,用户需要哪个功能就用哪个,而不是直接来一个大而全的东西。

    3.1 Bean 自身的方法

    比如构造函数、getter/setter 以及 init-method 和 destory-method 所指定的方法等,也就对应着上文说的实例化 -> 属性赋值 -> 初始化 -> 销毁四个阶段。

    在这里插入图片描述
    3.2 容器级的方法(BeanPostProcessor 一系列接口)

    主要是后处理器方法,比如下图的 InstantiationAwareBeanPostProcessorBeanPostProcessor 接口方法。这些接口的实现类是独立于 Bean 的,并且会注册到 Spring 容器中。在 Spring 容器创建任何 Bean 的时候,这些后处理器都会发生作用。

    在这里插入图片描述
    3.2.1 InstantiationAwareBeanPostProcessor 源码分析

    我们翻一下源码发现 InstantiationAwareBeanPostProcessor 是继承了 BeanPostProcessor

    在这里插入图片描述
    在这里插入图片描述

    • InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation 调用点

    Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName)
    返回值:如果返回的不为null,那么后续的Bean的创建流程【实例化、初始化afterProperties】都不会执行,而是直接使用返回的快捷Bean,此时的正常执行顺序如下:
    InstantiationAwareBeanPostProcessor接口中的postProcessBeforeInstantiation,在实例化之前调用。
    BeanPostProcessor接口中的postProcessAfterInitialization,在实例化之后调用。

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    总之,postProcessBeforeInstantiation 在 doCreateBean 之前调用,也就是在 bean 实例化之前调用的,英文源码注释解释道该方法的返回值会替换原本的 Bean 作为代理,这也是 AOP 等功能实现的关键点。

    • InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation 调用点

    boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException
    正常情况下在实例化之后在执行populateBean之前调用
    返回值:如果有指定的bean的时候返回false,那么后续的属性填充和属性依赖注入【populateBean】将不会执行,同时后续的postProcessPropertyValues将不会执行,但是初始化和BeanPostProcessor的仍然会执行。

    在这里插入图片描述

    public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
    实例化之后调用,在方法applyPropertyValues【属性填充】之前
    返回值:如果返回null,那么将不会进行后续的属性填充,比如依赖注入等,如果返回的pvs额外的添加了属性,那么后续会填充到该类对应的属性中。
    pvs:PropertyValues对象,用于封装指定类的对象,简单来说就是PropertyValue的集合,里面相当于以key-value形式存放类的属性和值。
    pds:PropertyDescriptor对象数组,PropertyDescriptor相当于存储类的属性,不过可以调用set,get方法设置和获取对应属性的值。

    在这里插入图片描述
    3.2.2 BeanPostProcessor 源码分析

    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

    在这里插入图片描述
    进入初始化接口:

    在这里插入图片描述
    我们先来看

    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization

    在这里插入图片描述

    • 首先获取到所有的后置处理器 getBeanPostProcessors()
    • 在 for 循环中依次调用后置处理器的方法 processor.postProcessBeforeInitialization(result, beanName);
    • 进入 postProcessBeforeInitialization 方法

    org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization

    在这里插入图片描述

    进入 invokeAwareInterfaces(bean); 方法,当前 bean 实现了 ApplicationContextAware 接口。

    在这里插入图片描述

    • ApplicationContextAwareProcessor#postProcessBeforeInitialization 首先判断此 bean 是不是各种的Aware,如果是它列举的那几个 Aware 就获取 Bean 工厂的权限,可以向容器中导入相关的上下文环境,目的是为了 Bean 实例能够获取到相关的上下文,如果不是它列举的几个 Aware,那就调用 invokeAwareInterfaces(bean),向容器中添加相关接口的上下文环境。

    3.3 工厂后处理器方法(BeanFactoryProcessor 一系列接口)

    包括 AspectJWeavingEnablerCustomAutowireConfigurerConfigurationClassPostProcessor 等。这些都是 Spring 框架中已经实现好的 BeanFactoryPostProcessor,用来实现某些特定的功能。

    我们知道 Spring IoC 容器初始化的关键环节就在 org.springframework.context.support.AbstractApplicationContext#refresh 方法中 ,容器创建的主体流程都在这个方法里面,这个方法是真的重要!!!

    对于工厂后处理器方法老周这里直接带你看 invokeBeanFactoryPostProcessors(beanFactory); 方法,这个方法处理的是 BeanFactoryPostProcessor 接口的 Bean。调用方法如下:

    在这里插入图片描述
    跟到最重要的方法里去,代码虽长,但逻辑中规中矩。

    BeanFactoryPostProcessor:一切处理 BeanFactory 的父接口
    BeanDefinitionRegistryPostProcessor:实现了 BeanFactoryPostProcessor 接口的接口

    在这里插入图片描述
    流程说明:

    • 调用 BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry(registry) 方法。参数 beanFactoryPostProcessors 传入的优先处理掉。然后获取容器注册的,对于这些 Bean 按照 PriorityOrdered 接口、Ordered、没有排序接口的实例分别进行处理。
    • 调用 BeanFactoryPostProcessor#postProcessBeanFactory(beanFactory) 方法。备注:BeanDefinitionRegistryPostProcessor 属于 BeanFactoryPostProcessor 子接口。先处理属于 BeanDefinitionRegistryPostProcessor 接口实例的 postProcessBeanFactory(beanFactory) 方法,然后获取容器注册的。对于这些 Bean 按照 PriorityOrdered 接口、Ordered、没有排序接口的实例分别进行处理。

    3.4 Bean 级生命周期方法

    可以理解为 Bean 类直接实现接口的方法,比如 BeanNameAwareBeanFactoryAwareApplicationContextAwareInitializingBeanDisposableBean 等方法,这些方法只对当前 Bean 生效。

    3.4.1 Aware 类型的接口

    Aware 类型的接口的作用就是让我们能够拿到 Spring 容器中的一些资源。基本都能够见名知意,Aware 之前的名字就是可以拿到什么资源,例如 BeanNameAware 可以拿到 BeanName,以此类推。调用时机需要注意:所有的 Aware 方法都是在初始化阶段之前调用的

    Aware 接口众多,这里同样通过分类的方式帮助大家记忆。Aware 接口具体可以分为两组,至于为什么这么分,详见下面的源码分析。如下排列顺序同样也是 Aware 接口的执行顺序,能够见名知意的接口不再解释。

    Aware Group1

    • BeanNameAware
    • BeanClassLoaderAware
    • BeanFactoryAware

    Aware Group2

    • EnvironmentAware
    • EmbeddedValueResolverAware
      这个知道的人可能不多,实现该接口能够获取 Spring EL 解析器,用户的自定义注解需要支持 SPEL 表达式的时候可以使用,非常方便。
    • ApplicationContextAware(ResourceLoaderAware/ApplicationEventPublisherAware/MessageSourceAware)
      这几个接口可能让人有点懵,实际上这几个接口可以一起记,其返回值实质上都是当前的 ApplicationContext 对象,因为 ApplicationContext 是一个复合接口,如下:
      在这里插入图片描述

    Aware 调用时机源码分析

    在这里插入图片描述
    可以看到并不是所有的 Aware 接口都使用同样的方式调用。Bean××Aware 都是在代码中直接调用的,而 ApplicationContext 相关的 Aware 都是通过 BeanPostProcessor#postProcessBeforeInitialization() 实现的。感兴趣的可以自己看一下 ApplicationContextAwareProcessor 这个类的源码,就是判断当前创建的 Bean 是否实现了相关的 Aware 方法,如果实现了会调用回调方法将资源传递给 Bean。

    BeanPostProcessor 的调用时机也能在这里体现,包围住 invokeInitMethods 方法,也就说明了在初始化阶段的前后执行。

    关于 Aware 接口的执行顺序,其实只需要记住第一组在第二组执行之前就行了。

    3.4.2 生命周期接口

    至于剩下的两个生命周期接口就很简单了,实例化和属性赋值都是 Spring 帮助我们做的,能够自己实现的有初始化和销毁两个生命周期阶段。

    • InitializingBean 对应生命周期的初始化阶段,在上面源码的 invokeInitMethods(beanName, wrappedBean, mbd);方法中调用。
      有一点需要注意,因为 Aware 方法都是执行在初始化方法之前,所以可以在初始化方法中放心大胆的使用 Aware 接口获取的资源,这也是我们自定义扩展 Spring 的常用方式。
      除了实现 InitializingBean 接口之外还能通过注解或者 xml 配置的方式指定初始化方法,至于这几种定义方式的调用顺序其实没有必要记。因为这几个方法对应的都是同一个生命周期,只是实现方式不同,我们一般只采用其中一种方式。
    • DisposableBean 类似于 InitializingBean,对应生命周期的销毁阶段,以ConfigurableApplicationContext#close()方法作为入口,实现是通过循环取所有实现了 DisposableBean 接口的 Bean 然后调用其 destroy() 方法,感兴趣的可以自行跟一下源码。

    3.5 Spring Bean 生命周期流程图

    在这里插入图片描述

    四、常用接口说明

    4.1 BeanNameAware

    该接口只有一个方法 setBeanName(String name),用来获取 bean 的 id 或者 name

    4.2 BeanFactoryAware

    该接口只有一个方法 setBeanFactory(BeanFactory beanFactory),用来获取当前环境中的 BeanFactory

    4.3 ApplicationContextAware

    该接口只有一个方法 setApplicationContext(ApplicationContext applicationContext),用来获取当前环境中的 ApplicationContext

    4.4 InitializingBean

    该接口只有一个方法 afterPropertiesSet(),在属性注入完成后调用

    4.5 DisposableBean

    该接口只有一个方法 destroy(),在容器销毁的时候调用,在用户指定的 destroy-method 之前调用

    4.6 BeanPostProcessor

    该接口有两个方法:

    • postProcessBeforeInitialization(Object bean, String beanName):在初始化之前调用此方法
    • postProcessAfterInitialization(Object bean, String beanName):在初始化之后调用此方法

    通过方法签名我们可以知道,我们可以通过 beanName 来筛选出我们需要进行个性化定制的 bean。

    4.7 InstantiationAwareBeanPostProcessor

    该类是 BeanPostProcessor 的子接口,常用的有如下三个方法:

    • postProcessBeforeInstantiation(Class beanClass, String beanName):在bean实例化之前调用
    • postProcessProperties(PropertyValues pvs, Object bean, String beanName):在bean实例化之后、设置属性前调用
    • postProcessAfterInstantiation(Class beanClass, String beanName):在bean实例化之后调用

    五、代码演示

    思路:创建一个类 UserBean ,让其实现几个特殊的接口,并分别在接口实现的构造器、接口方法中断点,观察线程调用栈,分析出 Bean 对象创建和管理关键点的触发时机。

    5.1 UserBean 类

    @Component
    public class UserBean implements InitializingBean, BeanNameAware, DisposableBean, ApplicationContextAware {
    	private int id;
    
    	private String name;
    
    	public UserBean(int id, String name) {
    		this.id = id;
    		this.name = name;
    		System.out.println("2. 调用构造函数");
    	}
    
    	public int getId() {
    		return id;
    	}
    
    	public void setId(int id) {
    		this.id = id;
    		System.out.println("5. 属性注入 id");
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    		System.out.println("5. 属性注入 name");
    	}
    
    	@Override
    	public void setBeanName(String name) {
    		System.out.println("6. 调用 BeanNameAware.setBeanName() 方法");
    	}
    
    	@Override
    	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    		UserBean userBean = (UserBean) applicationContext.getBean("userBean");
    		System.out.println(userBean);
    		System.out.println("7. 调用 BeanNameAware.setBeanName() 方法");
    	}
    
    	@Override
    	public void afterPropertiesSet() throws Exception {
    		System.out.println("9. 调用 InitializingBean.afterPropertiesSet() 方法");
    	}
    
    	public void myInit() {
    		System.out.println("10. 调用 init-method 方法");
    	}
    
    	@Override
    	public void destroy() throws Exception {
    		System.out.println("12. 调用 DisposableBean.destroy() 方法");
    	}
    
    	public void myDestroy() {
    		System.out.println("13. 调用 destroy-method 方法");
    	}
    
    	@Override
    	public String toString() {
    		return "UserBean{" +
    				"id=" + id +
    				", name='" + name + '\'' +
    				'}';
    	}
    }
    

    5.2 InstantiationAwareBeanPostProcessor 接口实现类

    @Component
    public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
    	@Override
    	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
    		if ("userBean".equals(beanName)) {
    			System.out.println("1. 调用 InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation() 方法");
    		}
    		return null;
    	}
    
    	@Override
    	public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
    		if ("userBean".equals(beanName)) {
    			UserBean userBean = (UserBean) bean;
    			System.out.println("3. 调用 InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation() 方法");
    			System.out.println(userBean);
    		}
    		return true;
    	}
    
    	@Override
    	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
    		if ("userBean".equals(beanName)) {
    			System.out.println("4. 调用 InstantiationAwareBeanPostProcessor.postProcessProperties() 方法");
    		}
    		return null;
    	}
    }
    

    5.3 BeanPostProcessor 接口实现类

    @Component
    public class MyBeanPostProcessor implements BeanPostProcessor {
    	@Override
    	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    		if ("userBean".equals(beanName)) {
    			System.out.println("8. 调用 BeanPostProcessor.postProcessBeforeInitialization() 方法");
    		}
    		return bean;
    	}
    
    	@Override
    	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    		if ("userBean".equals(beanName)) {
    			System.out.println("11. 调用 BeanPostProcessor.postProcessAfterInitialization() 方法");
    		}
    		return bean;
    	}
    }
    

    5.4 BeanFactoryPostProcessor 接口实现类

    @Component
    public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    	@Override
    	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    		System.out.println("0. 调用 BeanFactoryPostProcessor.postProcessBeanFactory() 方法");
    	}
    }
    

    5.5 applicationContext.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	   xsi:schemaLocation="
    	    http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
    ">
    
    	<bean class="com.riemann.test.MyInstantiationAwareBeanPostProcessor" />
    
    	<bean id="userBean" class="com.riemann.test.UserBean" init-method="myInit" destroy-method="myDestroy">
    		<!-- 构造函数注入 -->
    		<constructor-arg index="0" type="int">
    			<value>1</value>
    		</constructor-arg>
    		<constructor-arg index="1" type="java.lang.String">
    			<value>微信公众号【老周聊架构】</value>
    		</constructor-arg>
    
    		<!-- setter方法注入 -->
    		<property name="id" value="2"/>
    		<property name="name" value="riemann"/>
    	</bean>
    
    	<bean class="com.riemann.test.MyBeanPostProcessor" />
    
    	<bean class="com.riemann.test.MyBeanFactoryPostProcessor" />
    	
    </beans>
    

    5.6 测试类

    public class BeanLifeCycleTest {
    	public static void main(String[] args) {
    		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
    		UserBean user = (UserBean) applicationContext.getBean("userBean");
    		((AbstractApplicationContext) applicationContext).close();
    	}
    }
    

    5.7 控制台结果打印

    在这里插入图片描述

    展开全文
  • 详解Spring的Bean销毁

    千次阅读 2021-10-13 17:21:27
    protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { …… registerDisposableBeanIfNecessary(beanName, bean, mbd); ……

    一、注册Bean销毁逻辑

    Bean的生命周期中,在完成了Bean的创建之后,会注册Bean销毁的逻辑

    protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
        throws BeanCreationException {
        ……
        registerDisposableBeanIfNecessary(beanName, bean, mbd);
        ……
    }
    

    在注册Bean销毁逻辑时,首先需要通过!mbd.isPrototype()保证该Bean不是原型Bean,因为Spring容器并不会缓存原型Bean,就没有销毁一说

    接着通过requiresDestruction()判断该Bean是否需要销毁,对需要销毁的Bean通过适配器模式生成DisposableBeanAdapter对象,最后调用registerDisposableBean()将DisposableBeanAdapter对象放入disposableBeans缓存中,当Spring容器关闭的时候,可以直接从该缓存中取出销毁调用,调用它们的销毁方法

    	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, getBeanPostProcessorCache().destructionAware, acc));
    			}
                ……
    		}
    	}
    

    1.1 判断当前Bean是否需要销毁

    requiresDestruction()方法源码如下:

    protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) {
        return (bean.getClass() != NullBean.class && (DisposableBeanAdapter.hasDestroyMethod(bean, mbd) ||
                                                      (hasDestructionAwareBeanPostProcessors() && DisposableBeanAdapter.hasApplicableProcessors(
                                                          bean, getBeanPostProcessorCache().destructionAware))));
    }
    
    • 判断当前Bean是否有销毁方法

      如果当前Bean继承了DisposableBean或AutoCloseable接口,重写接口中的destroy()和close()方法,这两个方法都是销毁方法

      public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefinition) {
          if (bean instanceof DisposableBean || bean instanceof AutoCloseable) {
              return true;
          }
          return inferDestroyMethodIfNecessary(bean, beanDefinition) != null;
      }
      

      如果没有继承这两个接口,则判断当前Bean的RootBeanDefinition是否设置销毁方法名为"(inferred)"

      设置初始化和销毁方法名都是在Bean实例化后BeanDefinition的后置处理,即MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition()中完成的

      如果在BeanDefinition的后置处理中设置销毁方法名为"(inferred)",则会将该Bean中close()和shutdown()作为销毁方法(前提是Bean里面有这两个方法)

      private static String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) {
          String destroyMethodName = beanDefinition.resolvedDestroyMethodName;
          if (destroyMethodName == null) {
              destroyMethodName = beanDefinition.getDestroyMethodName(); //
              if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName)) {
                  destroyMethodName = null;
                  if (!(bean instanceof DisposableBean)) {
                      try {
                          destroyMethodName = bean.getClass().getMethod(CLOSE_METHOD_NAME).getName();
                      }
                      catch (NoSuchMethodException ex) {
                          destroyMethodName = bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName();
                      }
                  }
              }
              beanDefinition.resolvedDestroyMethodName = (destroyMethodName != null ? destroyMethodName : "");
          }
          return (StringUtils.hasLength(destroyMethodName) ? destroyMethodName : null);
      }
      
    • 判断是否有Bean实现了DestructionAwareBeanPostProcessor接口且requiresDestruction()方法返回True

      DestructionAwareBeanPostProcessor接口主要用于Bean销毁的,其中的requiresDestruction()判断Bean是否需要销毁,而postProcessBeforeDestruction()实现具体的销毁逻辑

      《创建Bean》的最后面,讲到了@PreDestroy注解,它主要用于定义销毁方法(被该注解修饰的方法都是销毁方法),而该注解的扫描的是在InitDestroyAnnotationBeanPostProcessor类中完成,InitDestroyAnnotationBeanPostProcessor实现了DestructionAwareBeanPostProcessor接口,它会缓存每个Bean以及它的父类哪些方法是被@PreDestroy修饰的

      hasDestructionAwareBeanPostProcessors()主要判断是否缓存的有实现了DestructionAwareBeanPostProcessor的Bean

      protected boolean hasDestructionAwareBeanPostProcessors() {
          return !getBeanPostProcessorCache().destructionAware.isEmpty();
      }
      

      然后调用DestructionAwareBeanPostProcessor的requiresDestruction()判断是否需要销毁

      public static boolean hasApplicableProcessors(Object bean, List<DestructionAwareBeanPostProcessor> postProcessors) {
          if (!CollectionUtils.isEmpty(postProcessors)) {
              for (DestructionAwareBeanPostProcessor processor : postProcessors) {
                  if (processor.requiresDestruction(bean)) {
                      return true;
                  }
              }
          }
          return false;
      }
      

      以InitDestroyAnnotationBeanPostProcessor.requiresDestruction(bean)为例,查看其实现过程:

      《创建Bean》中介绍@PostConstruct和@PreDestroy的时候,已经详细介绍过findLifecycleMetadata(),通过判断是否有@PreDestroy定义的销毁方法,判断当前Bean是否需要销毁

      public boolean requiresDestruction(Object bean) {
          return findLifecycleMetadata(bean.getClass()).hasDestroyMethods();
      }
      
      private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {
          if (this.lifecycleMetadataCache == null) {
              // Happens after deserialization, during destruction...
              return buildLifecycleMetadata(clazz);
          }
          LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);
          if (metadata == null) {
              synchronized (this.lifecycleMetadataCache) {
                  metadata = this.lifecycleMetadataCache.get(clazz);
                  if (metadata == null) {
                      metadata = buildLifecycleMetadata(clazz);
                      this.lifecycleMetadataCache.put(clazz, metadata);
                  }
                  return metadata;
              }
          }
          return metadata;
      }
      

    1.2 注册可以销毁的Bean

    判断完Bean是否可以销毁之后,需要注册销毁的Bean,代码如下:

    registerDisposableBean()中缓存的是DisposableBeanAdapter对象,即不论该Bean是实现了DisposableBean或AutoCloseable接口,或者是通过BeanDifinition后置处理指定了”(inferred)“销毁方法名或其他方法销毁方法, 还是通过@PreDestroy指定的销毁方法,对于各种销毁逻辑,这里都会将Bean适配成一个DisposableBeanAdapter对象

    // 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, getBeanPostProcessorCache().destructionAware, acc));
    
    public void registerDisposableBean(String beanName, DisposableBean bean) {
        synchronized (this.disposableBeans) {
            this.disposableBeans.put(beanName, bean);
        }
    }
    

    在DisposableBeanAdapter的构造方法中,会推断出构造方法,并过滤出所有实现了DestructionAwareBeanPostProcessor接口但requiresDestruction()方法返回True的Bean,销毁的时候会调用它们的postProcessBeforeDestruction()

    public DisposableBeanAdapter(Object bean, String beanName, RootBeanDefinition beanDefinition,
                                 List<DestructionAwareBeanPostProcessor> postProcessors, @Nullable AccessControlContext acc) {
        ……
        String destroyMethodName = inferDestroyMethodIfNecessary(bean, beanDefinition);
        if (destroyMethodName != null && !(this.invokeDisposableBean && "destroy".equals(destroyMethodName)) &&
            !beanDefinition.isExternallyManagedDestroyMethod(destroyMethodName)) {
            this.destroyMethodName = destroyMethodName;
            Method destroyMethod = determineDestroyMethod(destroyMethodName);
            if (destroyMethod == null) {
                ……
            }
            else {
                ……
                destroyMethod = ClassUtils.getInterfaceMethodIfPossible(destroyMethod);
            }
            this.destroyMethod = destroyMethod;
        }
        this.beanPostProcessors = filterPostProcessors(postProcessors, bean);
    }
    
    private static List<DestructionAwareBeanPostProcessor> filterPostProcessors(
        List<DestructionAwareBeanPostProcessor> processors, Object bean) {
    
        List<DestructionAwareBeanPostProcessor> filteredPostProcessors = null;
        if (!CollectionUtils.isEmpty(processors)) {
            filteredPostProcessors = new ArrayList<>(processors.size());
            for (DestructionAwareBeanPostProcessor processor : processors) {
                if (processor.requiresDestruction(bean)) {
                    filteredPostProcessors.add(processor);
                }
            }
        }
        return filteredPostProcessors;
    }
    

    在Bean销毁的时候,会调用DisposableBeanAdapter的destroy(),这个销毁方法里面会执行各种销毁逻辑

    public void destroy() {
        // 调用所有DestructionAwareBeanPostProcessor.postProcessBeforeDestruction方法
        if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
            for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
                // @PreDestroy定义的销毁方法就是在这一步执行
                processor.postProcessBeforeDestruction(this.bean, this.beanName);
            }
        }
        ……
    }
    

    二、Bean销毁过程

    在Spring容器关闭的时候,会去销毁所有的单例Bean,并不是只有注册了销毁逻辑的Bean才被销毁,注册了销毁逻辑的单例Bean在销毁之前,会调用它们注册的销毁逻辑

    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    context.close();
    

    容器的close()会调用doClose(),doClose()会调用来销毁单例Bean

    public void close() {
        synchronized (this.startupShutdownMonitor) {
            doClose();
            ……
        }
    }
    
    protected void doClose() {
        ……
        destroyBeans();
        ……
    }
    

    在destroySingletons()中会取出disposableBeans缓存中定了销毁逻辑的Bean的beanName,然后遍历进行销毁

    protected void destroyBeans() {
        getBeanFactory().destroySingletons();
    }
    
    public void destroySingletons() {
        ……
        String[] disposableBeanNames;
        synchronized (this.disposableBeans) {
            disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
        }
        for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
            destroySingleton(disposableBeanNames[i]);
        }
        ……
    }
    

    在进行销毁的时候,先从单例池等缓存中移除Bean,然后从disposableBeans移除当前DisposableBean并获取该对象,然后调用destroyBean(beanName, disposableBean)执行对象注册的销毁逻辑

    public void destroySingleton(String beanName) {
        // Remove a registered singleton of the given name, if any.
        // 先从单例池中移除掉
        removeSingleton(beanName);
    
        // Destroy the corresponding DisposableBean instance.
        DisposableBean disposableBean;
        synchronized (this.disposableBeans) {
            disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
        }
        destroyBean(beanName, disposableBean);
    }
    // 从缓存中移除Bean
    protected void removeSingleton(String beanName) {
        synchronized (this.singletonObjects) {
            this.singletonObjects.remove(beanName);
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.remove(beanName);
        }
    }
    

    在销毁当前Bean的时候,会获取依赖当前Bean的其他Bean的beanName,然后递归调用destroySingleton()方法,保证没有被任何其他Bean依赖的Bean先销毁,在进行销毁时,会先调用DisposableBean的destroy()方法,注册Bean销毁逻辑时已经讲过该方法,然后再去调用其他的销毁逻辑,其他的销毁逻辑无非就是从各种缓存中根据BeanName,清除缓存

    protected void destroyBean(String beanName, @Nullable DisposableBean bean) {
    
        // dependentBeanMap表示某bean被哪些bean依赖了
        // 所以现在要销毁某个bean时,如果这个Bean还被其他Bean依赖了,那么也得销毁其他Bean
        // Trigger destruction of dependent beans first...
        Set<String> dependencies;
        synchronized (this.dependentBeanMap) {
            // Within full synchronization in order to guarantee a disconnected Set
            dependencies = this.dependentBeanMap.remove(beanName);
        }
        if (dependencies != null) {
            if (logger.isTraceEnabled()) {
                logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);
            }
            for (String dependentBeanName : dependencies) {
                destroySingleton(dependentBeanName);
            }
        }
    
        // Actually destroy the bean now...
        if (bean != null) {
            bean.destroy();
        }
        ……
    }
    
    展开全文
  • bean的生命周期

    千次阅读 2022-05-02 20:38:07
    六、bean的生命周期 https://liayun.blog.csdn.net/article/details/110670961 Bean 的初始化和销毁; 1、@Bean注解中使用init-method属性和destroy-method属性来指定初始化方法和销毁方法。 初始化和销毁方法的...
  • @Bean注解详解

    万次阅读 2021-05-14 14:45:19
    @Bean注解详解 Bean 用法 @Bean表示方法产生一个由Spring管理的bean,属性的名称语义与Spring XML中的 标签配置的一样 public MyBean myBean() { // instantiate and configure MyBean obj return obj; } 当...
  • 【spring】Bean的Scope与自定义Scope

    千次阅读 2022-03-14 16:34:18
    Bean的作用域 在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为Bean。简单地讲,bean就是由IoC容器初始化、装配及管理的对象,除此之外,bean就与应用程序中的其他对象没有什么区别了。...
  • Spring bean生命周期详解

    千次阅读 2021-09-10 10:49:21
    Spring Bean的完整生命周期从创建Spring容器开始,直到最终Spring容器销毁Bean,这其中包含了一系列关键点。 Spring bean生命周期 四个阶段 Bean的实例化阶段 Bean的设置属性阶段 Bean的 初始化阶段 Bean的销毁...
  • Spring的Bean加载流程

    万次阅读 多人点赞 2021-02-18 02:44:40
    Spring IOC容器就像是一个生产产品的流水线上的机器,Spring创建出来的Bean就好像是流水线的终点生产出来的一个个精美绝伦的产品。既然是机器,总要先启动,Spring也不例外。因此Bean的加载流程总体上来说可以分为两...
  • Spring源码学习笔记——Bean加载

    千次阅读 多人点赞 2021-05-19 11:46:09
    @Bean —> BeanDefinition Bean加载,通过XML解析后得到的Bean 的定义信息( BeanDefinition )获取 Bean 对象实例。 BeanDefinition —> Bean对象 此外,IOC 还具有:自动装配、支持集合、指定初始化方法和销毁...
  • Springboot之Bean的加载过程

    千次阅读 2020-12-02 22:56:31
    加载和实例化bean分以下几个重要步骤: 1、根据package扫描出需要被管理的类 2、将这些类封装成BeanDefinition并注册到BeanFactory容器 3、实例化所有扫描到的BeanDefinition,其中包括解决循环依赖、延迟加载问题 ...
  • Spring的注解 @Bean用法

    千次阅读 2020-12-26 20:08:09
    随着SpringBoot的流行,基于注解式开发的热潮逐渐覆盖了基于XML纯配置的开发,而作为Spring中最核心的bean当然也能够使用注解的方式进行表示。所以本篇就来详细的讨论一下作为Spring中的Bean到底都有哪些用法。 @...
  • 较完整的 bean生命周期

    千次阅读 2021-03-11 14:47:02
    首先需要说明的是,Bean的生命周期主要指的是singleton bean,标签的scope默认就是singleton。对prototype bean来说,当用户getBean获得prototype bean的实例后,IOC容器就不再对当前实例进行管理,而是把管理权交由...
  • Spring中的普通bean和工厂bean

    千次阅读 多人点赞 2021-01-15 21:56:07
    普通bean和工厂bean普通bean工厂bean(FactoryBean) Spring中有两种类型bean,一种是普通bean,另外一种是工厂bean(FactoryBean)。 区别: 不同点 普通bean 在配置文件中定义bean的类型就是返回类型。 ...
  • Spring源码系列-第4章-Bean初始化流程

    千次阅读 2022-01-28 10:59:04
    文章目录第4章-Bean初始化流程流程图-bean初始化流程AbstractApplicationContext#refresh()AbstractApplicationContext#finishBeanFactoryInitialization()DefaultListableBeanFactory#preInstantiateSingletons()...
  • SpringBean生命周期详解

    千次阅读 多人点赞 2020-10-28 17:51:28
    SpringBean生命周期详解 一、简述: Spring是我们每天都在使用的框架,Bean是被Spring管理的Java对象,是Spring框架最重要的部分之一,那么让我们一起了解一下Spring中Bean的生命周期是怎样的吧 二、流程图 我们先从...
  • 创建Bean的入口:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean。 实例化 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#...
  • 我们都知道spring是创建和管理bean的工厂,它提供了多种定义bean的方式,能够满足我们日常工作中的多种业务场景。 那么问题来了,你知道spring中有哪些方式可以定义bean? 我估计很多人会说出以下三种: 最近...
  • spring bean创建流程

    千次阅读 2020-04-30 17:44:15
    一、流程图 getBean()流程图,来源:https://blog.csdn.net/laravelshao/article/details/82318063 其它有关图,来源:... ...scope="singleton"单例 在启动(容器初始化之前), 就已经创建了bean,且整个...
  • 【一篇搞懂】 bean的生命周期详解

    千次阅读 2021-06-20 18:33:00
    Spring中的一个Bean从生到灭要经历很多过程,总体分为Bean定义、实例化、属性赋值(依赖注入)、初始化、生存期、销毁几个阶段: ​​​​​​​​ 下面是一个细化的Bean生命周期图: 过程比较复杂,重点关注Bean的...
  • Spring 注入 Bean 的七种方式

    千次阅读 2021-09-01 00:05:40
    来源:juejin.cn/post/6844903813753602056通过注解注入Bean背景我们谈到Spring的时候一定会提到IOC容器、DI依赖注入,Spring通过将一个个类标...
  • 源码分析:Spring是如何获取容器中的Bean

    万次阅读 多人点赞 2020-10-19 23:16:25
    Spring中Bean注册过程,了解一下
  • Spring源码IOC之创建Bean的前置工作

    千次阅读 2021-09-13 00:10:13
    文章目录2.3 创建bean的实例--前置准备工作2.3.1 getSingleton2.3.1.1 beforeSingletonCreation 前置处理2.3.1.2 afterSingletonCreation 后置处理2.3.1.3 addSingleton 缓存bean实例2.3.2 createBean2.3.1 ...
  • Spring框架(2) -- Spring中的Bean

    万次阅读 2021-08-07 15:42:28
    文章目录Spring框架中的Bean1、Bean的配置2、Bean的作用域2.1、singleton作用域2.2、prototype作用域3、Bean的装配方式3.1、基于XML的装配3.1.1、构造器注入3.1.2、setter注入3.1.3、拓展注入3.2、基于Annotation的...
  • Spring Bean详细讲解 什么是Bean?

    万次阅读 多人点赞 2018-11-19 15:45:37
    什么是Bean? Spring Bean是被实例的,组装的及被Spring 容器管理的Java对象。 Spring 容器会自动完成@bean对象的实例化。 创建应用对象之间的协作关系的行为称为:装配(wiring),这就是依赖注入的本质。 Spring ...
  • @Bean注解的使用和详解

    万次阅读 多人点赞 2020-03-16 14:34:35
    @Bean:Spring的@Bean注解用于告诉方法,产生一个Bean对象,然后这个Bean对象交给Spring管理。产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中。 SpringIOC 容器...
  • 最近写项目时遇到了很多关于 Spring 注入 bean 的问题,也是耗费了我很多时间去查找注入失败的原因。因此这篇博客就来总结一下 bean 注入失败的常见情况,希望能对你有所帮助 博客主页:秋刀鱼与猫__ 如果觉得博主...
  • @Bean的使用

    万次阅读 多人点赞 2019-09-24 14:20:01
    @Bean是一个方法级别上的注解,主要用在@Configuration注解的类里,也可以用在@Component注解的类里。目的是创建一个类。当spring需要创建指定的一个类时会调用这个注解(@Bean)的方法。 一个对象 @Data @...
  • 什么是bean

    万次阅读 多人点赞 2021-06-20 10:41:25
    什么是beanbean是计算机自动生成的类,bean是一个由Spring IoC容器实例化、组装和管理的对象。也就是说,bean并不是程序员编辑的,而是程序运行时,由spring通过反射生成的。在程序中,我们并不使用代码去new一个...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,503,685
精华内容 601,474
关键字:

bean

友情链接: 蒙特卡罗.R.zip