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元素定义。

相关推荐
hanbarger3 分钟前
mybatis框架——缓存,分页
java·spring·mybatis
cdut_suye10 分钟前
Linux工具使用指南:从apt管理、gcc编译到makefile构建与gdb调试
java·linux·运维·服务器·c++·人工智能·python
苹果醋322 分钟前
2020重新出发,MySql基础,MySql表数据操作
java·运维·spring boot·mysql·nginx
小蜗牛慢慢爬行24 分钟前
如何在 Spring Boot 微服务中设置和管理多个数据库
java·数据库·spring boot·后端·微服务·架构·hibernate
azhou的代码园27 分钟前
基于JAVA+SpringBoot+Vue的制造装备物联及生产管理ERP系统
java·spring boot·制造
wm10431 小时前
java web springboot
java·spring boot·后端
smile-yan1 小时前
Provides transitive vulnerable dependency maven 提示依赖存在漏洞问题的解决方法
java·maven
老马啸西风1 小时前
NLP 中文拼写检测纠正论文-01-介绍了SIGHAN 2015 包括任务描述,数据准备, 绩效指标和评估结果
java
Earnest~1 小时前
Maven极简安装&配置-241223
java·maven
皮蛋很白1 小时前
Maven 环境变量 MAVEN_HOME 和 M2_HOME 区别以及 IDEA 修改 Maven repository 路径全局
java·maven·intellij-idea