Spring-Bean的作用域 (@EventListener, BeanPostProcessor )

本文介绍了Spring中BeanDefinition的概念,它是配置元数据的载体,包含类名、行为配置、依赖注入等信息。文章详细讲解了Bean的单例和原型作用域,通过示例展示了不同作用域下bean的生命周期。同时,提到了BeanPostProcessor后置处理器在对象初始化过程中的作用,以及如何通过@EventListener监听容器刷新事件。
摘要由CSDN通过智能技术生成

 

BeanDefinition

我们知道 java 是面向对象的编程语言,但是在 Spring 当中除了对象还有一个特别重要的概念,那就是 bean,我们的交给 Spring 管理的对象最终都会由 Spring 将其变成 bean,那么对象、bean、BeanDefinition 是什么关系呢

Spring 官网介绍

 

Spring IoC容器管理一个或多个bean。这些bean是使用您提供给容器的配置元数据创建的(例如,以XML<bean/>定义的形式 )。

在容器本身内,这些bean定义表示为BeanDefinition 对象,这些对象包含(除其他信息外)以下元数据:

  • 包限定的类名:通常,定义了Bean的实际实现类。
  • Bean行为配置元素,用于声明Bean在容器中的行为(作用域,生命周期回调等)。
  • 引用其他bean来完成其工作所需要的。这些引用也称为协作者或依赖项。
  • 要在新创建的对象中设置的其他配置设置-例如,池的大小限制或要在管理连接池的bean中使用的连接数。

此元数据转换为构成每个bean定义的一组属性。下表描述了这些属性:

Property
Explained in…

Class

Instantiating Beans

Name

Naming Beans

Scope

Bean Scopes

Constructor arguments

Dependency Injection

Properties

Dependency Injection

Autowiring mode

Autowiring Collaborators

Lazy initialization mode

Lazy-initialized Beans

Initialization method

Initialization Callbacks

Destruction method

Destruction Callbacks

 
翻译:
 
Bean元数据和手动提供的单例实例需要尽早注册,以便容器在自动装配和其他自省步骤期间正确地判断它们。
虽然在某种程度上支持覆盖现有元数据和现有的单例实例,但在运行时注册新bean(与对工厂的实时访问并发)不受官方支持,
可能会导致并发访问异常、bean容器中的不一致状态,或者两者都有。
 

Bean作用域 Scope

Table 3. Bean scopes
ScopeDescription

singleton

(Default) Scopes a single bean definition to a single object instance for each Spring IoC container.

prototype

Scopes a single bean definition to any number of object instances.

request

Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.

session

Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.

application

Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.

websocket

Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.

 
这里主要测试下单例和原形作用域:
单例:
      singleton为单例模式,即scope="singleton"的bean,在容器中,只实例化一次
 
原形:
      与其他作用域相比,Spring并不管理原型bean的完整生命周期。
容器实例化、配置和组装一个原型对象,并将其交给客户端, 不需记录该原型的实例。
因此,尽管初始化生命周期回调方法会在所有对象上调用,而不管其作用域是什么, 但在原型的情况下,配置的销毁生命周期回调不会被调用 客户机代码必须清理原型作用域的对象,并释放原型bean持有的昂贵资源。 要让Spring容器释放原型作用域bean所持有的资源,请尝试使用自定义bean后处理器,它持有对需要清理的bean的引用。
 

代码证明:

注解:

package com.davinqi.mytest.spring;
import java.lang.annotation.*;

/**
 * @Author qixi....
 * @Date 2021/4/22 2:19 下午
 * @Version 1.0
 **/
@Target({ElementType.FIELD})  
@Retention(RetentionPolicy.RUNTIME) 
@Documented  
public @interface MyBeanAnno {
    String value() default "";
}

测试Bean 作用域的类。

package com.davinqi.mytest.spring;

import org.springframework.context.annotation.Scope;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

import java.lang.ref.PhantomReference;

/**
 * @Author qixi....
 * @Date 2021/4/22 2:11 下午
 * @Version 1.0
 **/
@Component
public class MyBeanPostVo {

    public MyBeanPostVo() {
        System.out.println(">>>>>>>>> 我要new个对象");
    }
    
    
    @EventListener(classes = {ContextRefreshedEvent.class})
    public void testEvent(ContextRefreshedEvent event){
        System.out.println("======================  容器完成刷新,可以工作了  done---->"+event.toString());
    }
    
    @MyBeanAnno(value = " 哎, 该回家了!")
    private  String  nnnnn = " 欧耶,出去玩 !";
   
    public void sayHi () {
        System.out.println(">>>>>>>>> 我是 sayHi : " + nnnnn);
    }
}
 
 
Bean的后置处理器
package com.davinqi.mytest.spring;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;

/**
 * @Author qixi....
 * @Date 2021/4/22 2:10 下午
 * @Version 1.0
 **/
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {


    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MyBeanPostVo) {
            System.out.println(">>>>>>>>  封装数据 ... ");
            Field[] declaredFields = bean.getClass().getDeclaredFields();
            for (Field field : declaredFields) {
                MyBeanAnno annotation = field.getAnnotation(MyBeanAnno.class);
                if (annotation != null) {
                    String value = annotation.value();
                    field.setAccessible(true);
                    try {
                        field.set(bean, value);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        return bean;
    }
}

 

注意在:
@EventListener(classes = {ContextRefreshedEvent.class})
public void testEvent(ContextRefreshedEvent event){
    System.out.println("======================  容器完成刷新,可以工作了  done---->"+event.toString());
}

 

这块代码是用来监听Spring 容器启动后 调用refresh()后放容器中发布的一个完成的事件 我们使用注解声明这个事件后就可以监听是否刷新完成了。。。

 

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

            try {
                this.postProcessBeanFactory(beanFactory);
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }
 protected void finishRefresh() {
        this.clearResourceCaches();
        this.initLifecycleProcessor();
        this.getLifecycleProcessor().onRefresh();
        this.publishEvent((ApplicationEvent)(new ContextRefreshedEvent(this)));
        LiveBeansView.registerApplicationContext(this);
    }

 

 
 
运行上述代码打印的日志为:
2021-04-22 15:05:02.681  INFO 92914 --- [           main] com.davinqi.mytest.HelloBoyTest          : Starting HelloBoyTest using Java 11.0.10 on QXQ.local with PID 92914 (started by qixiangqun in /Users/qixiangqun/mycode/mytest)
2021-04-22 15:05:02.683  INFO 92914 --- [           main] com.davinqi.mytest.HelloBoyTest          : No active profile set, falling back to default profiles: default
>>>>>>>>> 我要new个对象
>>>>>>>>  封装数据 ... 
======================  容器完成刷新,可以工作了  done---->org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@666b83a4, started on Thu Apr 22 15:05:02 CST 2021]
2021-04-22 15:05:03.252  INFO 92914 --- [           main] com.davinqi.mytest.HelloBoyTest          : Started HelloBoyTest in 0.989 seconds (JVM running for 2.319)
>>>>>>>> 使用 helloBoy ... 
>>>>>>>>> 我是 sayHi :  哎, 该回家了!
@Component
public class MyBeanPostVo 

 可以知道 这个对象,默认的情况下是个单例的Bean 。通过日志可以知道,在容器刷新前,已经new好了 并且 Bean后置处理器已经重新赋值( 这块其实是:给BeanDefiniton 的 属性赋值)

 

 

现在改为:

@Component
@Scope("prototype")
public class MyBeanPostVo 

 

2021-04-22 15:10:51.103  INFO 93089 --- [           main] com.davinqi.mytest.HelloBoyTest          : No active profile set, falling back to default profiles: default
>>>>>>>>> 我要new个对象
>>>>>>>>  封装数据 ... 
======================  容器完成刷新,可以工作了  done---->org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@666b83a4, started on Thu Apr 22 15:10:51 CST 2021]
2021-04-22 15:10:51.755  INFO 93089 --- [           main] com.davinqi.mytest.HelloBoyTest          : Started HelloBoyTest in 1.171 seconds (JVM running for 2.276)
>>>>>>>>> 我要new个对象
>>>>>>>>  封装数据 ... 
>>>>>>>> 使用 helloBoy ... 
>>>>>>>>> 我是 sayHi :  哎, 该回家了!
Disconnected from the target VM, address: '127.0.0.1:51129', transport: 'socket'

 

我们发现后在容器启动刷新后又再次New了新的Bean。 
 
 
 
 
 
 

 

总结:

又上述代码中可知道

1,bean 的作用域默认为 单例 ,在容器其初始化时会 (refresh()  )创建对象放入缓存集合中。 声明了prototype,后在每次使用时生成新的Bean。

2,@EventListener(classes = {ContextRefreshedEvent.class}) 注解声明监听器的方式的使用 ,在Spring中的观察者模式。

3,BeanPostProcessor  后置处理器接口可以对对象进行自定义的处理,以及在实际的应用。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值