2. 声明简单的Bean

声明简单的Bean以及Bean的作用域

声明简单的Bean

示例:Chinese Idol 中国偶像选秀节目

下面是一个表演者的接口,其中有表演方法,当然,表演会有意外,需要抛出异常

package com.li.spring.chineseido;

public interface Performer {
    void perform() throws PerformanceException;
}

然后我们定义一个类:Juggler,为杂技表演者,该类继承表演者接口,并实现表演方法。

package com.li.spring.chineseidol;

import java.io.File;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Juggler implements Performer {
    private int balls=3;

    public int getBalls() {
        return balls;
    }

    public void setBalls(int balls) {
        this.balls = balls;
    }

    @Override
    public void perform() throws PerformanceException {
        System.out.println("JUGGLER "+balls+" BALLS");
    }

    public Juggler() {
        super();
    }

    public Juggler(int balls) {
        this.balls=balls;
    }

    public static void main(String[] args) {
        ApplicationContext ctx=new ClassPathXmlApplicationContext("com"+File.separator+"li"+File.separator+"spring"+File.separator+"chineseido"+File.separator+"chinese-ido.xml");
        Performer performer=(Performer) ctx.getBean("li");
        try {
            performer.perform();
        } catch (PerformanceException e) {
            e.printStackTrace();
        }
    }

}

在Spring中配置chinese-idol.xml中声明

<bean id="li" class="com.li.spring.chineseido.Juggler">
</bean>

Bean的名字由id属性定义,即这个Bean被称为 “li”,其中class指出对应的类,使用类的全限定名。当Spring容器加载该Bean时,Spring将使用默认的构造器来实例化 “li” Bean:

即,new com.li.spring.chineseidol.Juggle();

代码加载上下文:

ApplicationContext ctx=new ClassPathXmlApplicationContext("com"+File.separator+"li"+File.separator+"spring"+File.separator+"chineseido"+File.separator+"chinese-ido.xml");
Performer performer=(Performer) ctx.getBean("li");
try {
    performer.perform();
} catch (PerformanceException e) {
    e.printStackTrace();
}

其中 File.separator 是文件路径分隔符,也可用双反斜杠替代。其中,getBean()方法来获取Bean实例,运行结果:

JUGGLER 3 BALLS
通过构造器注入参数值

在Spring配置文件chiness-idol.xml中声明:

<bean id="li" class="com.li.spring.chineseidol.Juggler">
    <constructor-arg value="10"></constructor-arg>
</bean>

其中constructor-arg 表示构造参数,value值为10,这样会自动去调用有参构造方法。在构造Bean时,可以使用 <constructor-arg> 元素来告诉Spring额外的信息,即传递给有参构造方法的参数设置。运行结果:

JUGGLER 10 BALLS

当然,一个类可能会有多个构造方法,那么怎么确定来调用哪个构造方法呢?实际上,对构造器来说,一旦传入的参数个数以及类型确定了,那么构造方法也就确定了。<constructor-arg> 元素有index属性和type属性,分别表示参数的索引位置(按顺序从零开始)以及数据类型,指定这两个属性,便可以准确的确定调用的构造方法。

通过构造器注入对象引用

我们先定义一个接口,该接口是骑行这一动作,里面包含一个骑行方法

package com.li.spring.chineseidol;

public interface Ridable {
    void ride();
}

编写一个类,Bike,一个骑行的具体实现,实现了Ridable接口,用以装配Bean。

package com.li.spring.chineseidol;

public class Bike implements Ridable {

    @Override
    public void ride() {
        System.out.println("ride a bike...");
    }
}

创建一个骑行杂技表演者的类,该类继承Juggler类

package com.li.spring.chineseidol;

import java.io.File;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class RidableJuggler extends Juggler {

    /*这是一个接口,面向接口编程*/
    private Ridable ridable;

    public RidableJuggler(){
        super();
    }

    public RidableJuggler(int balls, Ridable ridable) {
        super(balls);
        this.ridable=ridable;
    }

    public RidableJuggler(Ridable ridable) {
        super();
        this.ridable=ridable;
    }

    @Override
    public void perform() throws PerformanceException {
        super.perform();
        System.out.println("while riding...");
        ridable.ride();
    }

    public static void main(String[] args) {
        ApplicationContext ctx=new ClassPathXmlApplicationContext("com"+File.separator+"li"+File.separator+"spring"+File.separator+"chineseidol"+File.separator+"chinese-idol.xml");
        Performer performer=(Performer) ctx.getBean("zhang");
        try {
            performer.perform();
        } catch (PerformanceException e) {
            e.printStackTrace();
        }

    }

}

通过构造器注入对象引用——XML配置

<bean id="bike" class="com.li.spring.chineseidol.Bike">
</bean>
<bean id="zhang" class="com.li.spring.chineseidol.RidableJuggler">
    <constructor-arg value="10"></constructor-arg>
    <constructor-arg ref="bike"></constructor-arg>
</bean>

该测试的执行逻辑是:

Ridable ridable=new Bike();
Performer performer=new RidableJuggler(10, ridable);

运行结果:

JUGGLER 10 BALLS
while riding...
ride a bike...
通过工厂方法创建Bean

有时静态工厂方法是实例化对象的唯一方法,Spring支持通过 <bean> 元素的factory-method属性来装配工厂创建的Bean。

示例:考虑Idol选秀过程中参赛者展示才艺的唯一舞台——Stage类型对象,采用单例模式,单例只能通过静态工厂方法来获取调用。

采用 initialication on demand holder 技术,这是单例模式的一种写法,完全使用了Java虚拟机的机制进行同步保证,没有一个同步的关键字,做到了延迟加载和线程安全。

package com.li.spring.chineseidol;

import java.io.File;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Stage {
    private Stage() {}

    /*采用 initialication on demand holder 技术*/

    private static class SingletonHolder{
        static Stage instance=new Stage();
    }

    /* 单例模式
     * 采用这种方法的好处就是:
     * 1. 延迟初始化
     * 2. 线程安全
     */
    public static Stage getInstance() {
        return SingletonHolder.instance;
    }

    public void show() {
        System.out.println("演出开始了");
    }

    public static void main(String[] args) {
        ApplicationContext ctx=new ClassPathXmlApplicationContext("com"+File.separator+"li"+File.separator+"spring"+File.separator+"chineseido"+File.separator+"chinese-ido.xml");
        Stage stage=(Stage) ctx.getBean("theStage");
        stage.show();

    }
}

Spring配置文件:

<bean id="theStage" class="com.li.spring.chineseidol.Stage"
        factory-method="getInstance">
</bean>

factory-method属性就是让Spring容器知道它并不是自己取调用构造方法去构造一个Bean的示例的,而是通过调用一个名为 “getInstance” 的静态工厂方法来获取实例的。

Bean作用域

Spring Bean默认都是单例(singleton)

配置Bean的scope属性为prototype,允许Bean可以被实例化多次(每次调用创建一个新的实例)

Spring的Bean作用域允许用户配置所创建的Bean属于哪种作用域,无需在Bean的实现里硬编码。

Scope what is does
singleton Scopes the bean definition to a single instance per Spring container.(default)
prototype Allows a bean to be instantiated any number of times(once per use)
request Scopes a bean definition to an HTTP request. Only valid when used with a web-capable Spring context(such as with Spring MVC)
session Scopes a bean definition to an HTTP session. Only valid when used with a web-capable Spring context(such as with Spring MVC)
global-session Scopes a bean definition to a global HTTP session. Only valid when used in a portlet context

初始化和销毁Bean

当初始化一个Bean时,可执行一些初始化操作来保证Bean处于可用状态。当不再需要Bean而将其从容器中移除时,可按顺序执行一些清除操作。

Spring提供Bean生命周期的构造方法:

  • <bean>元素的init-method属性指定在初始化Bean时要调用的方法。

  • <bean>元素的destroy-method属性指定将Bean从容器中移除前调用的方法。

示例

Idol选秀表演大厅Auditorium,在表演前要开灯,结束后要关灯。

package com.li.spring.chineseidol;

import java.io.File;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Auditorium {

    public void turnOnLight() {
        System.out.println("turn on the light!");
    }

    public void turnOffLight() {
        System.out.println("turn off the light!");
    }

    public static void main(String[] args) {
        /*上下文实例不一样了*/
        AbstractApplicationContext ctx=new ClassPathXmlApplicationContext("com"+File.separator+"li"+File.separator+"spring"+File.separator+"chineseidol"+File.separator+"chinese-idol.xml");
        /*获取Bean*/
        Auditorium au=(Auditorium)ctx.getBean("auditorium");
        /*手动的去移除这个Bean*/
        ctx.registerShutdownHook();
    }
}

Bean配置文件:

<bean id="auditorium" class="com.li.spring.chineseidol.Auditorium"
        init-method="turnOnLight" destroy-method="turnOffLight"></bean>

运行结果:

turn on the light!
turn off the light!

默认的 init-method 和 destroy-method

使用 <beans> 元素的 default-init-method 和 default-destroy-method 属性,可以为上下文中所有的Bean设置共同的初始化和销毁方法。(但是这种做法并不常用)

获取Spring上下文

获取Spring上下文使用的是 ApplicationContext 接口,一般来说我们可以通过其实现类来实例化它,其有两个实现类是值得我们关注的 ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext,当然这两个类也是很容易理解的,一个是从类路径下加载配置文件,一个是从计算机的文件系统上加载配置文件,一般来说使用 ClassPathXmlApplicationContext就可以了。

AbstractApplicationContext ctx=new ClassPathXmlApplicationContext("com"+File.separator+"li"+File.separator+"spring"+File.separator+"chineseidol"+File.separator+"chinese-idol.xml");

在上述代码中,我们在字符串中使用了File类的一个静态常量,这是一个分割符号,可以应对不同的系统。

如果我们在工程中写了多个配置文件呢?如果把它们统一加载到同一个Bean工厂中又是一个问题。此问题有两个解决方法。

第一种便是文件包含,即在一个配置文件中,引入其他所有配置文件,使用的是 <import>。

<beans>
    ......
    <import resource="classpath:com/li/student/student.xml"/>
    ......
</beans>

如上,该标签的resource表示了引入资源的路径,值里面classpath表示是在类路径下,位于com.li.student包下的student.xml文件。

第二种方法是在加载配置文件时,就加入多个配置文件,因为ClassPathXmlApplicationContext类还有一个构造方法:

public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
    ......
}

由此可见,这个构造方法可以接受多个String类型的参数,这时就可以加入多个配置文件的路径。

猜你喜欢

转载自blog.csdn.net/sinat_37976731/article/details/81361232
2.