Spring Bean的定义、作用域

  被称作Bean的对象是构成应用程序的基础,是由Spring的IOC容器进行管理的,而对于一个Bean通常包含一下属性:

属性 描述
class 完全限定名,这个属性是强制性的,并且指定用来创建 bean 的 bean 类。
name 这个属性指定唯一的 bean 标识符。在基于 XML 的配置元数据中,你可以使用 ID 和/或 name 属性来指定 bean 标识符。
scope 这个属性指定由特定的 bean 定义创建的对象的作用域,它将会在 bean 作用域的章节中进行讨论。
constructor-arg 它是用来注入依赖关系的,并会在接下来的章节中进行讨论。
properties 它是用来注入依赖关系的,并会在接下来的章节中进行讨论。
autowiring mode 它是用来注入依赖关系的,并会在接下来的章节中进行讨论。
lazy-initialization mode 延迟初始化的 bean 告诉 IoC 容器在它第一次被请求时,而不是在启动时去创建一个 bean 实例。
initialization 方法 在 bean 的所有必需的属性被容器设置之后,调用回调方法。它将会在 bean 的生命周期章节中进行讨论。
destruction 方法 当包含该 bean 的容器被销毁时,使用回调方法。它将会在 bean 的生命周期章节中进行讨论。

  Bean的标识符可以不用显示的配置,Spring会默认的根据Bean对象的名称按照标准的驼峰命名法来设置Bean的标识符,如accountService、userDao等;但是,在(不寻常的)特殊情况下,如果有多个字符,并且第一个和第二个字符都是大写的,则保留原来的大小写。

  Bean的作用域

  当在 Spring 中定义一个 bean 时,你必须声明该 bean 的作用域的选项。例如,为了强制 Spring 在每次需要时都产生一个新的 bean 实例,你应该声明 bean 的作用域的属性为 prototype。同理,如果你想让 Spring 在每次需要时都返回同一个bean实例,你应该声明 bean 的作用域的属性为 singleton

  Spring 框架支持以下五个作用域,分别为singleton、prototype、request、session和global session,5种作用域说明如下所示,注意,如果你使用 web-aware ApplicationContext 时,其中三个是可用的。

  

作用域 描述
singleton

在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,默认值

prototype 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean() 
request 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境
session 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境
global-session 一般用于Portlet应用环境,该运用域仅适用于WebApplicationContext环境

  下面,将讨论前两个作用域的使用。为了方便测试,我们需要重新创建一个POJO(LifeScope.java)和测试方法,代码如下:

/**
 * @author johnson liu
 * @description Bean的生命周期测试POJO类
 */
public class LifeScope {
    private String name;

    public void getName(int num) {
        System.out.println("Your name :"+name+">>>"+num);
    }
    public void setName(String name) {
        this.name = name;
    }
}

  定义接口LifeService.java和其实现类LifeServiceImpl.java:

package scope;

import quickstart.LifeScope;

public interface LifeService {
    /**
     *
     * @param lifeScope 作用域测试
     */
    void setLife(LifeScope lifeScope);

    LifeScope getLife();
}
package scope;

import quickstart.LifeScope;
/**
 * @author johnson liu
 * @description Bean生命周期测试
 */
public class LifeServiceImpl implements LifeService {

    private LifeScope lifeScope;
    /**
     * @param lifeScope 作用域测试
     */
    public void setLife(LifeScope lifeScope) {
        this.lifeScope=lifeScope;
    }
    public LifeScope getLife(){
        return this.lifeScope;
    }
}

  为了测试Spring容器实例化的Bean是单例还是多例,还需要创建一个简单的多线程类来测试(通过多线程,我们可以获取实现类的多个实例,输出该实例,如果实例对象的引用地址一样,则是单例,否则,属于多例;其次,为了方便区别这两种模式用法上的不同,在线程的内部创建了私有变量num,以此来区分是哪个线程进行了输出):

package scope;

import org.springframework.context.ApplicationContext;
import quickstart.LifeScope;

import java.util.Random;
/**
 * @author johnson liu
 * @description
 */
public class LifeThread extends Thread {
    private ApplicationContext context;
    private LifeScope lifeScope;
    private Integer num;
    public LifeThread(){
    }
    public LifeThread(int num,ApplicationContext context,LifeScope lifeScope){
        this.context=context;
        this.lifeScope=lifeScope;
        this.num=num;
    }
    @Override
    public void run() {
        long time=(long)(Math.random()*1000+1);
        try {
            LifeServiceImpl lifeService=((LifeServiceImpl)context.getBean("lifeService"));
            System.out.println(lifeService);
            lifeService.setLife(lifeScope);
            Thread.sleep(time);
            LifeScope returnLifeScope= lifeService.getLife();
            returnLifeScope.getName(num);
        }catch (Exception e){

        }
    }
}

  配置Beans.xml,让LifeServiceImpl被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">
    <bean id="helloWorld" class="quickstart.HelloWorld">
        <!--<constructor-arg name="message" value="使用xml进行构造函数注入"></constructor-arg>-->
        <property name="message" value="hello world"></property>
    </bean>

    <!--<bean id="lifeService" class="scope.LifeServiceImpl" scope="prototype"></bean>-->
    <bean id="lifeService" class="scope.LifeServiceImpl" scope="singleton"></bean>

</beans>

  编写测试代码:

package quickstart;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import scope.LifeServiceImpl;
import scope.LifeThread;

public class MainTest {

    public static void main(String[] args){
        /**
         * ApplicationContext 是一个接口,负责读取Spring的配置文件,管理对象的加载、生成;维护Bean对象与Bean对象之间的依赖关系,负责Bean的生命周期等。
         * ClassPathXmlApplicationContext、FileSystemXmlApplicationContext、AnnotationConfigApplicationContext等是ApplicationContext接口的实现类
         * ClassPathXmlApplicationContext用于从classpath路径下读取Spring的配置文件
         * FileSystemXmlApplicationContext用于从文件系统中加载应用上下文
         * AnnotationConfigApplicationContext用于从Java类中加载应用上下文
         */
        ApplicationContext context=new ClassPathXmlApplicationContext("Beans.xml");//从类路径中加载应用上下文
//        ApplicationContext context=new FileSystemXmlApplicationContext("D:\\IdeaProjects\\java\\spring\\core\\src\\main\\resources\\Beans.xml");//从文件系统中加载应用上下文
        //ApplicationContext context=new AnnotationConfigApplicationContext("quickstart");//从Java类中加载应用上下文
        /*HelloWorld helloWorld=(HelloWorld)context.getBean("helloWorld");
        helloWorld.getMessage();*/

        LifeServiceImpl lifeService=(LifeServiceImpl)context.getBean("lifeService");
        LifeScope lifeScope1=new LifeScope();
        lifeScope1.setName("Scope测试1");

        LifeScope lifeScope2=new LifeScope();
        lifeScope2.setName("Scope测试2");

        LifeScope lifeScope3=new LifeScope();
        lifeScope3.setName("Scope测试3");

        LifeScope lifeScope4=new LifeScope();
        lifeScope4.setName("Scope测试4");

        LifeScope lifeScope5=new LifeScope();
        lifeScope5.setName("Scope测试5");

        LifeScope lifeScope6=new LifeScope();
        lifeScope6.setName("Scope测试6");

        new LifeThread(1,context,lifeScope1).start();
        new LifeThread(2,context,lifeScope2).start();
        new LifeThread(3,context,lifeScope3).start();
        new LifeThread(4,context,lifeScope4).start();
        new LifeThread(5,context,lifeScope5).start();
        new LifeThread(6,context,lifeScope6).start();
    }
}

  此时,我们使用的是singleton作用域,其测试结果如下:

  从测试结果中可以发现,我们获取的Bean实例对象是同一个对象,但是在通过bean实例对象的私有属性赋值后输出,其得到的值并不是当前线程设置的值。下面我们再来看将bean的作用域修改为scope="prototype"后的结果:

<bean id="lifeService" class="scope.LifeServiceImpl" scope="prototype"></bean>//将以上beans.xml中的这段代码行取消注释

  从以上测试结果我们发现在多线程下,多例模式是线程安全的。通常这类Bean中含有全局的私有属性的Bean,我们称之为有状态Bean,反之为无状态的Bean。
  因此,对于有状态的这类bean,在定义时,我们一定要将其作用域声明为prototype模式。

 

 

 

 

猜你喜欢

转载自www.cnblogs.com/onedayinMay/p/12501203.html