Spring学习笔记-IOC高级特性1-Bean的创建、作用域、初始化和析构

在这一部分,将学习Spring IOC容器的高级特性和内部机制,这将帮助我们提高开发Spring应用的效率。尽管这些特性可能不是经常使用,但它是全面和强大的容器必备的。也是Spring框架的其他模块的基础。

调用静态工厂方法创建Bean

问题

当你打算调用一个静态工厂方法在Spring IOC容器创建一个Bean,静态工厂方法的目的是在静态方法中封装创建过程。请求一个对象的客户只要调用这个方法,不需要了解创建的细节。

解决方案

Spring支持调用一个静态工厂方法创建Bean,这个方法应该在factory-method属性中指定。

工作原理

例如,可以编写ProductCreator.createProduct()静态工厂方法从一个预先定义的产品ID常见一种产品。

package com.zy.IOC;

/**
 * Created by leon on 2017/4/13.
 */
public class ProductCreator {
    public static Product createProduct(String productId){
        if("aaa".equals(productId)){
            return new Battery("AAA",2.5);
        }else if("cdrw".equals(productId)){
            return new Disc("CD-RW",1.5);
        }

        throw new IllegalArgumentException("Unknown product");
    }
}

声明一个静态工厂方法的创建的Bean,需要在Class属性中指定工厂方法的宿主类,在factory-method属性中指定工厂方法名称。最后,使用 <constructor-arg> 元素传递方法的参数。

    <bean id="aaa" class="com.zy.IOC.ProductCreator" factory-method="createProduct">
        <constructor-arg value="aaa"/>
    </bean>

    <bean id="cdrw" class="com.zy.IOC.ProductCreator" factory-method="createProduct">
        <constructor-arg value="cdrw"/>
    </bean>

如果工厂方法抛出异常,Spring将用BeanCreationException对其进行封装。

调用一个实例工厂方法创建Bean

解决方案

Bean实例在factory-bean 属性中指定,而工厂方法应该在factory-method属性中指定。

public class ProductCreator {

    private Map<String,Product> map;

    public void setMap(Map<String, Product> map) {
        this.map = map;
    }

    public Product createProduct1(String productId){
        Product product = map.get(productId);
        if(product != null){
            return product;
        }
        throw new IllegalArgumentException("Unknown product");
    }
} 

<bean id="productCreator" class="com.zy.IOC.ProductCreator">
        <property name="map">
            <map>
                <entry key="aaa">
                    <bean class="com.zy.IOC.Battery">
                        <property name="productId" value="AAA"/>
                        <property name="price" value="2.5"/>
                    </bean>
                </entry>
                <entry key="cdrw">
                    <bean class="com.zy.IOC.Disc">
                        <property name="productId" value="CD-RW"/>
                        <property name="price" value="1.5"/>
                    </bean>
                </entry>
            </map>
        </property>
    </bean>

    <bean id="aaa" class="com.zy.IOC.ProductCreator" factory-bean="productCreator"
          factory-method="createProduct1">
        <constructor-arg value="aaa"/>
    </bean>

    <bean id="cdrw" class="com.zy.IOC.ProductCreator" factory-bean="productCreator"
          factory-method="createProduct1">
        <constructor-arg value="cdrw"/>
    </bean>   

从静态字段中声明Bean

问题

当你打算从一个静态字段中声明IOC容器的一个Bean。在java中,常量值旺旺声明为静态字段。

解决方案

从静态字段声明Bean,可以使用内奸的工厂BeanFieldRetrievingFactoryBean, 或者<util:content>标记。

首先在Product类定义两个产品常量

public class Product {
    public static final Product AAA = new Battery("AAA",2.5);
    public static final Product CDRW = new Disc("CD-RW",1.5);
}

使用内建的工厂BeanFieldRetrievingFactoryBean,在static属性中指定完全限定名。

    <bean id="aaa" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
        <property name="staticField">
            <value>com.zy.IOC.Product.AAA</value>
        </property>
    </bean>

    <bean id="cdrw" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
        <property name="staticField">
            <value>com.zy.IOC.Product.CDRW</value>
        </property>
    </bean>

或者使用


    <util:constant id="aaa" static-field="com.zy.IOC.Product.AAA"/>

    <util:constant id="cdrw" static-field="com.zy.IOC.Product.CDRW"/>

从对象属性中声明Bean

问题

当你打算从一个对象属性或嵌套属性(也就是属性路径)中声明Spring IOC容器中的一个Bean。

解决方案

可以使用内建的BeanPropertyPathFactoryBean或者<util:property-path> 标记

创建ProductRanking类

public class ProductRanking {

    private Product bestSeller;

    public Product getBestSeller() {
        return bestSeller;
    }

    public void setBestSeller(Product bestSeller) {
        this.bestSeller = bestSeller;
    }
}

按以下方式

    <bean id="productRanking" class="com.zy.IOC.ProductRanking">
        <property name="bestSeller">
            <bean class="com.zy.IOC.Disc">
                <property name="productId" value="CD-RW"/>
                <property name="price" value="2.5"/>
            </bean>
        </property>
    </bean>

    <bean id="bestSeller" class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
        <property name="targetBeanName" value="productRanking"/>
        <property name="propertyPath" value="bestSeller"/>
    </bean>

    <!--或-->
    <!--<util:property-path id="bestSeller" path="productRanking.bestSeller"/>-->

可采用代码测试

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        Product bestSeller = (Product) context.getBean("bestSeller");
        System.out.println(bestSeller);
    }
}

设置Bean作用域

问题

当你在配置文件声明Bean时,实际定义了Bean创建的一个模板,而不是实际的Bean实例。当getBean()方法或者其他Bean的一个引用请求Bean时,Spring将根据Bean作用域(Scope)确定应该放回的Bean实例,有时候,必须为Bean设置正确的作用域而不是默认的作用域。

解决方案

Bean的作用域在bean标签的scope属性中设置。默认情况,Spring为IOC容器中声明的每个Bean创建一个实例,这个实例在整个IOC容器范围内共享。所有后续的getBean()调用和Bean引用都将放回这个独特的Bean实例,这种作用域(默认)称为singleton。下表列出所有有效的Bean作用域:

作用域描述
Singleton每个Spring IOC容器中创建一个Bean实例
Prototype每次请求时创建一个新的Bean实例
Request为每个Http请求创建一个Bean实例,仅在Web应用上下文中有效
Session为每个Http会话创建一个Bean实例,仅在Web应用上下文中有效
GlobalSession为全局Http会话创建一个Bean实例,仅在Web应用上下文中有效

以考虑购物车为例,首先创建了ShoppingCar类,在IOC容器中声明为Bean这里写图片描述
前面Bean的声明的结果是,两个顾客取得是用一个购物车实例:
这里写图片描述
只需将bean的scope属性设为”prototype”即可解决以上问题,即不用的顾客获得不同的购物车实例。

自定义Bean初始化和析构

问题

许多现实中的组件的使用之前都必须进行某种初始化任务。这种任务包括打开文件、打开网络/数据库连接、分配内存等。等自检的生命周期结束时,也必须执行相应的析构任务。所以,就有在Spring IOC容器中定义Bean初始化和析构的需求。

解决方案

除了Bean的注册之外,Spring IOC容器还负责管理Bean的生命周期,允许你在它们的生命期特定时点执行自定义任务。你的任务应该封装在回调方法中,由Spring IOC容器在合适的时候调用。
下面的列表展示了IOC容器管理bean的周期步骤。这个列表将随着IOC容器更多特性的引入而扩展。
1. 构造程序或者工厂方法创建Bean实例
2. 向Bean属性设置值和Bean引用
3. 调用初始化回调方法
4. Bean就绪
5. 容器关闭时,调用析构回调方法
Spring有三种识别初始化和析构回调方法的方式。
第一,你的Bean可以实现InitalizingBean和DisposableBean生命周期接口,并实现用于初始化和析构的afterPropertiesSet()和destory()方法。
第二,可以Bean声明中设置init-method和destory-method属性,指定回调方法名称。
第三,在Spring2.5或更高版本中,还可以使用 生命周期注解@PostConstruct和@PreDestroy注解初始化和析构回调方法。然后可以再IOC容器注册CommonAnnotationBeanPost Processor实例来调用这些回调方法。

 <bean id="cashier1" class="com.zy.IOC.Cashier">
     <property name="name" value="cashier1"/>
     <property name="path" value="/Users/leon/test/1.txt"/>
 </bean>
public class Cashier implements InitializingBean,DisposableBean{

    private String name;
    private String path;
    private BufferedWriter writer;

    public void afterPropertiesSet() throws Exception {
        openFile();
    }

    public void destroy() throws Exception {
        closeFile();
    }

* 设置init-method 和 destory-method属性 *
推荐使用这用方法,此时Cashier类就不在需要实现InitializingBean和DisposableBean接口

    <bean id="cashier1" class="com.zy.IOC.Cashier" init-method="openFile" destroy-method="closeFile">
        <property name="name" value="cashier1"/>
        <property name="path" value="/Users/leon/test/1.txt"/>
    </bean>

* 使用@PostContstruct 和 @PreDestory注解 *
同样可以使用JSR-250生命周期注解@PostContstruct 和 @PreDestory注解初始化和析构回调方法。

    @PostConstruct
    private void openFile(){
        System.out.println("A file opened");
    }

    @PreDestroy
    private void closeFile(){
        System.out.println("The file closed");
    }

接下来,要在IOC容器中注册一个CommonAnnotationPostProcessor实例,调用带有生命周期实例注解的初始化和析构回调方法。这样就不需要指定Bean的init-method和destory-method属性了。

<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>

<bean id="cashier1" class="com.zy.IOC.Cashier">
    <property name="name" value="cashier1"/>
    <property name="path" value="/Users/leon/test/1.txt"/>
</bean>

也可以简单的在Bean配置文件中包含<context:annotation-config/>元素,自动注册CommonAnnotationPostProcessor实例。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值