05.Spring的Bean配置详解

简介

基于xml的Spring应用,开发中主要是对Bean的配置,下面将详解Bean的常用配置。

1. 常用配置总览

Xml配置方式 功能描述
<bean id="" class="" /> Bean的id和全限定名配置
<bean name="" /> 通过name设置Bean的别名,通过别名也能直接获取到Bean实例
<bean scope="" /> Bean的作用范围,BeanFactory作为容器时取值singleton和prototyp
<bean lazy-init="" /> Bean的实例化时机,是否延迟加载。BeanFactory作为容器时无效
<bean init-method="" /> 加载磁盘路径下的xml配置的ApplicationContext
<bean destroy-method="" /> Bean实例销毁前的方法,method指定方法名
<bean autowire="byType"/> 设置自动注入模式,常用的有按照类型byType,按照名字byName
<bean factory-bean="" factory-method=""/> 指定哪个工厂Bean的哪个方法完成Bean的创建

2. Bean的简单配置汇总

bean.xml的配置步骤和其他部分代码请参考 02.Spring的BeanFactory
applicationContext.xml的配置步骤和其他部分代码请参考 03.Spring的ApplicationContext

2.1 Bean的基础配置

例如:配置UserDaoImpl由Spring容器负责管理

<bean id="userDao" class="com.test.dao.impl.UserDaoImpl"/>

此时存储到Spring容器(singleObjects单例池)中的Bean的beanName是userDao,值是UserDaoImpl对象,可以根据beanName获取Bean实例

applicationContext.getBean("userDao");

如果不配置id,则Spring会把当前Bean实例的全限定名作为beanName

applicationContext.getBean("com.test.dao.impl.UserDaoImpl");

在这里插入图片描述

2.2 Bean的别名配置

可以为当前Bean指定多个别名,根据别名也可以获得Bean对象

<bean id="userDao" name="aaa,bbb" class="com.test.dao.impl.UserDaoImpl"/>

此时多个名称都可以获得UserDaoImpl实例对象

applicationContext.getBean("userDao");
applicationContext.getBean("aaa");
applicationContext.getBean("bbb");

2.3 Bean的范围配置

默认情况下,单纯的Spring环境Bean的作用范围有两个:Singleton和Prototype
singleton:单例,默认值,Spring容器创建的时候,就会进行Bean的实例化,并存储到容器内部的单例池中,每次getBean时都是从单例池中获取相同的Bean实例
prototype:原型,Spring容器初始化时不会创建Bean实例,当调用getBean时才会实例化Bean,每次getBean都会创建一个新的Bean实例

scope设置为singleton

当scope设置为singleton时,获得两次对象打印结果是一样的

<bean id="userDao" class="com.test.dao.impl.UserDaoImpl" scope="singleton"/>

在这里插入图片描述

通过断点调试,观察可以发现单例池中存在 userDao 实例

在这里插入图片描述

scope设置为prototype

当scope设置为prototype时,获得两次对象打印结果是不一样的

<bean id="userDao" class="com.test.dao.impl.UserDaoImpl" scope="prototype"/>

在这里插入图片描述

通过断点调试,观察可以发现单例池中不存在 userDao 实例,但是 userDao的信息已经被存储到beanDefinitionMap中了

在这里插入图片描述

在这里插入图片描述

2.4 Bean的延迟加载

当lazy-init设置为true时为延迟加载,也就是当Spring容器创建的时候,不会立即创建Bean实例,等待用到时在创建Bean实例并存储到单例池中去,后续在使用该Bean直接从单例池获取即可,本质上该Bean还是单例的

<bean id="userDao" class="com.test.dao.impl.UserDaoImpl" lazy-init="true"/>

可以看到debug走到getBean,单例池中才有userDao

在这里插入图片描述

在这里插入图片描述

2.5 Bean的初始化和销毁方法配置

Bean在被实例化后,可以执行指定的初始化方法完成一些初始化的操作,Bean在销毁之前也可以执行指定的销毁方法完成一些操作

<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl" init-method="init" 
destroy-method="destroy"/>
public class UserDaoImpl implements UserDao {
    
    

    public UserDaoImpl() {
    
     System.out.println("UserDaoImpl创建了..."); }
   
    @Override
    public void init(){
    
    
        System.out.println("init初始化方法执行...");
    }
    @Override
    public void destroy() {
    
    
        System.out.println("destroy方法执行....");
    }
}

init初始化方法执行:

在这里插入图片描述

destroy初始化方法执行:

在这里插入图片描述

除此之外,我们还可以通过实现 InitializingBean (DisposableBean)接口,完成一些Bean的初始化(销毁)操作。

public class UserDaoImpl implements UserDao, InitializingBean {
    
    
   public UserDaoImpl() {
    
    System.out.println("UserDaoImpl创建了...");}
   public void init(){
    
    System.out.println("初始化方法...");}
   public void destroy(){
    
    System.out.println("销毁方法...");}
   //执行时机早于init-method配置的方法
   public void afterPropertiesSet() throws Exception {
    
    
   System.out.println("InitializingBean..."); 
   }
}
public class MyBean implements DisposableBean {
    
    

    public void destroy() {
    
    
        // 在这里执行资源清理和善后工作
        // 执行时机早于destroy-method配置的方法
        System.out.println("MyBean destroyed");
    }

}

3. Bean的实例化配置

Spring的实例化方式主要如下两种:

构造方式实例化:底层通过构造方法对Bean进行实例化
工厂方式实例化:底层通过调用自定义的工厂方法对Bean进行实例化

3.1 构造方式实例化

构造方式实例化Bean又分为无参构造方法实例化有参构造方法实例化

Spring中配置的<bean>几乎都是无参构造该方式,此处不在赘述。下面讲解有参构造方法实例化Bean

//有参构造方法
public UserDaoImpl(String name){
    
    
}

有参构造在实例化Bean时,需要参数的注入,通过<constructor-arg>标签,嵌入在<bean>标签内部提供构造参数,如下:

<bean id="userDao" class="com.test.dao.impl.UserDaoImpl">
    <constructor-arg name="name" value="haohao"/>
</bean>

3.2 工厂方式实例化

工厂方式实例化Bean,又分为如下三种:
静态工厂方法实例化Bean
实例工厂方法实例化Bean
实现FactoryBean规范延迟实例化Bean

静态工厂方法

静态工厂方法实例化Bean,其实就是定义一个工厂类,提供一个静态方法用于生产Bean实例,在将该工厂类及其静态方法配置给Spring即可

<bean id="userDao" class="com.test.factory.MyBeanFactory1" factory-method="userDao">
     <constructor-arg name="name" value="test"/>
     <constructor-arg name="age" value="13"/>
</bean>
public class MyBeanFactory1 {
    
    

    public static UserDao userDao(String name,int age){
    
    
        //Bean创建之前可以进行一些其他业务逻辑操作
        return new UserDaoImpl();
    }

}

<constructor-arg>标签不仅仅是为构造方法传递参数,只要是为了实例化对象而传递的参数都可以通过<constructor-arg>标签完成,例如上面通过静态工厂方法实例化Bean所传递的参数也是要通过<constructor-arg>进行传递的

测试代码,直接通过ApplicationContext获得userDao即可,断点调试,UserDaoImpl实例对象会存在于单例池中

在这里插入图片描述

实例工厂方法

实例工厂方法,也就是非静态工厂方法产生Bean实例,与静态工厂方式比较,该方式需要先有工厂对象,在用工厂对象去调用非静态方法,所以在进行配置时,要先配置工Bean,再配置目标Bean

<!-- 配置实例工厂Bean -->
<bean id="userDaoFactoryBean2" class="com.test.factory.MyBeanFactory2"/>

<!-- 配置实例工厂Bean的哪个方法作为工厂方法 -->
<bean id="userDao" factory-bean="userDaoFactoryBean2" factory-method="userDao">
     <constructor-arg name="username" value="test"/>
</bean>

public class MyBeanFactory2 {
    
    

    public UserDao userDao(String username){
    
    
        //Bean创建之前可以进行一些其他业务逻辑操作
        return new UserDaoImpl();
    }

}

断点调试,UserDaoImpl实例对象一样会存在于单例池中(静态非静态都没有延迟加载)。
单例池中既有工厂Bean实例,也有目标Bean实例,且都是在Spring容器创建时,就完成了Bean的实例化

在这里插入图片描述

实现FactoryBean规范(延迟实例化)

上面不管是静态工厂方式还是非静态工厂方式,都是自定义的工厂方法,Spring提供了FactoryBean的接口规范,FactoryBean接口定义如下:

public interface FactoryBean<T> {
    
    

      String OBJECT_TYPE_ATTRIBUTE = “factoryBeanObjectType”;
      
      T getObject() throws Exception; //获得实例对象方法
      
      Class<?> getObjectType(); //获得实例对象类型方法
      
      default boolean isSingleton() {
    
    
          return true;
      }
}

定义工厂实现FactoryBean

public class MyBeanFactory3 implements FactoryBean<UserDao> {
    
    

    @Override
    public UserDao getObject() throws Exception {
    
    
        System.out.println("getObject被调用...");
        return new UserDaoImpl();
    }

    @Override
    public Class<?> getObjectType() {
    
    
        return UserDao.class;
    }
}

配置FactoryBean交由Spring管理即可

<bean id="userDao3" class="com.test.factory.MyBeanFactory3"/>

通过断点观察发现Spring容器创建时,MyBeanFactory3被实例化了,并存储到了单例池singletonObjects中,但是getObject() 方法尚未被执行,UserDaoImpl也没被实例化,当首次用到UserDaoImpl时,才调用getObject() ,此工厂方式产生的Bean实例不会存储到单例池singletonObjects中,会存储到 factoryBeanObjectCache 缓存池中,并且后期每次使用到userDao3都从该缓存池中返回的是同一个userDao3实例

在这里插入图片描述

在这里插入图片描述

4. Bean的依赖注入配置

4.1 注入方式

Bean的依赖注入有两种方式:

注入方式 配置方式
通过Bean的set方法注入 <property name="userDao" ref="userDao"/> <property name="userDao" value="haohao"/>
通过构造Bean的方法进行注入 <constructor-arg name="name" ref="userDao"/> <constructor-arg name="name" value="haohao"/>

其中,ref 是 reference 的缩写形式,翻译为:涉及,参考的意思,用于引用其他Bean的id。value 用于注入普通属性值。

4.2 数据类型

依赖注入的数据类型有如下三种:

类型 描述
普通数据类型 例如:String、int、boolean等,通过value属性指定
引用数据类型 例如:UserDaoImpl、DataSource等,通过ref属性指定。
集合数据类型 例如:List、Map、Properties等

注入 List 集合 – 普通数据

public void setStrList(List<String> strList){
    
    
    strList.forEach(str->{
    
    
        System.out.println(str);
    });
 }
<property name="strList">
   <list>
      <value>haohao</value>
      <value>miaomiao</value>
   </list>
</property>

注入 List 集合 – 引用数据

public void setObjList(List<UserDao> objList){
    
    
     objList.forEach(obj->{
    
    
        System.out.println(obj);
     });
}
<property name="objList">
   <list>
      <bean class="com.test.dao.impl.UserDaoImpl"></bean>
      <bean class="com.test.dao.impl.UserDaoImpl"></bean>
      <bean class="com.test.dao.impl.UserDaoImpl"></bean>
   </list>
</property>

也可以直接引用容器中存在的Bean

<!--配置UserDao-->
<bean id="userDao" class="com.test.dao.impl.UserDaoImpl"/>
<bean id="userDao2" class="com.test.dao.impl.UserDaoImpl"/>
<bean id="userDao3" class="com.test.dao.impl.UserDaoImpl"/>

<!--配置UserService-->
<bean id="userService" class="com.test.service.impl.UserServiceImpl">
        <property name="objList">
               <list>
                   <ref bean="userDao"></ref>
                   <ref bean="userDao2"></ref>
                   <ref bean="userDao3"></ref>
               </list>
        </property>
</bean>

注入 Set 集合

//注入泛型为字符串的Set集合
public void setValueSet(Set<String> valueSet){
    
    
   valueSet.forEach(str->{
    
    
        System.out.println(str);
   });
}
//注入泛型为对象的Set集合
public void setObjSet(Set<UserDao> objSet){
    
    
   objSet.forEach(obj->{
    
    
        System.out.println(obj);
   });
}
<!-- 注入泛型为字符串的Set集合 -->
<property name="valueSet">
    <set>
       <value>111111</value>
       <value>222222</value>
    </set>
</property>
<!-- 注入泛型为对象的Set集合 -->
<property name="objSet">
    <set>
       <ref bean="userDao"></ref>
       <ref bean="userDao2"></ref>
       <ref bean="userDao3"></ref>
    </set>
</property>

注入 Map<K,V> 集合

//注入值为字符串的Map集合
public void setValueMap(Map<String,String> valueMap){
    
    
     valueMap.forEach((k,v)->{
    
    
        System.out.println(k+"=="+v);
     });
}
//注入值为对象的Map集合
public void setObjMap(Map<String,UserDao> objMap){
    
    
     objMap.forEach((k,v)->{
    
    
        System.out.println(k+"=="+v);
     });
}
<!--注入值为字符串的Map集合-->
<property name="valueMap">
   <map>
     <entry key="aaa" value="AAA" />
     <entry key="bbb" value="BBB" />
     <entry key="ccc" value="CCC" />
   </map>
</property>
<!--注入值为对象的Map集合-->
<property name="objMap">
   <map>
     <entry key="ud" value-ref="userDao"/>
     <entry key="ud2" value-ref="userDao2"/>
     <entry key="ud3" value-ref="userDao3"/>
   </map>
</property>

注入 Properties 键值对

//注入Properties
public void setProperties(Properties properties){
    
    
     properties.forEach((k,v)->{
    
    
        System.out.println(k+"=="+v);
     });
}
<property name="properties">
   <props>
      <prop key="xxx">XXX</prop>
      <prop key="yyy">YYY</prop>
   </props>
</property>

4.3 自动装配

如果被注入的属性类型是Bean引用的话,那么可以在<bean>标签中使用 autowire 属性去配置自动注入方式,属性值有两个:
byName:通过属性名自动装配,即去匹配 setXxx 与 id=“xxx”(name=“xxx”)是否一致;
byType:通过Bean的类型从容器中匹配,匹配出多个相同Bean类型时,报错。

<bean id="userService" class="com.test.service.impl.UserServiceImpl" autowire="byType">

5. Spring的其他配置标签

Spring 的 xml 标签大体上分为两类,一种是默认标签,一种是自定义标签

类型 描述
默认标签 就是不用额外导入其他命名空间约束的标签,例如 <bean> 标签
自定义标签 就是需要额外引入其他命名空间约束,并通过前缀引用的标签,例如 <context:property-placeholder/> 标签

Spring的默认标签用到的是Spring的默认命名空间

<?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">
</beans>

该命名空间约束下的默认标签如下:

标签 作用
<beans> 一般作为 xml 配置根标签,其他标签都是该标签的子标签
<bean> Bean的配置标签,上面已经详解了,此处不再阐述
<import> 外部资源导入标签
<alias> 指定Bean的别名标签,使用较少

<beans>标签,除了经常用的做为根标签外,还可以嵌套在根标签内,使用profile属性切换开发环境

可以使用以下两种方式指定被激活的环境:
使用命令行动态参数,虚拟机参数位置加载 -Dspring.profiles.active=test
使用代码的方式设置环境变量,System.setProperty(“spring.profiles.active”,“test”)

<!-- 配置测试环境下,需要加载的Bean实例 -->
<beans profile="test"></beans>
<!-- 配置开发环境下,需要加载的Bean实例 -->
<beans profile="dev"></beans>

<import>标签,用于导入其他配置文件,项目变大后,就会导致一个配置文件内容过多,可以将一个配置文件根据业务某块进行拆分,拆分后,最终通过<import>标签导入到一个主配置文件中,项目加载主配置文件就连同<import> 导入的文件一并加载了

<!--导入用户模块配置文件-->
<import resource="classpath:applicationContext-user.xml"/>
<!--导入商品模块配置文件-->
<import resource="classpath:applicationContext-orders.xml"/>

<alias> 标签是为某个Bean添加别名,与在<bean> 标签上使用name属性添加别名的方式一样,我们为UserServiceImpl指定四个别名:aaa、bbb、xxx、yyy

<!--配置UserService-->
<bean id="userService" name="aaa,bbb" class="com.test.service.impl.UserServiceImpl">
     <property name="userDao" ref="userDao"/>
</bean>
<!--指定别名-->
<alias name="userService" alias="xxx"/>
<alias name="userService" alias="yyy"/>

断点调试,在beanFactory中维护着一个名为aliasMap的Map<String,String>集合,存储别名和beanName之间的映射关系
在这里插入图片描述
Spring的自定义标签需要引入外部的命名空间,并为外部的命名空间指定前缀,使用 <前缀:标签> 形式的标签,称之为自定义标签,自定义标签的解析流程也是 Spring xml扩展点方式之一,在后续整合第三方框架章节进行详细介绍

<!--默认标签-->
<bean id="userDao" class="com.test.dao.impl.UserDaoImpl"/>
<!--自定义标签-->
<context:property-placeholder/>
<mvc:annotation-driven/>
<dubbo:application name="application"/>

猜你喜欢

转载自blog.csdn.net/ChennyWJS/article/details/132036420