《深入浅出Spring》创建bean 实例的方式

创建bean实例的方式

Spring容器内部创建bean实例对象常见的有4种方式。

  • 通过反射调用构造方法创建bean对象
  • 通过静态工厂方法创建bean对象
  • 通过实例工厂方法创建bean对象
  • 通过FactoryBean创建bean对象

通过反射调用构造方法创建bean对象

调用类的构造方法获取对应的bean实例,是使用最多的方式,这种方式只需要在xml bean元素中指定class属性,spring容器内部会自动调用该类型的构造方法来创建bean对象,将其放在容器中以供使用。

  • 语法
<bean id="bean名称" name="bean名称或者别名" class="bean的完整类型名称">
    <constructor-arg index="0" value="bean的值" ref="引用的bean名称" />
    <constructor-arg index="1" value="bean的值" ref="引用的bean名称" />
    <constructor-arg index="2" value="bean的值" ref="引用的bean名称" />
    ....
    <constructor-arg index="n" value="bean的值" ref="引用的bean名称" />
</bean>
  • constructor-arg用于指定构造方法参数的值

  • index:构造方法中参数的位置,从0开始,依次递增

  • value:指定参数的值

  • ref:当插入的值为容器内其他bean的时候,这个值为容器中对应bean的名称
    栗子:

package com.yuan11;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

/**
 * @title: UserModel
 * @Author yuan11
 * @Date: 2022/6/1 23:09
 * @Version 1.0
 */
@Getter
@Setter
@ToString
public class UserModel {
    private String name;
    private int age;
    public UserModel() {
        this.name = "我是通过UserModel的无参构造方法创建的!";
    }
    public UserModel(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
  • bean.xml
<!-- 通过UserModel的默认构造方法创建UserModel对象 -->
    <bean id="createBeanByConstructor1" class="com.yuan11.UserModel"/>
    <!-- 通过UserModel有参构造方法创建UserModel对象 -->
    <bean id="createBeanByConstructor2" class="com.yuan11.UserModel">
        <constructor-arg index="0" value="我是通过UserModel的有参方法构造的对象!"/>
        <constructor-arg index="1" value="30"/>
    </bean>

上面这2种写法,spring容器创建这两个UserModel的时候,都会通过反射的方式去调用UserModel类中对应的构造函数来创建UserModel对象。

  • Test
package com.yuan11;

import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @title: TestUser
 * @Author yuan11
 * @Date: 2022/6/1 23:10
 * @Version 1.0
 */
public class TestUser {
    public static void main(String[] args) {
        String beanXml = "classpath:bean.xml";
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(beanXml);

        for (String beanName : context.getBeanDefinitionNames()) {
            //获取bean的别名
            String[] aliases = context.getAliases(beanName);
            System.out.println(String.format("beanName:%s,[%s]", beanName, context.getBean(beanName)));
        }
    }
}

代码中会输出spring容器中所有bean的名称和其对应的bean对象。

  • 输出结果:
  • 在这里插入图片描述

通过静态工厂方法创建bean对象

我们可以创建静态工厂,内部提供一些静态方法来生成所需要的对象,将这些静态方法创建的对象交给spring以供使用。

  • 语法
<bean id="bean名称" name="" class="静态工厂完整类名" factory-method="静态工厂的方法">
    <constructor-arg index="0" value="bean的值" ref="引用的bean名称" />
    <constructor-arg index="1" value="bean的值" ref="引用的bean名称" />
    <constructor-arg index="2" value="bean的值" ref="引用的bean名称" />
    ....
    <constructor-arg index="n" value="bean的值" ref="引用的bean名称" />
</bean>
  • class:指定静态工厂完整的类名

  • factory-method:静态工厂中的静态方法,返回需要的对象。

  • constructor-arg用于指定静态方法参数的值,用法和上面介绍的构造方法一样

spring容器会自动调用静态工厂的静态方法获取指定的对象,将其放在容器中以供使用。

  • 栗子:
    定义静态工厂
    创建一个静态工厂类,用于生成UserModel对象。 两个构造方法都为静态方法
package com.yuan11;

/**
 * @title: UserFactory
 * @Author yuan11
 * @Date: 2022/6/5 23:08
 * @Version 1.0
 */
public class UserFactory {
    /**
     * 静态无参方法创建UserModel
     *
     * @return
     */
    public static UserModel buildUser1() {
        System.out.println(UserFactory.class + ".buildUser1()");
        UserModel userModel = new UserModel();
        userModel.setName("我是无参静态构造方法创建的!");
        return userModel;
    }
    /**
     * 静态有参方法创建UserModel
     *
     * @param name 名称
     * @param age  年龄
     * @return
     */
    public static UserModel buildUser2(String name, int age) {
        System.out.println(UserFactory.class + ".buildUser2()");
        UserModel userModel = new UserModel();
        userModel.setName(name);
        userModel.setAge(age);
        return userModel;
    }
}
  • bean.xml 配置
   <!-- 通过工厂静态无参方法创建bean对象 -->
    <bean id="createBeanByStaticFactoryMethod1" class="com.yuan11.UserFactory"
          factory-method="buildUser1"/>
    <!-- 通过工厂静态有参方法创建bean对象 -->
    <bean id="createBeanByStaticFactoryMethod2" class="com.yuan11.UserFactory"
          factory-method="buildUser2">
        <constructor-arg index="0" value="通过工厂静态有参方法创建UerModel实例对象"/>
        <constructor-arg index="1" value="30"/>
    </bean>

上面配置中,spring容器启动的时候会自动调用UserStaticFactory中的buildUser1静态方法获取UserModel对象,将其作为createBeanByStaticFactoryMethod1名称对应的bean对象放在spring容器中。

会调用UserStaticFactory的buildUser2方法,并且会传入2个指定的参数,得到返回的UserModel对象,将其作为createBeanByStaticFactoryMethod2名称对应的bean对象放在spring容器中

  • Test
    在这里插入图片描述
    从输出中可以看出,两个静态方法都被调用了,createBeanByStaticFactoryMethod1对应的bean对象是通过buildUser1方法创建的;createBeanByStaticFactoryMethod2对应的bean对象是通过buildUser2方法创建的。

通过实例工厂方法创建bean对象

让spring容器去调用某些对象的某些实例方法来生成bean对象放在容器中以供使用。

语法







spring容器以factory-bean的值为bean名称查找对应的bean对象,然后调用该对象中factory-method属性值指定的方法,将这个方法返回的对象作为当前bean对象放在容器中供使用。

  • 栗子
    定义一个实例工厂
    内部写2个方法用来创建UserModel对象。注意这里的方法不能定义为static
package com.yuan11;

/**
 * @title: UserFactory
 * @Author yuan11
 * @Date: 2022/6/5 23:08
 * @Version 1.0
 */
public class UserFactory {
    /**
     * 静态无参方法创建UserModel
     *
     * @return
     */
    public UserModel  buildUser1() {
        System.out.println("----------------------1");
        UserModel userModel = new UserModel();
        userModel.setName("我是无参静态构造方法创建的!");
        return userModel;
    }
    /**
     * 静态有参方法创建UserModel
     *
     * @param name 名称
     * @param age  年龄
     * @return
     */
    public UserModel  buildUser2(String name, int age) {
        System.out.println("----------------------2");
        UserModel userModel = new UserModel();
        userModel.setName(name);
        userModel.setAge(age);
        return userModel;
    }

}
  • beans.xml
 <!-- 定义一个工厂实例 -->
    <bean id="userFactory" class="com.yuan11.UserFactory"/>
    <!-- 通过userFactory实例的无参user方法创建UserModel对象 -->
    <bean id="createBeanByBeanMethod1" factory-bean="userFactory" factory-method="buildUser1"/>
    <!-- 通过userFactory实例的有参user方法创建UserModel对象 -->
    <bean id="createBeanByBeanMethod2" factory-bean="userFactory" factory-method="buildUser2">
        <constructor-arg index="0" value="通过bean实例有参方法创建UserModel实例对象"/>
        <constructor-arg index="1" value="30"/>
    </bean>
  • Test

在这里插入图片描述
createBeanByBeanMethod1对应的bean是通过userFactory的buildUser1方法生成的。

createBeanByBeanMethod2对应的bean是通过userFactory的buildUser2方法生成的。

UserFactory 是他自己注册为bean

通过FactoryBean来创建bean对象

前面我们学过了BeanFactory接口,BeanFactory是spring容器的顶层接口,而这里要说的是FactoryBean,也是一个接口,这两个接口很容易搞混淆,FactoryBean可以让spring容器通过这个接口的实现来创建我们需要的bean对象。

FactoryBean接口源码:

接口中有3个方法,前面2个方法需要我们去实现,getObject方法内部由开发者自己去实现对象的创建,然后将创建好的对象返回给Spring容器,getObjectType需要指定我们创建的bean的类型;最后一个方法isSingleton表示通过这个接口创建的对象是否是单例的,如果返回false,那么每次从容器中获取对象的时候都会调用这个接口的getObject() 去生成bean对象。

package com.yuan11;

import org.springframework.lang.Nullable;

/**
 * @title: FactoryBean
 * @Author yuan11
 * @Date: 2022/6/5 23:24
 * @Version 1.0
 */
public interface FactoryBean<T> {
    /**
     * 返回创建好的对象
     */
    @Nullable
    T getObject() throws Exception;
    /**
     * 返回需要创建的对象的类型
     */
    @Nullable
    Class<?> getObjectType();
    /**
     * bean是否是单例的
     **/
    default boolean isSingleton() {
        return true;
    }
}
  • 语法
<bean id="bean名称" class="FactoryBean接口实现类" />
  • 栗子
package com.yuan11;

import org.springframework.lang.Nullable;

/**
 * @title: UserFactoryBean
 * @Author yuan11
 * @Date: 2022/6/5 23:25
 * @Version 1.0
 */
public class UserFactoryBean implements FactoryBean<UserModel>{
    int count = 1;
    @Nullable
    @Override
    public UserModel getObject() throws Exception { //@1
        UserModel userModel = new UserModel();
        userModel.setName("我是通过FactoryBean创建的第"+count+++ "对象");//@4
        return userModel;
    }
    @Nullable
    @Override
    public Class<?> getObjectType() {
        return UserModel.class; //@2
    }
    @Override
    public boolean isSingleton() {
        return true; //@3
    }
}
  • @1:返回了一个创建好的UserModel对象

  • @2:返回对象的Class对象

  • @3:返回true,表示创建的对象是单例的,那么我们每次从容器中获取这个对象的时候都是同一个对象

  • @4:此处用到了一个count,通过这个一会可以看出isSingleton不同返回值的时候从容器获取的bean是否是同一个

  • bean xml配置

package com.yuan11;

import org.springframework.context.support.ClassPathXmlApplicationContext;

  • Test
package com.yuan11;

import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @title: TestUser
 * @Author yuan11
 * @Date: 2022/6/1 23:10
 * @Version 1.0
 */
public class TestUser {
    public static void main(String[] args) {
        String beanXml = "classpath:bean.xml";
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(beanXml);

        for (String beanName : context.getBeanDefinitionNames()) {
            //获取bean的别名
            System.out.println(beanName + ": " + context.getBean(beanName));
        }

        System.out.println("--------------------------");
        //多次获取createByFactoryBean看看是否是同一个对象
        System.out.println("createByFactoryBean:" + context.getBean("createByFactoryBean"));
        System.out.println("createByFactoryBean:" + context.getBean("createByFactoryBean"));
    }
}

在这里插入图片描述

注意最后4行输出,有3行输出的都是同一个createByFactoryBean,程序中通过getBean从spring容器中查找createByFactoryBean了3次,3次结果都是一样的,说明返回的都是同一个UserModel对象。

下面我们将UserFactoryBean中的isSingleton调整一下,返回false

@Override
public boolean isSingleton() {
    return false;
}

当这个方法返回false的时候,表示由这个FactoryBean创建的对象是多例的,那么我们每次从容器中getBean的时候都会去重新调用FactoryBean中的getObject方法获取一个新的对象。

再运行一下Client,最后4行输出:

在这里插入图片描述

这3次获取的对象不一样了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值