IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 【探索Spring底层】6.SpringBean初始化销毁与Scope -> 正文阅读

[Java知识库]【探索Spring底层】6.SpringBean初始化销毁与Scope

【探索Spring底层】SpringBean初始化销毁与Scope

1. Spring Bean初始化与销毁

Spring Bean的初始化方法有三种

  • 通过@PostConstruct注解标注在这个Bean内方法上

  • 实现InitializingBean接口,重写afterPropertiesSet方法

  • 利用@Bean注解指定该Bean中的某个方法为初始化方法

    @Bean(initMethod = "init3")
    
public class Bean1 implements InitializingBean {
    private static final Logger log = LoggerFactory.getLogger(Bean1.class);

    @PostConstruct
    public void init1() {
        log.debug("初始化1");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        log.debug("初始化2");
    }

    public void init3() {
        log.debug("初始化3");
    }
}

Spring Bean销毁的方法对应也是有三种

  • 通过@PreDestroy标注Bean中某个方法作为销毁方法

  • 实现DisposableBean接口,并重写destroy方法作为销毁方法

  • 通过注入Bean时的@Bean注解的destroyMethod属性指明销毁方法

    @Bean(destroyMethod = "destroy3")
    public Bean2 bean2() {
        return new Bean2();
    }
    
public class Bean2 implements DisposableBean {
    private static final Logger log = LoggerFactory.getLogger(Bean2.class);

    @PreDestroy
    public void destroy1() {
        log.debug("销毁1");
    }

    @Override
    public void destroy() throws Exception {
        log.debug("销毁2");
    }

    public void destroy3() {
        log.debug("销毁3");
    }
}

既然有三种方法初始化和销毁,那么它们肯定有顺序

在这里插入图片描述

在这里插入图片描述

对于初始化来说,三种方法执行顺序如下:

  1. @PostConstruct
  2. 实现InitializingBean接口
  3. @Bean(initMethod = “init3”)

而对于销毁来说,三种方法的执行顺序如下:

  1. @PreDestroy
  2. 实现DisposableBean接口
  3. @Bean(destroyMethod = “destroy3”)

2. Scope的类型

Scope是Spring中的一个关键属性,其指定了Spring中的Bean的生命周期,也可以理解为Spring容器的创建方法。

一般来说我们使用得最多得是singleton,也就是单例模式,这个也是Spring中Bean的默认Scope

Scope有五种:

  1. singleton:容器启动时创建(未设置延迟),容器关闭时销毁
  2. prototype,每次使用时创建,不会自动销毁,需要调用 DefaultListableBeanFactory.destroyBean(bean) 销毁
  3. request,每次请求用到此 bean 时创建,请求结束时销毁
  4. session,每个会话用到此 bean 时创建,会话结束时销毁
  5. application,web 容器用到此 bean 时创建,容器停止时销毁

3. Scope失效问题及其解决

模拟场景出现

@ComponentScan("com.itheima.a08.sub")
public class A08_1 {

    private static final Logger log = LoggerFactory.getLogger(A08_1.class);

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(A08_1.class);
                
        E e = context.getBean(E.class);
        log.debug("{}", e.getF1().getClass());
        log.debug("{}", e.getF1());
        log.debug("{}", e.getF1());
        log.debug("{}", e.getF1());

        context.close();
    }
@Component
public class E {

    @Autowired
    private F1 f1;

    public F1 getF1() {
        return f1;
    }
}
@Scope("prototype")
@Component
public class F1 {
}

运行上面的程序,会发现打印的bean均为同一个,但是这个设置F1的Scope为prototype

在这里插入图片描述

这个F1希望的是多例,但是输出的确实同一个,也就是单例

这是因为它们是同一个对象,而不是期望的多例对象

对于单例对象来讲,依赖注入仅发生了一次,后续再没有用到多例的 F,因此 E 用的始终是第一次依赖注入的 F

e 创建
e set 注入 f
f 创建

想要解决这个也很简单,一共有四种方法解决


3.1 @Lazy

第一种则是使用需要使用一个@Lazy注解来生成代理

使用该注解代理之后,代理对象虽然还是同一个,但是使用代理对象的任意方法时候,由代理创建新的f对象

使用f方法
使用f方法
使用f方法
e 创建
e set 注入 f代理
f 创建
f 创建
f 创建
@Component
public class E {

    @Autowired
    @Lazy
    private F1 f1;

    public F1 getF1() {
        return f1;
    }
}

注意

  • @Lazy 也可以加在成员变量上,但加在 set 方法上的目的是可以观察输出,加在成员变量上就不行了
  • @Autowired 加在 set 方法的目的类似
@Component
public class E {

    @Autowired
    @Lazy
    public void setF(F f) {
        this.f = f;
        log.info("setF(F f) {}", f.getClass());
    }

    // ...
}

3.2 proxyMode

在使用@Scope定义F1时候,直接在 @Scope 注解加上proxyMode = ScopedProxyMode.TARGET_CLASS(其实底层本质也是自定义 TargetSource)

@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class F2 {
}

3.3 ObjectFactory

ObjectFactory是一个普通的对象工厂接口,其是一个函数式接口

@FunctionalInterface
public interface ObjectFactory<T> {

   /**
    * Return an instance (possibly shared or independent)
    * of the object managed by this factory.
    * @return the resulting instance
    * @throws BeansException in case of creation errors
    */
   T getObject() throws BeansException;

}

里面的getObject是可以从对应域中获取指定的对象

@Component
public class E {

    @Autowired
    private ObjectFactory<F1> f1;

    public F1 getF1() {
        return f1.getObject();
    }

}

3.4 ApplicationContext

最后一种就是最常见的ApplicationContext,因为F1已经注册在Spring容器中了,只需要调用ApplicationContextg的getBean方法就可以获取到多例子

@Component
public class E {

    @Autowired
    private ApplicationContext context;

    public F1 getF1() {
        return context.getBean(F1.class);
    }
}

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-09-25 23:07:17  更:2022-09-25 23:08:33 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 21:07:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码