欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring创建实例bean的几种方式(静态工厂,实例工厂...)

程序员文章站 2022-05-21 23:02:20
...

用Spring容器创建bean的方式有以下几种:

  1. 容器调用需要实例化的类的构造函数,可以带参,也可以不带参数。
  2. 通过静态工厂模式。
  3. 通过实例工厂模式。

第一种

首先自己创建一个类

package cn.edou.createBean;

/**
 * @author 中森明菜
 * @create 2019-04-20 18:58
 */
public class FirstCreateBean {
    private String name;

    public FirstCreateBean() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

然后在配置文件中声明bean,有一个属性是name,注入value值

  <bean id="firstBean" class="cn.edou.createBean.FirstCreateBean">
        <property name="name" value="创建bean的第一种方式"></property>
    </bean>

测试运行

public class TestGetBean {
    public static void main(String[] args){
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        FirstCreateBean firstCreateBean = (FirstCreateBean) ac.getBean("firstBean");
        System.out.println(firstCreateBean.getName());
    }
}

小结:这一种实例化bean的方式是最常见的,它会在上下文加载配置文件的时候,读取配置文件中bean的信息并且实例化。(提示:BeanFactory与ApplicationContext的区别就是前者在调用getBean()的时候才会实例化bean,而后者在容器加载配置文件的时候就会实例化bean,前者属于延迟加载,后者的话缺点是bean非常多的状况下会占用空间,加载慢。

第二种(静态工厂方式创建bean)

首先我们模拟一个人的接口,然后创建实现这个接口的两个实现类,学生和老师;

  • 接口
public interface Person {
    public void zhize();
}
  • 实现类:Student
public class Student implements Person {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void zhize() {
        System.out.println(name+":读书");
    }
}
  • 实现类:Teacher
public class Teacher implements Person {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void zhize() {
        System.out.println(name+":教书");
    }
}
  • 静态工厂
  //静态工厂的静态方法,由它负责bean的实例化
    public static Person getInstance(String type){
        if("student".equals(type)){
            return new Student();
        }else{
            return new Teacher();
        }
    }
  • 配置文件
  	<bean id="teacher" class="cn.edou.createBean.SecondCreateBean" factory-method="getInstance">
        <constructor-arg value="teacher"/>
        <property name="name" value="我是老师"/>
    </bean>
    <bean id="student" class="cn.edou.createBean.SecondCreateBean" factory-method="getInstance">
        <constructor-arg value="student"/>
        <property name="name" value="我是学生"/>
    </bean>
  • 测试
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = (Student) ac.getBean("student");
        student.zhize();
        Teacher teacher = (Teacher) ac.getBean("teacher");
        teacher.zhize();
  • 结果
我是学生:读书
我是老师:教书

小结:用静态工厂来实例bean的话需要我们创建一个工厂类,提供一个静态的方法,此方法负责bean的实例化,上面的配置文件的参数详解:

 <constructor-arg value="teacher"/>
 静态工厂方法参数的注入,值是teacher
 然后在调用静态方法的时候会根据值来判断实例化哪个bean
 factory-method="getInstance"
 值为静态方法名

在配置bean的时候,class属性不是实例化bean的全限定类名,而是静态工厂类。

	<bean id="teacher" class="cn.edou.createBean.SecondCreateBean" factory-method="getInstance">
        ...
    </bean>

实例工厂方法创建bean

其实,实例工厂与静态工厂的区别就是前者需要在容器中将工厂实例化,后者不需要实例化工厂,直接调用静态方法即可,因为静态方法可以直接通过类名调用,所以不用将工厂实例化,那么例子也很简单,直接将上面的工厂类的方法中static去除,再改动一下配置文件。

  • 配置文件
	<bean id="factory" class="cn.edou.createBean.ThirdCreateBean"></bean>
    <bean id="fac_teacher" factory-bean="factory" factory-method="getInstance">
        <constructor-arg value="teacher"/>
        <property name="name" value="我是老师"/>
    </bean>
    <bean id="fac_student" factory-bean="factory" factory-method="getInstance">
        <constructor-arg value="student"/>
        <property name="name" value="我是学生"/>
    </bean>
  • 实例工厂类
public class ThirdCreateBean {
    public Person getInstance(String type){
        if("student".equals(type)){
            return new Student();
        }else{
            return new Teacher();
        }
    }
}

测试和结果都与静态工厂一样。

ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = (Student) ac.getBean("fac_student");
        student.zhize();
        Teacher teacher = (Teacher) ac.getBean("fac_teacher");
        teacher.zhize();
我是学生:读书
我是老师:教书

小结:需要注意的是在配置文件中,bean的factory-bean属性值是工厂id。

 factory-bean="factory" factory-method="getInstance"

彩蛋(通过实现FactoryBean接口来创建一个工厂)

工厂类实现org.springframework.beans.factory.FacotryBean接口,实现了这个接口的类,会添加三个未实现的方法,getObject(),getObjectType(),isSingleton()
实现FactoryBean接口的类不会被视为普通的Bean,Spring会自动检测,调用getObject方法获取Bean实例,而且配置文件中和普通bean的写法一样。

<bean id="factoryBean" class="cn.edou.createBean.FactoryBean"></bean>
  • 工厂类
public class FactoryBean implements org.springframework.beans.factory.FactoryBean {
    @Override
    public Object getObject() throws Exception {
        return new Person() {
            @Override
            public void zhize() {
                System.out.println("活着就好");
            }
        };
    }

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

    @Override
    public boolean isSingleton() {
        return false;
    }
}
  • 测试与结果
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        Person person = (Person) ac.getBean("factoryBean");
        person.zhize();
活着就好

总结:

什么时候我们会需要用到静态工厂或者实例化工厂来实例化bean?
当我们不希望由容器来负责bean的实例化,而由我们自己来掌控何时实例化bean的时候,我们会用到工厂。
在网上看的的总结也不错,大家可以看看
如何在Spring中不再使用Spring创建Bean实例,而是把Bean创建过程转移到开发者手中。

使用静态工厂方法创建实例时必须提供工厂类和产生实例的静态工厂方法。通过静态工厂方法创建实例时需要对Spring配置文件做如下改变;

  1. class属性不在是Bean实例的实现类,而是生成Bean实例的静态工厂类
  2. 使用factory-method指定生产Bean实例的静态工厂方法
  3. 如果静态工厂方法需要参数,使用元素为其配置

当我们指定Spring使用静态工厂方法来创建Bean实例时,Spring将先解析配置文件,并根据配置文件指定的信息,通过反射调用静态工厂类的静态工厂方法,并将该静态工厂方法的返回值作为Bean实例,在这个过程中,Spring不再负责创建Bean实例,Bean实例是由用户提供的静态工厂方法提供的。