前言:
-
Spring容器创建bean对象,一般通过反射机制查找bean元素的class属性值来找到要实例化的类,从而实例化bean对象。这便是调用构造方法来实例化bean对象
-
在某些情况下,若采用简单的xml配置文件方式,比如写大量的bean元素,会大大增加工作量。Spring容器还添加了一些bean元素的属性来减少配置文件的编写工作量。比如,静态工厂方法(factory-method属性)、实例化工厂方法(factory-bean属性、factory-method属性)
-
此外,Spring还提供了FactoryBean接口来支持开发人员自定义实例化bean对象的方式
案例源码:码云仓库的base-003子项目
1、调用构造方法创建bean对象
-
解释:
- 调用类的构造方法获取对应的bean实例
- 在配置文件中,只需设置好bean元素的class属性,Spring容器会自动调用构造方法来创建bean对象
-
基本格式:
<bean id="bean名称" name="bean名称或者别名" class="完整类路径"> <constructor-arg index="0" value="bean的值" ref="引用的bean名称" /> <constructor-arg index="1" value="bean的值" ref="引用的bean名称" /> .... </bean>
- constructor-arg:用于指定构造方法参数的值
- index:构造方法中参数的位置,从0开始,依次递增
- value:
- 给构造参数设置值,值的类型只能为简单类型,如byte,int,long,float,double,boolean,Byte,Long,Float,Double,枚举等;
- Spring容器在注入属性时,会自动将value值转换为对应的类型
- ref:当插入的值为容器内其他bean的时候,这个值为容器中对应bean的名称
- 若
不指定constructor-arg
,则Spring容器会调用默认无参构造方法
来创建bean对象;若指定constructor-arg
,则调用有参构造方法
来创建bean对象 - 若
指定constructor-arg
,index要和实体类的属性一一对应,不可缺少
- constructor-arg:用于指定构造方法参数的值
-
案例
-
实体类
package com.spring.study; public class Dog { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Dog() { this.name = "小白"; this.age = 1; } public Dog(String name, int age) { this.name = name; this.age = age; } }
-
配置文件
<?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对象--> <bean id="dog1" class="com.spring.study.Dog"/> <!-- 调用有参构造方法创建bean对象--> <bean id="dog2" class="com.spring.study.Dog"> <constructor-arg index="0" value="大黄"/> <constructor-arg index="1" value="2"/> </bean> </beans>
-
测试类
package com.spring.test; import com.spring.study.Dog; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestConstructor { public static void main(String[] args) { // 1、定义bean配置文件位置 String classPathXml = "classpath:applicationContext.xml"; // 2、创建ClassPathXmlApplicationContext容器,给容器指定需要加载的bean配置文件 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(classPathXml); // 3、获取容器中所有bean对象 //获取所有bean对象的bean名称 String[] beanNames = context.getBeanDefinitionNames(); //打印输出bean对象 for (String beanName : beanNames){ Dog dog = context.getBean(beanName,Dog.class); System.out.println(String.format("bean名称:%s,bean对象的name值:%s,bean对象的age值:%d",beanName,dog.getName(),dog.getAge())); } } }
-
运行结果
bean名称:dog1,bean对象的name值:小白,bean对象的age值:1 bean名称:dog2,bean对象的name值:大黄,bean对象的age值:2
-
2、静态工厂方法创建bean对象
-
解释:
- 创建静态工厂,内部提供一些静态方法来生成所需要的bean对象,将这些静态方法创建的对象交给spring容器以供使用
-
基本格式:
<bean id="bean名称" class="静态工厂完整类路径" factory-method="静态工厂的方法名"> <constructor-arg index="0" value="bean的值" ref="引用的bean名称" /> <constructor-arg index="1" value="bean的值" ref="引用的bean名称" /> .... </bean>
- factory-method:其值为被调用的bean对象的方法名,用该方法来返回所需要的bean对象。该方法必须为静态方法
-
案例
-
实体类为Dog类
-
静态工厂
package com.spring.study; public class DogStaticFactory { /** * 静态无参方法创建Dog对象 * @return */ public static Dog buildOne(){ System.out.println("==============静态无参buildOne方法被调用=============="); return new Dog(); } /** * 静态有参方法创建Dog对象 * @param name * @param age * @return */ public static Dog buildTwo(String name, int age){ System.out.println("==============静态有参buildTwo方法被调用=============="); return new Dog(name, age); } }
-
配置文件
<?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对象--> <bean id="dog1" class="com.spring.study.DogStaticFactory" factory-method="buildOne"/> <!-- 通过工厂调用静态有参方法创建bean对象--> <bean id="dog2" class="com.spring.study.DogStaticFactory" factory-method="buildTwo"> <constructor-arg index="0" value="二狗子"/> <constructor-arg index="1" value="3"/> </bean> </beans>
-
测试类
package com.spring.test; import com.spring.study.Dog; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestStaticFactory { public static void main(String[] args) { // 1、定义bean配置文件位置 String classPathXml = "classpath:dogStaticFactory.xml"; // 2、创建ClassPathXmlApplicationContext容器,给容器指定需要加载的bean配置文件 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(classPathXml); // 3、获取容器中所有bean对象 //获取所有bean对象的bean名称 String[] beanNames = context.getBeanDefinitionNames(); //打印输出bean对象 for (String beanName : beanNames){ Dog dog = context.getBean(beanName,Dog.class); System.out.println(String.format("bean名称:%s,bean对象的name值:%s,bean对象的age值:%d",beanName,dog.getName(),dog.getAge())); } } }
-
运行结果
==============静态无参buildOne方法被调用============== ==============静态有参buildTwo方法被调用============== bean名称:dog1,bean对象的name值:小白,bean对象的age值:1 bean名称:dog2,bean对象的name值:二狗子,bean对象的age值:3
-
3、实例工厂方法创建bean对象
-
解释:
- 使Spring容器去调用某些已实例化的bean对象的实例方法来生成所需要的bean对象
-
基本格式:
<bean id="bean名称" factory-bean="要调用的实例对象的bean名称" factory-method="要调用bean对象的实例方法名"> <constructor-arg index="0" value="bean的值" ref="引用的bean名称" /> <constructor-arg index="1" value="bean的值" ref="引用的bean名称" /> .... </bean>
- factory-bean:其值为要调用的bean对象的bean名称
- factory-method:其值为被调用的bean对象的方法名,用该方法来返回所需要的bean对象
- 流程:容器通过factory-bean的值找到某个bean对象,然后根据factory-method的值来确定要调用该bean对象的某个方法,最后调用该方法来返回所需要的bean对象
-
案例
-
实体类为Dog类
-
实例工厂类
package com.spring.study; public class DogInstanceFactory { /** * 通过调用无参构造方法创建bean对象 * @return */ public Dog buildOne(){ System.out.println("==============buildOne方法被调用=============="); return new Dog(); } /** * 通过调用有参构造方法创建bean对象 * @param name * @param age * @return */ public Dog buildTwo(String name, int age){ System.out.println("==============buildTwo方法被调用=============="); return new Dog(name, age); } }
-
配置文件
<?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="dogIntanceFactory" class="com.spring.study.DogInstanceFactory"/> <!-- 通过工厂调用无参构造方法创建bean对象--> <bean id="dog1" factory-bean="dogIntanceFactory" factory-method="buildOne"/> <!-- 通过工厂调用有参构造方法创建bean对象--> <bean id="dog2" factory-bean="dogIntanceFactory" factory-method="buildTwo"> <constructor-arg index="0" value="狗剩"/> <constructor-arg index="1" value="4"/> </bean> </beans>
-
测试类
package com.spring.test; import com.spring.study.Dog; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestInstanceFactory { public static void main(String[] args) { // 1、定义bean配置文件位置 String classPathXml = "classpath:dogInstanceFactory.xml"; // 2、创建ClassPathXmlApplicationContext容器,给容器指定需要加载的bean配置文件 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(classPathXml); // 3、获取容器中所有bean对象 //获取所有bean对象的bean名称 String[] beanNames = new String[]{"dog1","dog2"}; //打印输出bean对象 for (String beanName : beanNames){ Dog dog = context.getBean(beanName,Dog.class); System.out.println(String.format("bean名称:%s,bean对象的name值:%s,bean对象的age值:%d",beanName,dog.getName(),dog.getAge())); } } }
-
运行结果
==============buildOne方法被调用============== ==============buildTwo方法被调用============== bean名称:dog1,bean对象的name值:小白,bean对象的age值:1 bean名称:dog2,bean对象的name值:狗剩,bean对象的age值:4
-
4、实现FactoryBean接口创建bean对象
-
一般情况下,
Spring容器通过反射机制利用bean元素的class属性指定实现类来实例化Bean
。在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在bean中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑
。 -
以Bean结尾,表示它是一个Bean。不同于普通Bean的是:它是实现了FactoryBean接口的Bean,根据该bean对象的bean名称从BeanFactory中获取的实际上是FactoryBean的getObject方法返回的对象,而不是FactoryBean本身,如果要获取FactoryBean对象,请在bean对象的bean名称前面加一个&符号来获取。以Dog类举例,
Dog dog = new ClassPathXmlApplicationContext("classpath:dogFactoryBean.xml).getBean("&dog2"))
-
FactoryBean接口源码
package org.springframework.beans.factory; import org.springframework.lang.Nullable; public interface FactoryBean<T> { String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType"; /** * 返回创建好的对象 */ @Nullable T getObject() throws Exception; /** * 返回需要创建的对象的类型 */ @Nullable Class<?> getObjectType(); /** * bean对象是否是单例对象 **/ default boolean isSingleton() { return true; } }
- getObject方法,由开发人员实现bean对象的创建方式,并返回bean对象给Spring容器
- getObjectType方法,由开发人员指定需要创建的bean的类型
- isSingleton方法,表示通过这个接口创建的对象是否是单例的,
如果返回false,那么每次从容器中获取对象都会调用getObject方法去生成新的bean对象
。默认是单例对象。
-
基本格式:
<bean id="bean名称" class="FactoryBean接口实现类" />
- 格式同bean元素
-
案例
-
实体类即Dog类
-
FactoryBean接口实现类
package com.spring.study; import org.springframework.beans.factory.FactoryBean; public class DogFactoryBean implements FactoryBean<Dog> { /** * 设置是否为单例对象 */ private boolean singleton; public boolean getSingleton() { return singleton; } public void setSingleton(boolean singleton) { this.singleton = singleton; } /** * 返回已创建的对象--调用无参构造方法创建的bean对象 * @return * @throws Exception */ @Override public Dog getObject() throws Exception { if (this.singleton){ System.out.println("=========即将打印单例对象========="); }else{ System.out.println("=========即将打印非单例对象========="); } return new Dog(); } /** * 返回需要创建的对象的类型 * @return */ @Override public Class<?> getObjectType() { return Dog.class; } /** * 设置bean对象是否为单例 * @return */ @Override public boolean isSingleton() { return this.singleton; } public DogFactoryBean(boolean singleton) { this.singleton = singleton; } public DogFactoryBean() { } }
- 私有属性singleton为布尔类型,默认值为false,所以该实现类的getObject方法返回的bean对象默认为非单例对象
-
配置文件
<?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"> <!-- 定义DogFactoryBean对象,要获取的Dog对象为非单例对象--> <bean id="dog1" class="com.spring.study.DogFactoryBean"/> <!-- 定义DogFactoryBean对象,要获取的Dog对象为单例对象--> <bean id="dog2" class="com.spring.study.DogFactoryBean"> <constructor-arg index="0" value="true"/> </bean> </beans>
-
测试类
package com.spring.test; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestFactoryBean { public static void main(String[] args) { // 1、定义bean配置文件位置 String classPathXml = "classpath:dogFactoryBean.xml"; // 2、创建ClassPathXmlApplicationContext容器,给容器指定需要加载的bean配置文件 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(classPathXml); // 3、获取容器中所有的bean对象的bean名称 String[] beanNames = context.getBeanDefinitionNames(); // 4、打印输出bean对象的内存地址,以此来判断在单例模式下,获得的bean对象是否为同一个对象 for (String beanName : beanNames){ for (int i = 0; i < 2; i++){ System.out.println(String.format("bean名称:%s,bean对象内存地址:%s",beanName,context.getBean(beanName))); } } System.out.println("FactoryBean实现类对象:" + context.getBean("&dog2")); } }
-
运行结果
=========即将打印非单例对象========= bean名称:dog1,bean对象内存地址:com.spring.study.Dog@cd2dae5 =========即将打印非单例对象========= bean名称:dog1,bean对象内存地址:com.spring.study.Dog@3a883ce7 =========即将打印单例对象========= bean名称:dog2,bean对象内存地址:com.spring.study.Dog@4973813a bean名称:dog2,bean对象内存地址:com.spring.study.Dog@4973813a FactoryBean实现类对象:com.spring.study.DogFactoryBean@6321e813
- 从运行结果打印的内存地址来看,若为
单例模式
,则每次创建bean对象时,从头到尾,只调用一次getObject方法且使用同一个bean对象
;若为非单例模式
,则每次创建bean对象时,都需要调用getObject方法并创建新的bean对象
- 从运行结果打印的内存地址来看,若为
-