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