Spring为Bean提供了多种实例化方式,通常包括4种方式。
● 第一种:通过构造方法实例化
● 第二种:通过简单工厂模式实例化
● 第三种:通过factory-bean实例化
● 第四种:通过FactoryBean接口实例化
01通过构造方法实例化
我们之前一直使用的就是这种方式。默认情况下,会调用Bean的无参数构造方法。
package com.sunsplanter.spring6.bean;
public class User {
public User() {
System.out.println("User类的无参数构造方法执行。");
}
}
<bean id="userBean" class="com.sunsplanter.spring6.bean.User"/>
package com.sunsplanter.spring6.test;
import com.powernode.spring6.bean.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringInstantiationTest {
@Test
public void testConstructor(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
User user = applicationContext.getBean("userBean", User.class);
System.out.println(user);
}
}
02通过简单工厂模式(静态工厂方法)实例化
第一步:定义一个Bean
package com.sunsplanter.spring6.bean;
public class Vip {
}
第二步:编写简单工厂模式当中的工厂类
package com.sunsplanter.spring6.bean;
public class VipFactory {
//定义一个get方法,返回值是Vip类型的对象
//采用简单工厂模式,一个工厂只对应一个产品,无需"个性化"定制参数,因此定义为为静态方法
public static Vip get(){
return new Vip();
}
}
第三步:在Spring配置文件中指定创建该Bean的方法(使用factory-method属性指定)
<!--既然不是通过默认的无参构造方法获取Bean了,就需要告诉Spring用哪个可以获取Bean
factory-method方法的参数指定工厂类中的静态方法get,Spring调用该指定的静态方法get创建Bean-->
<bean id="vipBean" class="com.sunsplanter.spring6.bean.VipFactory" factory-method="get"/>
第四步:编写测试程序
@Test
public void testSimpleFactory(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Vip vip = applicationContext.getBean("vipBean", Vip.class);
System.out.println(vip);
}
03通过工厂方法模式(factory-bean)实例化
这种方式本质上是:通过工厂方法模式进行实例化。
第一步:定义一个Bean
package com.sunsplanter.spring6.bean;
public class Vip {
}
第二步:定义具体工厂类,工厂类中定义实例方法
package com.sunsplanter.spring6.bean;
//采用工厂方法模式,一个工厂对应多个产品,需要根据参数的不同生成对象,因此定义为实例方法
public class VipFactory {
public Vip get(){
return new Vip();
}
}
第三步:在Spring配置文件中指定factory-bean以及factory-method
<!--告诉Spring,调用哪个类(VipFactory)的哪个方法来(get)获取Bean-->
<bean id="vipFactory" class="com.sunsplanter.spring6.bean.VipFactory"/>
<!--可以从后往前看,第二句的factory-bean说明要调用哪个对象(bean),那么当然第一句要先定义个vip的bean(对象)-->
<bean id="vipBean" factory-bean="vipFactory" factory-method="get"/>
第四步:编写测试程序
@Test
public void testSelfFactoryBean(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Vip vipBean = applicationContext.getBean("vipBean", Vip.class);
System.out.println(vipBean);
}
04通过FactoryBean接口实例化
常用,实际上是对"03通过工厂方法模式(factory-bean)实例化"的简化.
以上的第三种方式中,factory-bean是我们自定义的,factory-method也是我们自己定义的。
在Spring中,当你编写的类直接实现FactoryBean接口之后,factory-bean不需要指定了,factory-method也不需要指定了。
factory-bean会自动指向实现FactoryBean接口的类,factory-method会自动指向getObject()方法。
第一步:定义一个Bean
package com.sunsplanter.bean;
public class Person {
}
第二步:编写一个类实现FactoryBean接口,必须重写三个方法
package com.sunsplanter.bean;
import org.springframework.beans.factory.FactoryBean;
public class PersonFactoryBean implements FactoryBean<Person> {
@Override
public Person getObject() throws Exception {
//最终这个Bean还是自己new的
return new Person();
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
// true表示单例
// false表示原型
return true;
}
}
第三步:在Spring配置文件中配置FactoryBean
<bean id="personBean" class="com.sunsplanter.bean.PersonFactoryBean"/>
测试程序:
@Test
public void testFactoryBean(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Person personBean = applicationContext.getBean("personBean", Person.class);
System.out.println(personBean);
}
05BeanFactory和FactoryBean的区别
BeanFactory:Bean工厂,是Spring IoC容器的顶级类,用来创建bean对象的。
FactoryBean:它是一个Bean,用来辅助Spring创建bean对象。
在Spring中,Bean可以分为两类:
● 第一类:普通Bean
● 第二类:工厂Bean(记住:工厂Bean也是一种Bean,只不过这种Bean比较特殊,它可以辅助Spring实例化其它Bean对象。)
05借助FactoryBean完成Date日期对象的实例化,并用构造方法注入
package com.bean;
import java.util.Date;
public class Student {
//java.util.Date在Spring被当作简单类型,但若以简单类型注入,其格式要求严格,不够灵活
//故我们主动将其视为非简单类型,用非简单类型的方法注入
private Date birth;
public void setBirth(Date birth) {
this.birth = birth;
}
@Override
public String toString() {
return "Student{" +
"birth=" + birth +
'}';
}
}
<bean id="nowTime" class="java.util.Date"/>
<bean id="student" class="com.bean.Student">
<!-- 将Date视为对象注入,因此用ref而不是Date -->
<property name="birth" ref="nowTime"/>
</bean>
编写测试程序,可正常输出.
但问题是:通过调用Java的Date类,只能输出当前的时间.但既然是注入生日.需求当然是能注入任意时间.
package com.bean;
import java.util.Date;
public class Student {
//java.util.Date在Spring被当作简单类型,但若以简单类型注入,其格式要求严格,不够灵活
//故我们主动将其视为非简单类型,用非简单类型的方法注入
private Date birth;
public void setBirth(Date birth) {
this.birth = birth;
}
@Override
public String toString() {
return "Student{" +
"birth=" + birth +
'}';
}
}
package com.bean;
import org.springframework.beans.factory.FactoryBean;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateFactoryBean implements FactoryBean<Date> {
//流程是传一个yyyy-mm-dd字符串进来,然后用SimpleDateFormat转化成日期对象Date
//Spring会注入一个日期,那么当然要定义一个变量来接受
private String date;
// 通过构造方法给日期字符串属性赋值
public DateFactoryBean(String date) {
this.date = date;
}
@Override
public Date getObject() throws Exception {
//创建一个yyyy--mm--dd的sdf对象,用sdf将yyyy--mm--dd转化成标准日期对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.parse(this.date);
}
@Override
public Class<?> getObjectType() {
return null;
}
}
<!-- 这是一个工厂Bean,通过其来返回普通Bean:java.util.Date
只要一获取上下文,自动生成这个Bean,而这个Bean会直接传入一个简单类型的String,通过DateFactoryBean里重写的getObject方法
String会转成日期对象date,进而将这个date以ref标签通过set的方式注入Student-->
<bean id="date" class="com.bean.DateFactoryBean">
<constructor-arg name="date" value="1980-10-11"/>
</bean>
<bean id="student" class="com.bean.Student">
<!--
property标签的name提示setBirth方法名。
property标签的ref指定注入的bean对象的id。-->
<property name="birth" ref="date"/>
</bean>
@Test
public void testDate(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Student studentBean = applicationContext.getBean("student", Student.class);
System.out.println(studentBean);
}