目录
5. BeanFactory和FactoryBean的区别(面试题)
一:Bean的实例化方式
Spring为Bean提供了多种实例化方式,通常包括4种方式。(也就是说在Spring中为Bean对象的创建准备了多种方案,目的是:更加灵活)
第一种:通过构造方法实例化
第二种:通过简单工厂模式实例化
第三种:通过factory-bean实例化(工厂方法模式)
第四种:通过FactoryBean接口实例化
1. 通过构造方法实例化
我们之前一直使用的就是这种方式!默认情况下,会调用Bean的无参数构造方法,这里在复习一遍!
SpringBean类
package com.bjpowernode.spring.bean;
public class SpringBean {
public SpringBean() {
System.out.println("SpringBean的无参数构造方法执行了");
}
}
spring.xml配置
第一种:在spring配置文件中直接配置类全路径,Spring会自动调用该类的无参数构造方法来实例化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">
<!--Spring提供的实例化方式,第一种-->
<bean id="sb" class="com.bjpowernode.spring.bean.SpringBean"/>
</beans>
BeanInstantiationTest测试类
package com.bjpowernode.spring.test;
import com.bjpowernode.spring.bean.SpringBean;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanInstantiationTest {
@Test
public void tesInstantiation1(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
SpringBean sb = applicationContext.getBean("sb", SpringBean.class);
System.out.println(sb);
}
}
执行结果:成功调用无参数构造方法实例化对象
2. 通过简单工厂模式实例化
简单工厂模式又叫做静态工厂方法模式,因为工厂类中有一个静态方法!
第一步:定义一个Bean
package com.bjpowernode.spring.bean;
public class Vip {
public Vip() {
System.out.println("我是一个Vip");
}
}
第二步:编写简单工厂模式当中的工厂类
package com.bjpowernode.spring.bean;
public class VipFactory {
// 里面有一个静态方法
public static Vip get(){
// 实际上对象的创建还是我们程序员自己完成的
return new Vip();
}
}
第三步:在Spring配置文件中指定创建该Bean的方法
第二种:通过简单工厂模式。
需要在Spring配置文件中告诉Spring框架,调用哪个类的哪个方法获取Bean?
①class属性指定的是工厂类的全限定类名!
②factory-method属性指定的是工厂类当中的静态方法,也就是告诉Spring框架,调用这个方法可以获取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">
<!--Spring提供的实例化方式,第二种-->
<bean id="vipBean" class="com.bjpowernode.spring.bean.VipFactory" factory-method="get"/>
</beans>
第四步:编写测试程序
package com.bjpowernode.spring.test;
import com.bjpowernode.spring.bean.SpringBean;
import com.bjpowernode.spring.bean.Vip;
import com.bjpowernode.spring.bean.VipFactory;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanInstantiationTest {
@Test
public void tesInstantiation2(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Object vipBean = applicationContext.getBean("vipBean", Vip.class);
System.out.println(vipBean);
}
}
执行结果:通过简单工厂模式也能实例化对象
3. 通过factory-bean实例化
本质上是:通过工厂方法模式进行实例化对象!
注:简单工厂模式和工厂方法模式的区别
①简单工厂模式是所有的产品对应一个工厂类,使用的是静态方法!
②工厂方法模式是一个产品对应一个工厂类,使用的是实例方法!
第一步:定义一个Bean
package com.bjpowernode.spring.bean;
// 工厂方法模式当中的:具体产品角色
public class Gun {
public Gun() {
System.out.println("Gun的无参数构造方法执行");
}
}
第二步:定义具体工厂类,工厂类中定义实例方法
package com.bjpowernode.spring.bean;
// 工厂方法模式当中:的具体工厂角色
public class GunFactory {
// 实例方法
public Gun get(){
// 还是我们自己new的对象
return new Gun();
}
}
第三步:在Spring配置文件中指定factory-bean以及factory-method
第三种:通过工厂方法模式。
通过 factory-bean属性 + factory-method属性来共同完成。告诉Spring框架,调用哪个对象(因为是实例方法需要创建对象)的哪个方法来获取Bean。
①factory-bean属性用来告诉Spring调用那个对象!
②factory-method属性用来告诉Spring调用该对象的那个方法!
<?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">
<!--Spring提供的实例化方式,第三种-->
<bean id="gunBean" class="com.bjpowernode.spring.bean.GunFactory"/>
<bean id="gun" factory-bean="gunBean" factory-method="get"/>
</beans>
第四步:编写测试程序
package com.bjpowernode.spring.test;
import com.bjpowernode.spring.bean.Gun;
import com.bjpowernode.spring.bean.SpringBean;
import com.bjpowernode.spring.bean.Vip;
import com.bjpowernode.spring.bean.VipFactory;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanInstantiationTest {
@Test
public void tesInstantiation3(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Gun gun = applicationContext.getBean("gun", Gun.class);
System.out.println(gun);
}
}
执行结果:通过工厂方法模式也能实例化对象
4. 通过FactoryBean接口实例化
①在第三种方式中,factory-bean和factory-method都是我们自己定义的。
②在Spring中,当编写的类直接实现FactoryBean接口之后,factory-bean和factory-method就不需要指定了!factory-bean会自动指向实现FactoryBean接口的类,factory-method会自动指向getObject()方法!
第一步:定义一个Bean
package com.bjpowernode.spring.bean;
public class Person {
public Person() {
System.out.println("Person的无参数构造方法执行了");
}
}
第二步:编写一个类实现FactoryBean接口,重写里面的方法
PersonFactory也是一个Bean,只不过这个Bean比较特殊,叫做工厂Bean。通过工厂Bean这个特殊的Bean可以获取一个普通的Bean!
package com.bjpowernode.spring.bean;
import org.springframework.beans.factory.FactoryBean;
public class PersonFactory implements FactoryBean<Person> {
@Override
public Person getObject() throws Exception {
// 对象的创建也是自己new的
return new Person();
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
// 这个方法是默认存在的,true表示单例,false表示原型
return true;
}
}
第三步:在Spring配置文件中配置FactoryBean
第四种:通过FactoryBean接口来实现,这种方式实际上就是第三种方式的简化!
①由于你编写的类实现了FactoryBean接口,所以这个类是一个特殊的类,不需要你再手动指定:factory-bean、factory-method。
②通过一个特殊的Bean:工厂Bean,来返回一个普通的Bean Person对象。即通过FactoryBean这个工厂Bean主要是想对普通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">
<!--Spring提供的实例化方式,第四种-->
<bean id="person" class="com.bjpowernode.spring.bean.PersonFactory" />
</beans>
第四步:编写测试程序
package com.bjpowernode.spring.test;
import com.bjpowernode.spring.bean.*;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanInstantiationTest {
@Test
public void tesInstantiation4(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Person person = applicationContext.getBean("person", Person.class);
System.out.println(person);
}
}
执行结果:通过FactoryBean接口实例化
注:FactoryBean在Spring中是一个接口,被称为“工厂Bean”。“工厂Bean”是一种特殊的Bean,所有的“工厂Bean”都是用来协助Spring框架来创建其他Bean对象的!
5. BeanFactory和FactoryBean的区别(面试题)
(1)BeanFactory(是一个工厂)
BeanFactory是Spring IoC容器的*对象,BeanFactory被翻译为“Bean工厂”,在Spring的IoC容器中,“Bean工厂”负责创建Bean对象!
(2)FactoryBean(是一个Bean)
FactoryBean是一个Bean,是一个能够辅助Spring实例化其它Bean对象的一个Bean!
在Spring中,Bean可以分为两类:
- 第一类:普通Bean
- 第二类:工厂Bean(工厂Bean也是一种Bean,只不过这种Bean比较特殊,它可以辅助Spring实例化其它Bean对象)
6. 使用FactoryBean注入自定义Date
①前面我们说过,java.util.Date在Spring中被当做简单类型,简单类型在注入的时候可以直接使用value属性或value标签来完成。
②但是之前我们已经测试过了,对于Date类型来说,采用value属性或value标签赋值的时候,对日期字符串的格式要求非常严格,必须是这种格式的:Mon Oct 10 14:30:26 CST 2022,其他格式是不会被识别的!
③当然我们也可以当成非简单类型处理,使用ref属性来处理,但是却有一个弊端,获取的都是当前的时间,并不能自己指定时间!
注:下面我们就使用FactoryBean来完成这个骚操作!
Student类
package com.bjpowernode.spring.bean;
import java.util.Date;
public class Student {
// 每个学生都有出生日期
private Date birth;
@Override
public String toString() {
return "Student{" +
"birth=" + birth +
'}';
}
public void setBirth(Date birth) {
this.birth = birth;
}
}
编写DateFactory实现FactoryBean接口
package com.bjpowernode.spring.bean;
import org.springframework.beans.factory.FactoryBean;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateFactory implements FactoryBean<Date> {
// 定义一个日期属性,用来处理传过来的日期字符串
private String date;
// 通过构造方法给日期字符串属性赋值
public DateFactory(String date) {
this.date = date;
}
@Override
public Date getObject() throws Exception {
// 处理
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.parse(this.date);
}
@Override
public Class<?> getObjectType() {
return null;
}
}
编写spring配置文件
<?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">
<!--通过这个类的构造方法,把字符串转换成Date-->
<bean id="date" class="com.bjpowernode.spring.bean.DateFactory">
<constructor-arg name="date" value="1999-01-14"/>
</bean>
<!--把上面的Date通过上面的类,使用ref属性引进来-->
<bean id="studentBean" class="com.bjpowernode.spring.bean.Student">
<property name="birth" ref="date"/>
</bean>
</beans>
编写测试程序
package com.bjpowernode.spring.test;
import com.bjpowernode.spring.bean.*;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanInstantiationTest {
@Test
public void testDate(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Student studentBean = applicationContext.getBean("studentBean", Student.class);
System.out.println(studentBean);
}
}
执行结果