Spring:Bean管理(二)

一、Bean的作用域

我们可以在 元素中添加 scope 属性来配置 Spring Bean 的作用范围

Spring 5 共提供了 6 种 scope 作用域,如下表。

作用域 (Scope) 描述 创建时机 适用场景
singleton 默认值 ,在整个 Spring IOC 容器中,该 Bean 只有一个实例。(单例模式) 容器启动时创建 无状态的 Bean,如 Service、DAO、工具类
prototype 每次从容器中获取 Bean (getBean()) 时,都会创建一个新的实例。(原型模式) 每次调用 getBean() 时创建 有状态的 Bean,如购物车、用户会话对象
request 每个 HTTP 请求创建一个新的 Bean 实例,仅在 Web 应用中有效。 收到 HTTP 请求时创建 Web 应用中,每个请求独立的 Bean
session 每个 HTTP Session 创建一个新的 Bean 实例,仅在 Web 应用中有效。 用户首次访问创建 Session 时创建 Web 应用中,与用户会话绑定的 Bean
application 在整个 Web 应用的生命周期内,只有一个 Bean 实例。 Web 应用启动时创建 与整个 Web 应用共享的 Bean
websocket 在一个 WebSocket 的生命周期内,创建一个 Bean 实例。 WebSocket 连接建立时创建 WebSocket 应用

其中最常用的是 singletonprototype

1.1 singleton

singleton 是 Spring 容器默认的作用域。当 Bean 的作用域为 singleton 时,Spring IOC 容器中只会存在一个共享的 Bean 实例。

1.2 prototype

如果一个 Bean 定义的作用域为 prototype,那么这个 Bean 就被称为 prototype bean。对于

prototype bean 来说,Spring 容器会在每次请求该 Bean 时,都创建一个新的 Bean 实例。

在 Spring 配置文件中,可以使用 元素的 scope 属性将 Bean 的作用域定义成 prototype,其

配置方式如下所示:

二、Bean的生命周期(面试当中一定会有)

2.1 Spring Bean 的完整生命周期

在传统的 Java 应用中,Bean 的生命周期很简单,使用 Java 关键字 new 进行 Bean 的实例化后,这个Bean 就可以使用了。一旦这个 Bean 长期不被使用,Java 自动进行垃圾回收。

相比之下,Spring 中 Bean 的生命周期较复杂,Spring IOC容器可以管理Bean的生命周期,Spring允许在Bean生命周期的特定点执行定制的任务,大致可以分为以下 阶段:

1、bean对象创建:IOC容器在初始化时,通过反射机制调用无参构造器创建bean的实例;

2、给bean对象设置属性:通过反射机制调用setter方法实现属性注入;

3、bean对象初始化之前操作:由 bean 的后置处理器负责;

4、bean对象初始化:需在配置 bean 时指定初始化方法;

5、bean对象初始化之后操作:由 bean 的后置处理器负责

6、bean对象就绪可以使用

7、bean 对象销毁:需在配置 bean 时指定销毁方法

8、IOC 容器关闭

代码演示:

Dog.java:

复制代码
public class Dog {

    private String name;

    private String owner;

    private int age;

    public Dog() {

    	System.out.println("Dog对象被创建...");

    }

    public String getName() {

    	return name;

    }

    public void setName(String name) {

    	System.out.println("调用setName方法....");

    	this.name = name;

    }

    public String getOwner() {

    	return owner;

    }
    public void setOwner(String owner) {

    	System.out.println("调用setOwner方法....");

    	this.owner = owner;

    }

    public int getAge() {

    	return age;

    }

    public void setAge(int age) {

    	System.out.println("调用setAge方法....");

    	this.age = age;

    }

    @Override

    public String toString() {

    	return "Dog{" +

    		"name='" + name + '\'' +

    		", owner='" + owner + '\'' +

    		", age=" + age +

    	'}';

    }
}

applicationContext.xml:

复制代码
<bean id="dog" class="com.gs.bean.Dog" p:name="旺财" p:owner="小明" p:age="5"/>

测试:

复制代码
public class LifeCycleTest {

    @Test

    public void testLifeCycle() {

    	System.out.println("-------容器初始化阶段---------");

    	ClassPathXmlApplicationContext ac = new

    	ClassPathXmlApplicationContext("applicationContext.xml");

    	System.out.println("-------对象使用阶段---------");

    	Dog dog = ac.getBean("dog", Dog.class);

    	System.out.println(dog);

    	System.out.println("-------容器关闭阶段---------");

    	//手动关闭容器

    	ac.close();//ApplicationContext没有close()

    }

}

2.2 如何自定义初始化和销毁方法

我们可以在 Spring Bean 生命周期的某个特定时刻,指定一些生命周期回调方法完成一些自定义的操作,对 Bean 的生命周期进行管理。

方式一:通过接口实现,在 Spring Bean 的 Java 类中,通过实现 InitializingBean 和 DisposableBean 接口,指定 Bean 的生命周期回调方法。

方式二:通过 XML 配置实现,在 Spring 的 XML 配置中,通过 `` 元素中的 init-method 和 destory-method 属性,指定 Bean 的生命周期回调方法。

方式三:使用注解实现。

**注意:**如果一个 Bean 中有多种生命周期回调方法时,优先级顺序为:注解 > 接口 > XML 配置。

2.2.1 通过接口实现

我们可以在 Spring Bean 的 Java 类中,通过实现 InitializingBean 和 DisposableBean 接口,指定Bean 的生命周期回调方法。

注意:通常情况下,我们不建议通过这种方式指定生命周期回调方法,这是由于这种方式会导致代码的耦合性过高。

复制代码
public class Dog implements InitializingBean, DisposableBean{

    private String name;

    private String owner;

    private int age;

    public Dog() {

    	System.out.println("Dog对象被创建...");

    }

    public String getName() {

    	return name;

    }

    public void setName(String name) {

    	System.out.println("调用setName方法....");

    	this.name = name;

    }

    public String getOwner() {

    	return owner;

    }
    public void setOwner(String owner) {

    	System.out.println("调用setOwner方法....");

    	this.owner = owner;

    }

    public int getAge() {

    	return age;

    }

    public void setAge(int age) {

    	System.out.println("调用setAge方法....");

    	this.age = age;

    }

    @Override

    public String toString() {

    	return "Dog{" +

    		"name='" + name + '\'' +

    		", owner='" + owner + '\'' +

    		", age=" + age +

    	'}';

    }
    /**

    * 初始化回调逻辑

    *

    * @throws Exception

    */

    @Override

    public void afterPropertiesSet() throws Exception {

    	System.out.println("调用接口:InitializingBean,方法:afterPropertiesSet,无参数");

    }

    /**

    * 销毁回调逻辑

    *

    * @throws Exception

    */

    @Override

    public void destroy() throws Exception {

    	System.out.println("调用接口:DisposableBean,方法:destroy,无参数");

    }
}

2.2.2 通过 XML 配置实现

我们还可以在 Spring 的 XML 配置中,通过 元素中的 init-method 和 destory-method 属性,指定 Bean 的生命周期回调方法。

复制代码
public class Dog {

    private String name;

    private String owner;

    private int age;

    public Dog() {

    	System.out.println("Dog对象被创建...");

    }
    //......

}

applicationContext.xml:

复制代码
<bean id="dog" class="com.qcby.Dog" p:name="admin" p:owner="aaa"
      p:age="18" init-method="大阿萨大师"
                 destroy-method="就打卡大事件"/>

2.2.3 使用 JSR-250 注解实现

使用 @PostConstruct@PreDestroy 注解是现代 Spring 开发中最推荐的方式,它既解耦又简洁。

Java 类:

复制代码
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public class Dog {
    // ... 属性和构造函数 ...

    @PostConstruct
    public void init() {
        System.out.println("调用 @PostConstruct 注解的初始化方法");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("调用 @PreDestroy 注解的销毁方法");
    }
}

XML 配置:

复制代码
<!-- 需要开启注解支持 -->
<context:annotation-config/>

2.3 Bean的后置处理器

创建后置处理器类:

复制代码
// 声明一个自定义的bean后置处理器

// 注意:bean后置处理器不是单独针对某一个bean生效,而是针对IOC容器中所有bean都会执行

public class MyBeanProcessor implements BeanPostProcessor {

    @Override

    public Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException {

    	System.out.println("☆☆☆" + beanName + " = " + bean);

    	return bean;

    }

    @Override

    public Object postProcessAfterInitialization(Object bean, String beanName)throws BeansException {

    	System.out.println("★★★" + beanName + " = " + bean);

    	return bean;

    }

}

把 bean 的后置处理器放入 IOC 容器:

复制代码
<!-- bean的后置处理器要放入IOC容器才能生效 -->

<bean id="myBeanProcessor" class="com.gs.process.MyBeanProcessor"/>

三、LomLock

既然 javaBean 中 getXxx ()、setXxx () 方法、toString () 方法、构造器都是按照固定格式生成的,那能否由程序自动生成呢?

  • 使用 Lombok 注解就可以省略生成 getXxx ()、setXxx () 方法、toString () 方法、构造器等固定格式代码的繁琐操作,提高开发效率。

Lombok 是一个在 java 开发过程中用注解的方式,简化了 JavaBean 的编写,避免了冗余和样板式代码而出现的插件,让编写的类更加简洁。

3.1 Lombok 原理:

Lombok 是将自动生成的代码织入字节码文件中,从而实现:源代码没有,但是字节码文件有 ------ 毕竟我们最终运行的是字节码文件,只要字节码文件中有即可。而这个过程因为要参与源文件编译,所以需要安装 IDEA 插件。

在 IDEA 安装 Lombok 插件:

File → Settings → Plugins → Browse repositories → 搜索 lombok

加入依赖:

复制代码
<dependency>

<groupId>org.projectlombok</groupId>

<artifactId>lombok</artifactId>

<version>1.18.12</version>

<scope>provided</scope>

</dependency>

3.2 使用 Lombok 注解:

@Getter/@Setter:自动生成 getter/setter;

@ToString:自动重写 toString () 方法,会印出所有变量;

@EqualsAndHashCode:自动生成 equals (Object other) 和 hashCode () 方法,包括所有非静态变量和非 transient 的变量;

@NoArgsConstructor,@AllArgsConstructor,@RequiredArgsConstructor:这三个很像,都是在自动生成该类的构造器,差别只在生成的构造器的参数不一样而已;

@NoArgsConstructor:生成一个没有参数的构造器

@AllArgsConstructor:生成一个包含所有参数的构造器

@RequiredArgsConstructor:生成一个包含 "特定参数" 的构造器,特定参数指的是那些有加上 final 修饰词的变量;补充一下,如果所有的变量都是正常的,都没有用 final 修饰的话,那就会生成一个没有参数的构造器

@Data:整合包,只要加了 @Data 这个注解,等同于同时加了以下注解 @Getter/@Setter、@ToString、@EqualsAndHashCode、@RequiredArgsConstructor;

相关推荐
xxxxxxllllllshi2 小时前
深入解析单例模式:从原理到实战,掌握Java面试高频考点
java·开发语言·单例模式·面试
Miss_Chenzr2 小时前
Springboot快递信息管理52c05本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
java·数据库·spring boot
千寻技术帮2 小时前
基于SpringBoot的仿知乎知识问答系统
java·spring boot·毕业设计·论坛·文答
醉卧考场君莫笑2 小时前
数据分析理论基础
java·数据库·数据分析
廋到被风吹走2 小时前
【Java】【Jdk】Jdk11->Jdk17
java·开发语言·jvm
南昌彭于晏2 小时前
解决springboot静态内部类非空校验无效的问题
java·spring boot·后端
czlczl200209252 小时前
MybatisPlusInterceptor实现无感修改SQL的底层原理(源码)
数据库·spring boot·后端·sql
.鸣2 小时前
CSDN Java反射
java·学习
cookqq2 小时前
踩坑记:MySQL 连接 URL 缺失useCursorFetch参数引发的 Java 内存溢出惨案
java·mysql