02. Spring中的bean

1 Bean的配置

Spring容器支持XML和Properties两种格式的配置文件,在实际开发中,最常使用的就是XML格式的配置方式
在Spring中,XML配置文件的根元素是beans, beans中包含了多个bean子元素,每一个bean子元素定义了一个Bean,并描述了该Bean如何被装配到Spring容器中

< bean>元素的常用属性及其子元素

属性或子元素名称描述
id是一个Bean的唯一标识符, Spring容器对Bean的配置、管理都通过该属性来完成
nameSpring容器同样可以通过此属性对容器中的Bean进行配置和管理,name属性中可以为指定多个名称,每个名称之间用逗号或分号隔开
class该属性指定了Bean的具体实现类,它必须是一个完整的类名,使用类的全限定名
scope用来设定Bean实例的作用域,其属性值有: singleton(单例)、 prototype(原型)、 request、session、 global Session、 application和 websocket。其默认值为 singletonbean
constructor-arg< bean>元素的子元素,可以使用此元素传入构造参数进行实例化。该元素的 index属性指constructor-arg「定构造参数的序号(从0开始),ype属性指定构造参数的类型,参数值可以通过ref属性或 value属性直接指定,也可以通过ref或 value子元素指定
property< bean>元素的子元素,用于调用Bean实例中的 setter方法完成属性赋值,从而完成依赖property注入。该元素的name属性指定Bean实例中的相应属性名,ref属性或 value属性用于指定参数值
ref< property>、< constructor-arg>等元素的属性或子元素,可以用于指定对Bean工厂中某个Bean实例的引用
value< property>、< constructor-arg>等元素的属性或子元素,可以用于直接指定一个常量值
list用于封装List或数组类型的依赖注入
set用于封装Set类型属性的依赖注入
map用于封装Map类型属性的依赖注入
entry< map>元素的子元素,用于设置一个键值对。其key属性指定字符串类型的键值,ref或entryvalue子元素指定其值,也可以通过 value-ref或 value属性指定其值
1. xml文件的配置
<?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-4.3.xsd">

		<!-- 使用id来配置bean -->
	<bean id="userDao" class="com.clarence.ioc.UserDaoImpl"></bean>
		<!-- 使用name配置bean -->
	<bean name="userService" class="com.clarence.ioc.UserServiceImpl"/>
	//初级情况下,id和name的作用基本一致
	//如果在Bean中未指定id和name,则Spring会将class值当作id使用。
</beans>

2 Bean的实例化

实例化Bean有三种方式,分别为构造器实例化、静态工厂方式实例化和实例工厂方式实例化(其中最常用的是构造器实例化)

2.1 构造器实例化

构造器实例化是指Spring容器通过Bean对应类中默认的无参构造方法来实例化Bean

1. Bean1.java
public class Bean1 {
   
}
2. xml配置
	<bean id="bean1" class="com.clarence.instance.constructor.Bean1"/>
	

实例化结果
通过构造器实例化

2.2 静态工厂实例化

该方式要求开发者创建一个静态工厂的方法来创建Bean的实例,其Bean配置中的class属性所指定的不再是Bean实例的实现类,而是静态工厂类,同时还需要使用factory-method属性来指定所创建的静态工厂方法

1. xml文件配置
    <bean id="bean2" class="com.clarence.instance.static_factory.MyBean2Factory"
        factory-method="createBean"/>
        //由于这种方式配置Bean后,Spring容器不知道哪个是所需要的工厂方法,
        //所以增加了factory-method属性来告诉Spring容器,其方法名称为createBean
2. 静态工厂
public class MyBean2Factory {
    //使用自己的工厂创建Bean2实例
    public static Bean2 createBean() {
        return new Bean2();
    }
}

实例化结果
在这里插入图片描述

2.3 实例工厂方式实例化

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

1. 实例工厂
public class MyBean3Factory {
    public MyBean3Factory() {
        System.out.println("bean3工厂实例化中.......");
    }
    public Bean3 createBean() {
        return new Bean3();
    }
}
2. xml文件配置
<bean id="myBean3Factory" class="com.clarence.instance.factory.MyBean3Factory"/>
    <bean id="bean3" factory-bean="myBean3Factory" factory-method="createBean"/>

实例化结果
在这里插入图片描述

2.4 三种方式的异同

构造器直接实例化Bean,静态工厂在实例化Bean不会实例化工厂类,实例工厂在实例化bean时会先实例化工厂类
实际应用中的异同有待进一步的探究

3 Bean的作用域

前文提到,由scope元素来指定作用域,Spring4.3定义了7中作用域

作用域名称说明
singleton(单例)使用 singleton 定义的Bean在 Spring容器中将只有一个实例,也就是说,无论有多少个Bean引用它,始终将指向同一个对象 。这也是 Spring容器默认的作用域
prototype(原型)每次通过 Spring容器获取的 prototype定义的Bean时,容器都将创建一个新的Bean实例
request在一次HTTP请求中,容器会返回该Bean的同一个实例。对不同的HTTP请求则会产生request个新的Bean,而且该Bean仅在当前HTTP Request内有效
session在一次HTTP Session中,容器会返回该Bean的同一个实例。对不同的HTTP请求则会产生一个新的Bean,而且该Bean仅在当前HTTP Session内有效
globalSession在一个全局的HTTPSession中,容器会返回该Bean的同一个实例。仅在使用portlet上下文时有效
application为每个 Servletcontext 对象创建一个实例。仅在Web相关的Application Context中生效
websocket为每个 websocket对象创建一个实例。仅在Web相关的 Applicationcontext 中生效

3.1 singleton作用域

singleton是Spring容器默认的作用域,当Bean的作用域为singleton时,Spring容器就只会存在一个共享的Bean实例,并且所有对Bean的请求,只要id与该Bean的id属性相匹配,就会返回同一个Bean实例。singleton作用域对于无会话状态的Bean(如Dao组件、Service组件)来说,是最理想的选择。

1.xml文件配置
    <!-- 作用域为单例,也是默认作用域 -->
    <bean id="scope" class="com.clarence.scope.Scope"
        scope="singleton"/>
2.测试用例
    System.out.println(applicationContext.getBean("scope"));
    //两次打印对象相同
    System.out.println(applicationContext.getBean("scope"));

运行结果:
在这里插入图片描述
说明两次返回的是同一个对象

3.2 prototype作用域

对需要保持会话状态的Bean(如Struts2的Action类)应该使用prototype作用域。在使用prototype作用域时,Spring容器会为每个对该Bean的请求都创建一个新的实例。

1.xml文件配置
    <!-- 作用域为原型,每次实例化都会产生一个新的对象 -->
    <bean id="scope2" class="com.clarence.scope.Scope"
        scope="prototype"/>

运行结果
在这里插入图片描述

疑惑点:两次运行,单例返回的对象都是一样的,说明该对象实际并没有在运行结束后销毁,那什么时候销毁?

4 Bean的生命周期

Spring容器可以管理singleton作用域的Bean的生命周期,在此作用域下,Spring能够精确地知道该Bean何时被创建,何时初始化完成以及何时被销毁。
对于prototype作用域的Bean, Spring只负责创建,当容器创建了Bean实例后,Bean的实例就交给客户端代码来管理,Spring容器将不再跟踪其生命周期。
每次客户端请求prototype作用域的Bean时,Spring容器都会创建一个新的实例,并且不会管那些被配置成prototype作用域的Bean的生命周期。
在一般情况下,常会在Bean的postinitiation(初始化后)和predestruction(销毁前)执行一些相关操作

在这里插入图片描述

(1)根据配置情况调用Bean构造方法或工厂方法实例化Bean。
(2)利用依赖注入完成Bean中所有属性值的配置注入。
(3)如果Bean实现了BeanNameAware接口,则Spring调用Bean的setBeanName()方法传入当前Bean的id值。
(4)如果Bean实现了BeanFactoryAware接口,则Spring调用setBeanFactory()方法传入当前工厂实例的引用。
(5)如果Bean实现了ApplicationContextAware接口,则Spring调用setApplicationContext()方法传入当前ApplicationContext实例的引用。
(6)如果BeanPostProcessor和Bean关联,则Spring将调用该接口的预初始化方法postProcessBeforeInitialzation()对Bean进行加工操作,这个非常重要,Spring的AOP就是用它实现的。
(7)如果Bean实现了InitializingBean接口,则Spring将调用afterPropertiesSet()方法。
(8)如果在配置文件中通过init-method属性指定了初始化方法,则调用该初始化方法。
(9)如果有BeanPostProcessor和Bean关联,则Spring将调用该接口的初始化方法post ProcessAfterInitialization()。此时,Bean已经可以被应用系统使用了。(10)如果在< bean> 中指定了该Bean的作用范围为scope=“singleton”,则将该Bean放入Spring IoC的缓存池中,将触发Spring对该Bean的生命周期管理;如果在< bean>中指定了该Bean的作用范围为scope=“prototype”,则将该Bean交给调用者,调用者管理该Bean的生命周期,Spring不再管理该Bean。
(11)如果Bean实现了DisposableBean接口,则Spring会调用destory()方法将Spring中的Bean销毁;如果在配置文件中通过destory-method属性指定了Bean的销毁方法,则Spring将调用该方法进行销毁

5 Bean的装配方式

Bean的装配可以理解为依赖关系注入,Bean的装配方式即Bean依赖注入的方式。Spring容器支持多种形式的Bean的装配方式,如基于XML的装配、基于注解(Annotation)的装配和自动装配等(其中最常用的是基于注解的装配)

5.1 基于XML的装配

Spring提供了两种基于XML的装配方式:设值注入(Setter Injection)和构造注入(Constructor Injection)。
在Spring实例化Bean的过程中,Spring首先会调用Bean的默认构造方法来实例化Bean对象,然后通过反射的方式调用setter方法来注入属性值。
因此,设值注入要求一个Bean必须满足以下两点要求。

  1. Bean类必须提供一个默认的无参构造方法。
  2. Bean类必须为需要注入的属性提供对应的setter方法。

使用设值注入时,在Spring配置文件中,需要使用< bean>元素的子元素< property>来为每个属性注入值;而使用构造注入时,在配置文件里,需要使用< bean>元素的子元素< constructor-arg>来定义构造方法的参数,可以使用其value属性(或子元素)来设置该参数的值。

1.xml文件配置
	<!-- 使用构造注入方式装配User实例 ,调用的是有参构造器-->
	<bean id="user1" class="com.clarence.assmeble.User">
	   <!-- 配置的是参数列表 -->
		<constructor-arg index="0" value="tom"></constructor-arg>
		<constructor-arg index="1" value="123456"></constructor-arg>
		<constructor-arg index="2">
			<list>
				<value>"constructorvalue1"</value>
				<value>"constructorvalue2"</value>
			</list>
		</constructor-arg>
	</bean>
	<!-- 使用设值注入方式装配User实例 -->
	<bean id="user2" class="com.clarence.assmeble.User">
	<!-- 配置的是每个独立的属性 -->
		<property name="username" value="jack"></property>
		<property name="password" value="654321"></property>
		<property name="list">
			<list>
				<value>"constructorvalue1"</value>
				<value>"constructorvalue2"</value>
			</list>
		</property>
	</bean>

两种方式的异同:

  1. 构造注入需要有相匹配的构造方法,例如,有两参构造不能输入三个参数,而设值注入可以注入任意多的值,每次都是独立的注入
  2. 设值注入必须提供无参构造和相应的setter方法,否则注入失败
  3. 两者xml写法不同
  4. 个人更愿意使用设值注入

5.2 基于Annotation的装配

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

常用注解功能
@Component可以使用此注解描述Spring中的Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可
@Repository用于将数据访问层(DAO层)的类标识为Spring中的Bean,其功能与@Component相同
@Service通常作用在业务层(Service层),用于将业务层的类标识为Spring中的Bean,其功能与@Component相同
@Controller通常作用在控制层(如Spring MVC的Controller),用于将控制层的类标识为Spring中的Bean,其功能与@Component相同
@Autowired用于对Bean的属性变量、属性的setter方法及构造方法进行标注,配合对应的注解处理器完成Bean的自动配置工作。默认按照Bean的类型进行装配
@Resource其作用与Autowired一样。其区别在于@Autowired默认按照Bean类型装配,而@Resource默认按照Bean实例名称进行装配
@Qualifier与@Autowired注解配合使用,会将默认的按Bean类型装配修改为按Bean的实例名称装配,Bean的实例名称由@Qualifier注解的参数指定

注解详细解释

  1. @Resource中有两个重要属性:name和type。Spring将name属性解析为Bean实例名称,type属性解析为Bean实例类型。如果指定name属性,则按实例名称进行装配;如果指定type属性,则按Bean类型进行装配;如果都不指定,则先按Bean实例名称装配,如果不能匹配,再按照Bean类型进行装配;如果都无法匹配,则抛出NoSuchBeanDefinitionException异常
  2. 虽然@Repository、@Service与@Controller功能与@Component注解的功能相同,但为了使标注类本身用途更加清晰,建议在实际开发中使用@Repository、@Service与@Controller分别对实现类进行标注
  3. 注解必须写在相应语句的上一行,如:
1.
    @Resource(name="userDao")
    private UserDao userDao;//将userDao装配给了userDao
    private UserDao userDao2;
2.
    @Resource(name="userDao")
    private UserDao userDao2;//将userDao装配给了userDao2
    private UserDao userDao;

注解与xml的关系
注:@Repository、@Service与@Controller基本一致,只是bean所在的逻辑层不同,以此区分

注解xml写法
@Repository(“userDao”)< bean id="userDao"class=“com.clarence.annotation.UserDaoImpl”/>
@Resource(name=“userDao”)< property name=“userDao” ref=“userDao”/>
1.xml配置
    <!-- 
      使用config新添加的约束信息
      xmlns:context="http://www.springframework.org/schema/context"
      xsi:schemaLocation=
      "http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context-4.3.xsd"
     -->

    <!-- 使用context需要添加配置信息 -->
    <!-- 需要导入额外的包spring-aop -->
    
    <!--
    <context:annotation-config />
    <bean id="userDao" class = "com.clarence.annotation.UserDaoImpl"/>
    <bean id="userService" class = "com.clarence.annotation.UserServiceImpl"/>
    <bean id="userController" class = "com.clarence.annotation.UserController"/>
    -->
    
    <!-- 对bean所在的包自动扫描,和前面配置作用一样 -->
    
    <context:component-scan base-package="com.clarence.annotation"/>
    <!--配合注解使用,减少xml文件的冗余-->
2.注解写法
@Repository("userDao")
@Service("userService")
@Controller("userController")

运行结果:
在这里插入图片描述

5.3 自动装配

Spring的< bean>元素中包含一个autowire属性,我们可以通过设置autowire的属性值来自动装配Bean。所谓自动装配,就是将一个Bean自动地注入到其他Bean的Property中

< bean>元素的autowire属性值及说明

属性值说明
default(默认值)由< bean>的上级标签< beans>的 default- autowire属性值确定。例如< beans default(默认值) autowire=" byName">,则该< bean>元素中的 autowire属性对应的属性值就为 byName
byName根据属性的名称自动装配。容器将根据名称查找与属性完全一致的Bean,并将其属性自动装配
byType根据属性的数据类型(Type)自动装配,如果一个Bean的数据类型,兼容另一个Bean中属性的数据类型,则自动装配
constructor根据构造函数参数的数据类型,进行 bytype模式的自动装配
no在默认情况下,不使用自动装配,Bean依赖必须通过ref元素定义
1.xml文件配置
	<bean id="userDao" class = "com.clarence.annotation.UserDaoImpl" />
    <bean id="userService" class = "com.clarence.annotation.UserServiceImpl" autowire="byName"/>
    <bean id="userController" class = "com.clarence.annotation.UserController" autowire="byName"/> 
    
1.2.在Service、Controller分别加入属性的setter方法

2.使用注解方式,只需在@Autowired,自动装填
然后使用@Qualifier()标记需要装填的属性
两种输入方式没特别的大的区别,看自身习惯使用

运行结果在这里插入图片描述

5.4 三种方式的比较

没有太大的区别,个人觉得,使用更多的看个人习惯,自动装配相对于XML装配要简单一些。注解如果习惯的话,应该是更好用一些。

6 源代码

02. Spring中的bean

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值