Spring4.x 笔记(4):Bean 在容器中的装配-XML(Schema)

了解 Bean

  1. Spring 启动时读取应用程序提供的 Bean 配置信息,并在 Spring 容器中生成一份相应的 Bean 配置注册表,根据注册表实例化 Bean,装配好 Bean 之间的依赖关系。

  2. Bean 配置信息是 Bean 的元数据信息,主要有4个方面:

  • Bean 的实现类
  • Bean 的属性信息,如数据源的连接数、用户名、密码等
  • Bean 的依赖关系,Spring 根据依赖关系配置完成 Bean 之间的装配
  • Bean 的行为配置,如生命周期范围、生命周期各过程的回调函数等
  1. Bean 元数据信息在 Spring 容器中由 org.springframework.beans.factory.config.BeanDefinition 接口形成的 Bean 注册表,Spring 支持多种形式的 Bean 的配置方式。基于XML配置、基于注解配置、基于 Java Config 类配置、基于Croovy 动态语言配置

XML(Schema)形式的配置

Bean 的基本配置

装配一个 Bean
  1. 在 Spring 容器的配置文件中,定义一个简单的 Bean 的配置:一个 标签表示一个 Bean
<bean id="managerBean" name="managerBean"
      class="com.learning.spring.ioc.bean.xml.manager.ManagerBean"
      lazy-init="false" scope="singleton"
      init-method="init" destroy-method="destroy"
      autowire="no" 
      abstract="false" 
      parent="" depends-on=""
      factory-bean="" factory-method=""
/>
  1. bean 标签
属性描述
idBean 的名称。id 在 Ioc 容器中必须是唯一。id 是 xml 的特殊属性,需要符合其命名规范:必须字母开头、不能使用逗号空格等
nameBean 的名称,功能与 id 一致。name 没有字符上的限制,可以不唯一,但是定义在后面的会覆盖前面的。不指定,默认是全限定类名,建议使用 id
classBean 对应的实现类
scopeBean 的作用域,默认是 singleton,可选prototype、request等
lazy-init是否懒加载,默认 false
init-method指定初始化方法,与实现 InitializingBean 接口的 afterPropertiesSet 效果一致
destroy-method指定销毁方法,与实现 DisposableBean 接口的 destroy 效果一致
autowire自动装配,默认是 no 不启用。不建议使用
abstract是否抽象,默认是 flase。true-不实例化该 Bean,继承关系的父类Bean ,会设置这个属性
parent指定父 Bean
depends-on指定依赖
factory-bean指定工厂类,非静态工厂方法装配使用
factory-method指定工厂方法,工厂方法装配使用
Bean 的装配方式
  1. 构造函数装配
  • Bean 实体类
public class ManagerBean {

    private String name;

    public ManagerBean() {
        System.out.println("我被实例化了");
    }

    public void save() {
        System.out.println("~~ save 方法 ~~");
    }
}
  • XML 装配配置
<!--构造函数的方式-->
<bean id="managerBean" name="managerBean" class="com.learning.spring.ioc.bean.xml.manager.ManagerBean"/>
  1. 静态工厂方法装配
  • Bean 实体类与工厂
public class BeanManagerFactory {

    /**
     * 静态方法,创建 ManagerBean 对象
     *
     * @return
     */
    public static ManagerBean createManagerBean() {
        System.out.println("静态方法工厂方法");
        return new ManagerBean();
    }
}
  • XML 装配配置
<!--静态工厂方法,其实 createPersonService 内部还是调用的构造函数-->
<bean id="beanManagerFactory" class="com.learning.spring.ioc.bean.xml.manager.BeanManagerFactory"
      factory-method="createManagerBean"/>
  1. 非静态工厂方法装配
  • Bean 实体类与工厂
public class BeanManagerFactory {
    /**
     * 非静态方法,需要把 PersonServiceFactory 也交给容器管理
     *
     * @return
     */
    public ManagerBean managerBean() {
        System.out.println("工厂非静态方法");
        return new ManagerBean();
    }
}
  • XML 装配配置:先实例化工厂类,然后在实例化需要的 Bean
<!-- 实例工厂非静态方法:先实例化工厂类,然后在实例化需要的 Bean -->
<bean id="beanManagerFactory2" class="com.learning.spring.ioc.bean.xml.manager.BeanManagerFactory"/>
<bean id="personService3" factory-bean="beanManagerFactory2" factory-method="managerBean"/>
懒加载设置(ApplicationContext)
  1. 默认容器初始化就会加载,详见 Bean 生命周期相关博文
  2. lazy-init 配置指定懒加载,实现延迟初始化 Bean,实现只有第一次获取 getBean 时才会初始化
  3. <beans default-lazy-init="true"></beans> 实现所有 Bean 延迟初始化
  4. scope="prototype",默认不会随容器初始化而初始化,并且没法修改( lazy-init=“false” 无效),因为单例会缓存,而 prototype 每次都是新的
指定初始化与销毁方法
  1. init-method 指定初始化方法,在构造函数之后
  2. destroy-method 指定销毁方法,在容器销毁之前

依赖注入

属性注入
  1. 属性注入指通过 setXXX 方法注入 Bean 的属性值或依赖对象,最常用的方式。属性注入要求 Bean 提供一个默认的构造函数,并为需要注入的属性提供对应的 Setter 方法。
  2. Bean 实例类
public class BeanInjectionService {

    private FieldInjection fieldInjection;

    /**
     * 属性注入需要有 setXXX 方法
     *
     * @param fieldInjection
     */
    public void setFieldInjection(FieldInjection fieldInjection) {
        this.fieldInjection = fieldInjection;
    }
}

/**
 * 属性注入的Bean
 */
class FieldInjection {

    public void get() {
        System.out.println("调用 FieldInjection.get() 方法");
    }
}

  1. 依赖注入配置
<bean id="fieldInjection" class="com.learning.spring.ioc.bean.xml.injection.FieldInjection"/>

<bean id="beanInjectionService" class="com.learning.spring.ioc.bean.xml.injection.BeanInjectionService">
    <!--1- 属性注入-->
    <property name="fieldInjection" ref="fieldInjection"/>
</bean>
构造函数注入
  1. 构造函数注入,有几种方式,如按类型参数入参(type)、按索引匹配入参(index)、联合使用、通过自身类型反射匹配入参,其中最简单稳妥的是按索引匹配入参(index)或者类型索引联合使用。
  2. 构造函数注入示例
  • Bean 对象类
public class BeanInjectionService {

    private ConstructorInjection constructorInjection;

    /**
     * 构造函数注入
     *
     * @param constructorInjection
     */
    public BeanInjectionService(ConstructorInjection constructorInjection) {
        this.constructorInjection = constructorInjection;
    }
}

/**
 * 构造函数注入的 Bean
 */
class ConstructorInjection {

    public void done() {
        System.out.println("调用 ConstructorInjection.done() 方法");
    }
}
  • 构造函数注入配置
<bean id="constructorInjection" class="com.learning.spring.ioc.bean.xml.injection.ConstructorInjection"/>
    <bean id="beanInjectionService" class="com.learning.spring.ioc.bean.xml.injection.BeanInjectionService">
        <!--2- 构造函数注入-->
        <constructor-arg index="0" name="constructorInjection" ref="constructorInjection" />
    </bean>
构造函数实现注入,循环依赖问题
  1. Spring 容器能对构造函数配置的 Bean 进行实例化有一个前提,即 Bean 构造函数入参引用的对象必须已经准备就绪。由于这个机制,如果两个Bean 都采用构造函数注入,而且都通过构造函数入参引用对方,就会发生类似于线程死锁的循环依赖问题。

  2. 解决:构造函数注入改成属性注入就可以了

  3. 代码示例:Sam 与 Eason 相互依赖,导致 ApplicationContext 启动失败


class Sam {
    private Eason eason;

    public Sam(Eason eason) {
        this.eason = eason;
    }
}

class Eason {
    private Sam sam;

    public Eason(Sam sam) {
        this.sam = sam;
    }
}

<!--构造函数实现注入,循环依赖问题-->
<bean id="sam" class="com.learning.spring.ioc.bean.xml.injection.Sam">
    <constructor-arg ref="eason"/>
</bean>
<bean id="eason" class="com.learning.spring.ioc.bean.xml.injection.Eason">
    <constructor-arg ref="sam"/>
</bean>

各种注入参数的详细讲解

在Spring 配置文件中,可以将String、int等字面值注入 Bean 中,还可以将集合、Map等类型的数据注入 Bean ,此外还可以注入配置文件汇总其他定义的 Bean

  1. 字面量
  2. 引用其他Bean
  3. 内部 Bean
  4. null 值
  5. 级联属性
  6. 集合类型属性
  7. 简化配置方式
  8. 自动装配

Bean 之间的关系

  1. 继承:abstract、parent 属性
  2. 依赖: depends-on、ref 属性。不是直接的 ref 关系,但是间接关联,使用depends-on
  3. 引用:一个Bean 要引用另一个 Bean 的id属性,可以使用 idref 标签

Bean 的作用域

基本作用域
  1. singleton:在Spring Ioc 容器中仅存在一个Bean 实例,Bean 以单例的方式存在
  2. prototype:多实例。每次从容器中调用Bean 时,都返回一个新的实例。即每次调用getBean(),相当于执行new XxxBean
Web 的作用域
  1. request:每次Http请求都会创建一个新的 Bean。

  2. session:同一个Http session 共享一个 Bean。不同的 http Session 使用不同的 Bean

  3. global session:同一个全局的 Session 共享一个bean,一般用于 Portlet 应用环境

  4. 配置实现:
    配置 RequestContextListener 监听器,该监听器实现 ServletRequestListener 监听

<listener>
    <listener-class>
    org.springframework.web.context.request.RequestContextListener
    </listener-class>
</listener>

import 整合多个配置文件

FactoryBean 的作用

了解 FactoryBean
  1. 一般情况下,Spring 通过反射机制利用 bean 标签的class属性指定的实现类实例化 Bean 。某些时候实例化 Bean 的过程比较复杂,配置方式灵活性收到限制,编码的方式更合适。Spring 提供了一个 FactoryBean 工厂接口,用户通过该接口定制化 Bean 的逻辑

  2. FactoryBean 支持泛型,且有三个方法

方法描述
boolean isSingleton();确定由 FactoryBean 创建的Bean 的作用域
T getObject() throws Exception;返回由 FactoryBean 创建的 Bean 的类型
T getObject() throws Exception;返回由 FactoryBean 创建的 Bean 实例。如果 isSingleton()方法返回 true,该实例会放到 Spring 容器的单实例缓存池
FactoryBean 示例
  1. Bean 对象类
class CustomBean {

    public CustomBean() {
        System.out.println("CustomBean 实例化");
    }

    public void done() {
        System.out.println("调用了 CustomBean.done 方法");
    }
}
  1. 实现 FactoryBean 接口
public class CustomFactoryBean implements FactoryBean<CustomBean> {
    /**
     * 返回由 FactoryBean 创建的 Bean 实例。
     * 如果 isSingleton=true,该实例会放到 Spring 容器的单实例缓存池
     *
     * @return
     * @throws Exception
     */
    @Override
    public CustomBean getObject() throws Exception {
        return new CustomBean();
    }

    /**
     * 返回由 FactoryBean 创建的 Bean 的类型
     *
     * @return
     */
    @Override
    public Class<?> getObjectType() {
        return CustomBean.class;
    }

    /**
     * 确定由 FactoryBean 创建的Bean 的作用域
     *
     * @return
     */
    @Override
    public boolean isSingleton() {
        return true;
    }
}
  1. Bean 装配:不需要配置具体的Bean类,只需要配置 FactoryBean 的实现类
<bean id="customFactoryBean" class="com.learning.spring.ioc.bean.xml.factory.CustomFactoryBean"/>
  1. 简单运行:
  • 直接获取 Bean 实例对象
  • 获取 FactoryBean 实例对象 ,需要加上 &
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");

// 获取 CustomBean 的 bean 对象
CustomBean customBean = context.getBean("customFactoryBean", CustomBean.class);
customBean.done();

// 获取 FactoryBean 实例 ,需要加上 &
CustomFactoryBean customFactoryBean = context.getBean("&customFactoryBean", CustomFactoryBean.class);
customFactoryBean.getObject().done();

((ClassPathXmlApplicationContext) context).close();

参考

  1. 源码地址

Fork me on Gitee

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值