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

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

语法

  1. <bean id="bean名称" name="bean名称或者别名" class="bean的完整类型名称">
  2. <constructor-arg index="0" value="bean的值" ref="引用的bean名称" />
  3. <constructor-arg index="1" value="bean的值" ref="引用的bean名称" />
  4. <constructor-arg index="2" value="bean的值" ref="引用的bean名称" />
  5. ....
  6. <constructor-arg index="n" value="bean的值" ref="引用的bean名称" />
  7. </bean>

constructor-arg用于指定构造方法参数的值 index:构造方法中参数的位置,从0开始,依次递增 value:指定参数的值 ref:当插入的值为容器内其他bean的时候,这个值为容器中对应bean的名称

案例

UserModel类
  1. @Getter
  2. @Setter
  3. @ToString
  4. public class UserModel {
  5. private String name;
  6. private int age;
  7. public UserModel() {
  8. this.name = "我是通过UserModel的无参构造方法创建的!";
  9. }
  10. public UserModel(String name, int age) {
  11. this.name = name;
  12. this.age = age;
  13. }
  14. }

beans.xml配置
  1. <!-- 通过UserModel的默认构造方法创建UserModel对象 -->
  2. <bean id="createBeanByConstructor1" class="com.javacode2018.lesson001.demo3.UserModel"/>
  3. <!-- 通过UserModel有参构造方法创建UserModel对象 -->
  4. <bean id="createBeanByConstructor2" class="com.javacode2018.lesson001.demo3.UserModel">
  5. <constructor-arg index="0" value="我是通过UserModel的有参方法构造的对象!"/>
  6. <constructor-arg index="1" value="30"/>
  7. </bean>

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

测试用例
  1. package com.javacode2018.lesson001.demo3;
  2. import org.springframework.context.support.ClassPathXmlApplicationContext;
  3. import java.net.URL;
  4. import java.net.URLClassLoader;
  5. import java.util.Arrays;
  6. /**
  7. * 公众号:程序员路人
  8. */
  9. public class Client {
  10. public static void main(String[] args) {
  11. //1.bean配置文件位置
  12. String beanXml = "classpath:/com/javacode2018/lesson001/demo3/beans.xml";
  13. //2.创建ClassPathXmlApplicationContext容器,给容器指定需要加载的bean配置文件
  14. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(beanXml);
  15. System.out.println("spring容器中所有bean如下:");
  16. //getBeanDefinitionNames用于获取容器中所有bean的名称
  17. for (String beanName : context.getBeanDefinitionNames()) {
  18. System.out.println(beanName + ":" + context.getBean(beanName));
  19. }
  20. }
  21. }

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

运行输出
  1. spring容器中所有bean如下:
  2. createBeanByConstructor1:UserModel(name=我是通过UserModel的无参构造方法创建的!, age=0)
  3. createBeanByConstructor2:UserModel(name=我是通过UserModel的有参方法构造的对象!, age=30)

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

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

语法

  1. <bean id="bean名称" name="" class="静态工厂完整类名" factory-method="静态工厂的方法">
  2. <constructor-arg index="0" value="bean的值" ref="引用的bean名称" />
  3. <constructor-arg index="1" value="bean的值" ref="引用的bean名称" />
  4. <constructor-arg index="2" value="bean的值" ref="引用的bean名称" />
  5. ....
  6. <constructor-arg index="n" value="bean的值" ref="引用的bean名称" />
  7. </bean>

class:指定静态工厂完整的类名 factory-method:静态工厂中的静态方法,返回需要的对象。 constructor-arg用于指定静态方法参数的值,用法和上面介绍的构造方法一样。

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

案例

定义静态工厂

创建一个静态工厂类,用于生成UserModel对象。

  1. package com.javacode2018.lesson001.demo3;
  2. /**
  3. * 公众号:程序员路人
  4. */
  5. public class UserStaticFactory {
  6. /**
  7. * 静态无参方法创建UserModel
  8. *
  9. * @return
  10. */
  11. public static UserModel buildUser1() {
  12. System.out.println(UserStaticFactory.class + ".buildUser1()");
  13. UserModel userModel = new UserModel();
  14. userModel.setName("我是无参静态构造方法创建的!");
  15. return userModel;
  16. }
  17. /**
  18. * 静态有参方法创建UserModel
  19. *
  20. * @param name 名称
  21. * @param age 年龄
  22. * @return
  23. */
  24. public static UserModel buildUser2(String name, int age) {
  25. System.out.println(UserStaticFactory.class + ".buildUser2()");
  26. UserModel userModel = new UserModel();
  27. userModel.setName(name);
  28. userModel.setAge(age);
  29. return userModel;
  30. }
  31. }

beans.xml配置
  1. <!-- 通过工厂静态无参方法创建bean对象 -->
  2. <bean id="createBeanByStaticFactoryMethod1" class="com.javacode2018.lesson001.demo3.UserStaticFactory"
  3. factory-method="buildUser1"/>
  4. <!-- 通过工厂静态有参方法创建bean对象 -->
  5. <bean id="createBeanByStaticFactoryMethod2" class="com.javacode2018.lesson001.demo3.UserStaticFactory"
  6. factory-method="buildUser2">
  7. <constructor-arg index="0" value="通过工厂静态有参方法创建UerModel实例对象"/>
  8. <constructor-arg index="1" value="30"/>
  9. </bean>

上面配置中,spring容器启动的时候会自动调用UserStaticFactory中的buildUser1静态方法获取UserModel对象,将其作为createBeanByStaticFactoryMethod1名称对应的bean对象放在spring容器中。
会调用UserStaticFactory的buildUser2方法,并且会传入2个指定的参数,得到返回的UserModel对象,将其作为createBeanByStaticFactoryMethod2名称对应的bean对象放在spring容器中。

运行Client
  1. class com.javacode2018.lesson001.demo3.UserStaticFactory.buildUser1()
  2. class com.javacode2018.lesson001.demo3.UserStaticFactory.buildUser2()
  3. spring容器中所有bean如下:
  4. createBeanByStaticFactoryMethod1:UserModel(name=我是无参静态构造方法创建的!, age=0)
  5. createBeanByStaticFactoryMethod2:UserModel(name=通过工厂静态有参方法创建UerModel实例对象, age=30)

从输出中可以看出,两个静态方法都被调用了,createBeanByStaticFactoryMethod1对应的bean对象是通过buildUser1方法创建的;createBeanByStaticFactoryMethod2对应的bean对象是通过buildUser2方法创建的。

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

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

语法

  1. <bean id="bean名称" factory-bean="需要调用的实例对象bean名称" factory-method="bean对象中的方法">
  2. <constructor-arg index="0" value="bean的值" ref="引用的bean名称" />
  3. <constructor-arg index="1" value="bean的值" ref="引用的bean名称" />
  4. <constructor-arg index="2" value="bean的值" ref="引用的bean名称" />
  5. ....
  6. <constructor-arg index="n" value="bean的值" ref="引用的bean名称" />
  7. </bean>

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

案例

定义一个实例工厂

内部写2个方法用来创建UserModel对象。

  1. package com.javacode2018.lesson001.demo3;
  2. /**
  3. * 公众号:程序员路人
  4. */
  5. public class UserFactory {
  6. public UserModel buildUser1() {
  7. System.out.println("----------------------1");
  8. UserModel userModel = new UserModel();
  9. userModel.setName("bean实例方法创建的对象!");
  10. return userModel;
  11. }
  12. public UserModel buildUser2(String name, int age) {
  13. System.out.println("----------------------2");
  14. UserModel userModel = new UserModel();
  15. userModel.setName(name);
  16. userModel.setAge(age);
  17. return userModel;
  18. }
  19. }

beans.xml
  1. <!-- 定义一个工厂实例 -->
  2. <bean id="userFactory" class="com.javacode2018.lesson001.demo3.UserFactory"/>
  3. <!-- 通过userFactory实例的无参user方法创建UserModel对象 -->
  4. <bean id="createBeanByBeanMethod1" factory-bean="userFactory" factory-method="buildUser1"/>
  5. <!-- 通过userFactory实例的有参user方法创建UserModel对象 -->
  6. <bean id="createBeanByBeanMethod2" factory-bean="userFactory" factory-method="buildUser2">
  7. <constructor-arg index="0" value="通过bean实例有参方法创建UserModel实例对象"/>
  8. <constructor-arg index="1" value="30"/>
  9. </bean>

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

运行Client
  1. spring容器中所有bean如下:
  2. createBeanByBeanMethod1:UserModel(name=bean实例方法创建的对象!, age=0)
  3. createBeanByBeanMethod2:UserModel(name=通过bean实例有参方法创建UserModel实例对象, age=30)

通过FactoryBean来创建bean对象

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

  1. public interface FactoryBean<T> {
  2. /**
  3. * 返回创建好的对象
  4. */
  5. @Nullable
  6. T getObject() throws Exception;
  7. /**
  8. * 返回需要创建的对象的类型
  9. */
  10. @Nullable
  11. Class<?> getObjectType();
  12. /**
  13. * bean是否是单例的
  14. **/
  15. default boolean isSingleton() {
  16. return true;
  17. }
  18. }

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

语法

  1. <bean id="bean名称" class="FactoryBean接口实现类" />

案例

创建一个FactoryBean实现类
  1. package com.javacode2018.lesson001.demo3;
  2. import org.springframework.beans.factory.FactoryBean;
  3. import org.springframework.lang.Nullable;
  4. /**
  5. * 公众号:程序员路人
  6. */
  7. public class UserFactoryBean implements FactoryBean<UserModel> {
  8. int count = 1;
  9. @Nullable
  10. @Override
  11. public UserModel getObject() throws Exception { //@1
  12. UserModel userModel = new UserModel();
  13. userModel.setName("我是通过FactoryBean创建的第"+count+++ "对象");//@4
  14. return userModel;
  15. }
  16. @Nullable
  17. @Override
  18. public Class<?> getObjectType() {
  19. return UserModel.class; //@2
  20. }
  21. @Override
  22. public boolean isSingleton() {
  23. return true; //@3
  24. }
  25. }

@1:返回了一个创建好的UserModel对象
@2:返回对象的Class对象
@3:返回true,表示创建的对象是单例的,那么我们每次从容器中获取这个对象的时候都是同一个对象
@4:此处用到了一个count,通过这个一会可以看出isSingleton不同返回值的时候从容器获取的bean是否是同一个

bean xml配置
  1. <!-- 通过FactoryBean 创建UserModel对象 -->
  2. <bean id="createByFactoryBean" class="com.javacode2018.lesson001.demo3.UserFactoryBean"/>

Client代码
  1. package com.javacode2018.lesson001.demo3;
  2. import org.springframework.context.support.ClassPathXmlApplicationContext;
  3. import java.net.URL;
  4. import java.net.URLClassLoader;
  5. import java.util.Arrays;
  6. /**
  7. * 公众号:程序员路人
  8. */
  9. public class Client {
  10. public static void main(String[] args) {
  11. //1.bean配置文件位置
  12. String beanXml = "classpath:/com/javacode2018/lesson001/demo3/beans.xml";
  13. //2.创建ClassPathXmlApplicationContext容器,给容器指定需要加载的bean配置文件
  14. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(beanXml);
  15. System.out.println("spring容器中所有bean如下:");
  16. //getBeanDefinitionNames用于获取容器中所有bean的名称
  17. for (String beanName : context.getBeanDefinitionNames()) {
  18. System.out.println(beanName + ":" + context.getBean(beanName));
  19. }
  20. System.out.println("--------------------------");
  21. //多次获取createByFactoryBean看看是否是同一个对象
  22. System.out.println("createByFactoryBean:" + context.getBean("createByFactoryBean"));
  23. System.out.println("createByFactoryBean:" + context.getBean("createByFactoryBean"));
  24. }
  25. }

运行输出

  1. class com.javacode2018.lesson001.demo3.UserStaticFactory.buildUser1()
  2. class com.javacode2018.lesson001.demo3.UserStaticFactory.buildUser2()
  3. ----------------------1
  4. ----------------------2
  5. spring容器中所有bean如下:
  6. createBeanByConstructor1:UserModel(name=我是通过UserModel的无参构造方法创建的!, age=0)
  7. createBeanByConstructor2:UserModel(name=我是通过UserModel的有参方法构造的对象!, age=30)
  8. createBeanByStaticFactoryMethod1:UserModel(name=我是无参静态构造方法创建的!, age=0)
  9. createBeanByStaticFactoryMethod2:UserModel(name=通过工厂静态有参方法创建UerModel实例对象, age=30)
  10. userFactory:com.javacode2018.lesson001.demo3.UserFactory@610694f1
  11. createBeanByBeanMethod1:UserModel(name=bean实例方法创建的对象!, age=0)
  12. createBeanByBeanMethod2:UserModel(name=通过bean实例有参方法创建UserModel实例对象, age=30)
  13. createByFactoryBean:UserModel(name=我是通过FactoryBean创建的第1对象, age=0)
  14. --------------------------
  15. createByFactoryBean:UserModel(name=我是通过FactoryBean创建的第1对象, age=0)
  16. createByFactoryBean:UserModel(name=我是通过FactoryBean创建的第1对象, age=0)

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

  1. @Override
  2. public boolean isSingleton() {
  3. return false;
  4. }

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

  1. createByFactoryBean:UserModel(name=我是通过FactoryBean创建的第1对象, age=0)
  2. --------------------------
  3. createByFactoryBean:UserModel(name=我是通过FactoryBean创建的第2对象, age=0)
  4. createByFactoryBean:UserModel(name=我是通过FactoryBean创建的第3对象, age=0)

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

总结

spring容器提供了4种创建bean实例的方式,除了构造函数的方式,其他几种方式可以让我们手动去控制对象的创建,这几种方式大家都掌握一下,能够灵活使用。