Spring中的Bean

Spring中的Bean

目录:

一、Bean的配置

二、Bean的实例化

三、Bean的作用域

四、Bean的装配方式

一、Bean的配置
1.IoC 容器

​ Spring容器会负责控制程序之间的关系,而不是由程序代码直接控制,这样控制权由应用代码转移到了外部容器,控制权发生了反转,也就是Spring的IoC(Inversion of Control, IoC)思想。Spring为我们提供了两种IoC容器,分别为BeanFactory和ApplicationContext。如果把Spring看做一个大型的工厂,而Spring容器中的Bean就是工厂的产品。要想使用工厂生产和管理的Bean,就需要在配置文件中告诉它容器中有那些Bean,接下来我们将介绍如何将这些Bean放入到容器中。

Spring 容器的核心:用于消减代码耦合问题。

2.Spring 配置文件

Spring 容器支持两种格式的 配置文件Properties 文件XML 文件

在实际开发中,最常使用的是XML文件格式的配置方式,这种配置方式是通过XML文件来注册并管理Bean之间的依赖关系。

XML配置文件的根元素是中包含了多个子元素,每一个子元素定义了一个Bean,并描述了该Bean如何被装配到Spring容器中。定义Bean的方式如下所示:

复制代码
<?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">
	<!-- 使用id属性定义bean1,其对应的实现类为cn.itcast.bean1 -->
	<bean id="bean1" class="edu.cqie.ssm.bean1"/>
	<!-- 使用name属性定义bean2,其对应的实现类为cn.itcast.bean2 -->
	<bean name="bean2" class="edu.cqie.ssm.bean2"/>
</beans>

注意:通常一个普通的Bean只需要定义id(或name)和class 两个属性即可,如果在Bean中未指定id和name,则Spring会将class值当作id使用。

元素,常用属性包含以下这些属性,具体如下表所示:

二、Bean的实例化
1.Spring 容器实例化 Bean

在面向对象的程序中,要想使用某个对象,就需要先实例化这个对象。在Spring中,常见的实例化Bean有三种方式,分别是构造器实例化、静态工厂方式实例化和实例工厂方式实例化,接下来对这几种方式进行详细介绍:

(1) 构造器方式

构造器实例化是指Spring容器通过Bean对应的类中默认的构造函数来实例化Bean,这种方式也是最常用的。

案例:

复制代码
package com.example;

public class User {
    private String name;
    private int age;

    // 默认无参构造函数
    public User() {
    }

    // 带参数的构造函数
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Getter和Setter方法
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

<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.xsd">

    <!-- 使用默认构造函数创建User Bean -->
    <bean id="user" class="com.example.User">
        <!-- 使用属性注入设置name和age -->
        <property name="name" value="张三"/>
        <property name="age" value="30"/>
    </bean>

</beans>

在这个XML配置中:

标签定义了一个id为user的Bean,class属性指定了Bean的全限定类名(这里假设为com.example.User)。

由于User类有一个默认的无参构造函数,Spring容器可以使用这个构造函数来实例化User对象。

标签用于注入Bean的属性。这里我们设置了name和age属性的值。

当Spring容器加载这个配置文件时,它会使用User类的默认无参构造函数来创建一个User对象的实例,然后通过setter方法注入name和age属性的值。这种方式简单且常用,因为它允许在不修改类代码的情况下灵活地配置Bean的属性。

(2) 静态工厂方式

使用静态工厂是实例化Bean的另一种方式。该方式要求自己创建一个静态工厂的方法来创建Bean的实例。

案例:

复制代码
package com.example;

public class Car {
    private String brand;

    private Car(String brand) {
        this.brand = brand;
    }

    public String getBrand() {
        return brand;
    }

    // 静态工厂方法
    public static Car getCar(String brand) {
        return new Car(brand);
    }
}

<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.xsd">

    <!-- 使用静态工厂方法创建Car Bean -->
    <bean id="myCar" class="com.example.Car">
        <constructor-arg value="宝马"/>
    </bean>

    <!-- 或者使用factory-method方式 -->
    <bean id="myCar" class="com.example.Car" factory-method="getCar">
        <constructor-arg value="宝马"/>
    </bean>

</beans>

在这个XML配置中:

第一个标签尝试使用Car类的构造函数来创建Bean,但由于Car类的构造函数是私有的,这种方式会失败。

第二个标签使用factory-method属性指定了静态工厂方法getCar,constructor-arg标签提供了静态工厂方法需要的参数。这种方式可以成功创建Car的实例。

静态工厂方法实例化提供了一种灵活的方式来创建Bean,特别是当你需要根据参数动态决定返回哪个类的实例时。这种方式也有助于保持代码的模块化,因为实例化的逻辑被封装在工厂方法中。

(3) 实例工厂方式

实例化工厂方式的工厂类中,不再使用静态方法创建Bean实例,而是采用直接创建Bean实例的方式。同时,在配置文件中,需要实例化的Bean也不是通过class属性直接指向其实例化的类,而是通过factory-bean属性配置一个实例工厂,然后使用factory-method属性确定使用工厂中的哪个方法。

案例:

复制代码
package com.example;

public class Car {
    private String brand;

    private Car(String brand) {
        this.brand = brand;
    }

    public String getBrand() {
        return brand;
    }
}

// 工厂类
public class CarFactory {
    private String defaultBrand;

    public CarFactory(String defaultBrand) {
        this.defaultBrand = defaultBrand;
    }

    // 实例工厂方法
    public Car createCar() {
        return new Car(defaultBrand);
    }
}

<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.xsd">

    <!-- 定义CarFactory Bean -->
    <bean id="carFactory" class="com.example.CarFactory">
        <constructor-arg value="宝马"/>
    </bean>

    <!-- 使用实例工厂方法创建Car Bean -->
    <bean id="myCar" factory-bean="carFactory" factory-method="createCar"/>
</beans>

在这个XML配置中:

我们首先定义了一个CarFactory的Bean,id为carFactory,并使用构造函数注入了一个默认的品牌名称。

然后,我们定义了一个Car的Bean,id为myCar。我们使用factory-bean属性指定了工厂Bean的id,使用factory-method属性指定了实例工厂方法的名称。这样,Spring容器会调用carFactory实例的createCar方法来创建myCar的实例。

实例工厂方法实例化提供了一种灵活的方式来创建Bean,特别是当你需要在创建Bean之前对工厂Bean进行复杂的初始化,或者需要同一个工厂实例来创建多个Bean时。这种方式也有助于保持代码的模块化,因为实例化的逻辑被封装在工厂方法中。

(4) 实例工厂变种方式

与实例化工厂方式不同的是,自定义工厂类需要实现FactoryBean接口,通过覆写接口的getObject()、getObjectType()和 isSingleton()方法来创建Bean。同时,在配置文件中需将该工厂Bean注册到容器中。

案例:

复制代码
package com.example;

public interface Car {
    void drive();
}

public class SportsCar implements Car {
    @Override
    public void drive() {
        System.out.println("Driving a sports car");
    }
}

public class SedanCar implements Car {
    @Override
    public void drive() {
        System.out.println("Driving a sedan car");
    }
}

// 工厂类
public class CarFactory {
    // 实例工厂方法,根据传入的类型参数创建不同类型的Car实例
    public Car createCar(String type) {
        if ("sports".equalsIgnoreCase(type)) {
            return new SportsCar();
        } else if ("sedan".equalsIgnoreCase(type)) {
            return new SedanCar();
        }
        throw new IllegalArgumentException("Unknown car type: " + type);
    }
}

<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.xsd">

    <!-- 定义CarFactory Bean -->
    <bean id="carFactory" class="com.example.CarFactory"/>

    <!-- 使用实例工厂方法创建SportsCar Bean -->
    <bean id="sportsCar" factory-bean="carFactory" factory-method="createCar">
        <constructor-arg value="sports"/>
    </bean>

    <!-- 使用实例工厂方法创建SedanCar Bean -->
    <bean id="sedanCar" factory-bean="carFactory" factory-method="createCar">
        <constructor-arg value="sedan"/>
    </bean>

</beans>

在这个XML配置中:

我们首先定义了一个CarFactory的Bean,id为carFactory。

然后,我们定义了两个Car类型的Bean,id分别为sportsCar和sedanCar。我们使用factory-bean属性指定了工厂Bean的id,使用factory-method属性指定了实例工厂方法的名称,并使用constructor-arg标签提供了实例工厂方法需要的参数。这样,Spring容器会调用carFactory实例的createCar方法,并根据参数的不同来创建SportsCar或SedanCar的实例。

实例工厂变种方式提供了一种灵活的方式来创建Bean,特别是当你需要根据不同的条件或参数来创建不同类型的Bean实例时。这种方式也有助于保持代码的模块化,因为实例化的逻辑被封装在工厂方法中。

(5) 注解方式

创建一个配置类,在该类上添加@Configuration注解,如果在配置类中编写一个方法,并给该方法添加上@Bean注解时,该方法将会创建一个bean的实例,这个实例将会被注册到Spring容器中。

复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration // 告诉Spring这是一个配置类
public class AppConfig {

    // 使用@Bean注解来声明一个Bean,并返回这个Bean的实例
    @Bean
    public MyBean myBean() {
        return new MyBean();
    }
}

// MyBean类的定义
class MyBean {
    // 类的成员和方法
三、Bean的作用域
1.作用域分类

Spring容器在初始化一个Bean实例时,可以同时为其指定特定的作用域。Spring为Bean实例定义了五种Bean的作用域,这五种作用域说明如下所示:

(1)singleton

​ 单例模式,使用singleton定义的bean在Spring容器中将只有一个实例,也就是说,无论有多少个Bean引用它,始终将指向同一个对象。这也是Spring容器默认的作用域。( singleton作用域是Spring容器默认的作用域,当一个Bean的作用域为singleton时,Spring容器中只会存在一个共享的Bean实例,并且所有对Bean的请求,只要id与该Bean定义相匹配的,就只会返回Bean的同一个实例。)

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

复制代码
<bean id="hello"   
    class="edu.cqie.ssm.Hello"   
    scope="singleton"/>
(2)prototype

​ 原型模式(也可以是多例模式),每次通过Spring容器获取的prototype定义的Bean时,容器都将创建一个新的Bean实例。( 在使用prototype作用域时,Spring容器会为每个对该Bean的请求都创建一个新的实例。要将Bean定义为prototype,只需在xml文件中将元素的scope属性的值设置为prototype即可。)

复制代码
<bean id="hello"  
    class="edu.cqie.ssm.Hello" 
    scope="prototype "/>
(3)request

​ 在一次HTTP请求中,容器会返回该Bean的同一个实例。而对不同的HTTP请求则会产生一个新的Bean,而且该Bean仅在当前HTTP Request内有效。

(4)session

​ 在一次HTTP Session中,容器会返回该Bean的同一个实例。而对不同的HTTP请求则会产生一个新的Bean,而且该Bean仅在当前HTTP Session内有效。

2.Bean 的生命周期
四、Bean的装配方式

Bean的装配可以理解为Bean的依赖关系注入过程,而依赖注入(DI,Dependency Injection)是控制反转的另外一种说法,只是从不同角度描述相同的概念。控制反转是指将对象(Bean)交给Spring容器创建,而依赖注入则是指由IoC容器在运行期间动态地将某种依赖资源注入到对象之中,也可以理解为通过Spring容器将Bean赋值给另一个Bean的属性的过程。

Spring框架支持多种Bean的装配方式,常用的方式有:

1.基于XML的装配方式
(1)设值注入 Setter Injection

设值注入要求一个Bean必须满足以下两点要求:

​ Bean类必须提供一个默认的构造方法。

复制代码
Bean类必须为需要注入的属性提供对应的set方法。

使用设值注入时,在Spring配置文件中,需要使用元素的子元素元素来为每个属性注入值。

(2)构造注入 Constructor Injection

​ 使用构造注入时,在配置文件里使用标签来定义构造方法的参数,可以使用其value属性(或子元素)来设置该参数的值。

2.基于Annotation的装配方式

​ 在Spring中,尽管使用XML配置文件可以实现Bean的装配工作,但如果应用中有很多Bean时,会导致XML配置文件过于臃肿,给后续的维护和升级工作带来一定的困难。为此,Java从JDK1.5以后,提供了Annotation(注解)功能,Spring也提供了对Annotation技术的全面支持。

@Component:可以使用此注解描述Spring中的Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可。

@Repository:用于将数据访问层 (DAO 层 ) 的类标识为Spring中的Bean,其功能与 @Component 相同。

@Service:通常作用在业务层(Service层),用于将业务层的类标识为Spring中的Bean,其功能与 @Component 相同。

@ Constroller:通常作用在控制层(Controller层),用于将控制层的类标识为Spring中的Bean,其功能与 @Component 相同。

@Configuration **:**通常作用在配置类,配合指定该类中一个或多个@Bean方法,交由Spring加载和生成bean,相当于xml的标签+子标签。

@ Autowired:用于对Bean的属性变量、属性的Set方法及构造函数进行标注,配合对应的注解处理器完成Bean的自动注入工作。默认按照Bean的类型进行装配。

@Resource:其作用与Autowired一样。其区别在于@Autowired默认按照Bean类型装配,而@Resource默认按照Bean实例名称进行装配。@Resource中有两个重要属性:name和type。Spring将name属性解析为Bean实例名称,type属性解析为Bean实例类型。如果指定name属性,则按实例名称进行装配;如果指定type属性,则按Bean类型进行装配;如果都不指定,则先按Bean实例名称装配,如果不能匹配,再按照Bean类型进行装配;如果都无法匹配,则抛出NoSuchBeanDefinitionException异常。

@Qualifier:与@Autowired注解配合使用,会将默认的按Bean类型装配修改为按Bean的实例名称装配,Bean的实例名称由@Qualifier注解的参数指定。

3.自动装配方式

自动装配就是由Spring框架自动将一个Bean注入到其他Bean的Property中。要使用自动装配,就需要配置元素的autowire属性。

autowire 属性有 5 个值,其值说明如下所示。

byName:根据Property的Name自动装配,如果一个bean的name,和另一个bean中的Property的name相同,则自动装配这个bean到Property中。

byType:根据Property的数据类型(Type)自动装配,如果一个bean的数据类型,兼容另一个bean中Property的数据类型,则自动装配。

constructor:根据构造函数参数的数据类型,进行byType模式的自动装配。

autodetect:如果发现默认的构造函数,用constructor模式,否则用byType模式。

no:默认情况下,不使用自动装配,Bean依赖必须通过ref元素定义。

入到其他Bean的Property中。要使用自动装配,就需要配置元素的autowire属性。

autowire 属性有 5 个值,其值说明如下所示。

byName:根据Property的Name自动装配,如果一个bean的name,和另一个bean中的Property的name相同,则自动装配这个bean到Property中。

byType:根据Property的数据类型(Type)自动装配,如果一个bean的数据类型,兼容另一个bean中Property的数据类型,则自动装配。

constructor:根据构造函数参数的数据类型,进行byType模式的自动装配。

autodetect:如果发现默认的构造函数,用constructor模式,否则用byType模式。

no:默认情况下,不使用自动装配,Bean依赖必须通过ref元素定义。

相关推荐
蓝色王者19 小时前
springboot 2.6.13 整合flowable6.8.1
java·spring boot·后端
Tao____19 小时前
基于Ruoyi开发的IOT物联网平台
java·网络·物联网·mqtt·网络协议
花哥码天下20 小时前
apifox登录后设置token到环境变量
java·后端
浩瀚地学20 小时前
【Java】常用API(二)
java·开发语言·经验分享·笔记·学习
廋到被风吹走21 小时前
【Spring】Spring MVC核心原理与RESTful最佳实践详解
spring·mvc·restful
hashiqimiya21 小时前
springboot事务触发滚动与不滚蛋
java·spring boot·后端
PPPHUANG21 小时前
一次 CompletableFuture 误用,如何耗尽 IO 线程池并拖垮整个系统
java·后端·代码规范
恩创软件开发21 小时前
创业日常2026-1-8
java·经验分享·微信小程序·小程序