该篇博客关于工厂方法与FactoryBean,其实就是在Spring中不再使用Spring创建Bean实例,而是利用工厂方法与FactoryBean把Bean创建过程转移到开发者手中
该篇博客目录
1、理解工厂方法和FactoryBean是干什么的
2、静态工厂方法实例化Bean
3、工厂方法实例化Bean
4、FactoryBean实例化Bean(工厂Bean)
一、理解工厂方法和FactoryBean是干什么的
笔者认为这一块在本篇博客是最重要的,你得理解工厂方法和FactoryBean是干什么的,以及它和哪些是同类
首先通常在Spring中,我们会利用XML或者注解的方式类配置bean。而在XML方式中有三种方式来实例化bean
- 反射模式
- 工厂方法模式
- FactoryBean模式
其中经常会用到反射模式,即在bean的配置中,声明bean的全类名,而本博客笔者将会介绍工厂模式和FactoryBean模式下实例化bean,对两种模式深入理解其思想
二、静态工厂方法实例化Bean(Static Factory Method)
考虑一个场景,在Spring中将一个单例类配置Bean。一般来说,单例类的实例只能通过静态工厂方法创建(笔者这里在《Spring IN ACTION》的例子上进行扩展):以下例子一个舞台类,舞台只有一个,即采用静态工厂方法
package ;
/**
* @author LinJie
* @Description:这是一个舞台单例类(所以需要静态工厂方法)
* 每个参赛者用同一个舞台
*/
public class Stage {
private static Stage instance;
public static Stage getInstance() {
if(instance == null){
instance = new Stage();
}
return instance;
}
//搭建舞台完成
public void CreateStageSuccess() {
("舞台搭建完成,舞者可以上台了");
}
}
(舞者类)
package ;
/**
* @author LinJie
* @Description:这是每个舞者的类,但他们使用同一个舞台
*/
public class Dancer {
private Stage stage;
/**
* @param stage the stage to set
*/
public void setStage(Stage stage) {
= stage;
}
//LinJie dancer上台
public void Show() {
();
("LinJie dancer上台");
}
}
测试类
package ;
/**
* @author LinJie
* @Description:这是每个舞者的类,但他们使用同一个舞台
*/
public class Dancer {
private Stage stage;
/**
* @param stage the stage to set
*/
public void setStage(Stage stage) {
= stage;
}
//LinJie dancer上台
public void Show() {
();
("LinJie dancer上台");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="/schema/beans"
xmlns:xsi="http:///2001/XMLSchema-instance"
xsi:schemaLocation="
/schema/beans /schema/beans/">
<!-- 静态工厂实例化Bean -->
<bean class="" factory-method="getInstance"></bean>
<bean class="">
<property name="stage" ref="stage"></property>
</bean>
</beans>
结果
舞台搭建完成,舞者可以上台了
LinJie dancer上台
注意:Stage没有公开的构造方法,取而代之的是,静态方法getInstance()每次调用都返回相同的实例(因为舞台只有一个),那么在Spring中如何把没有公开构造方法的Stage配置为一个Bean呢?
可以看到在中看到
- factory-method:允许我们调用一个指定的静态工厂方法,从而代替构造方法来创建一个类的实例(即指定静态工厂方法名)
- class:不再是Bean实例的实现类,而是Bean实例的静态工厂类(上面的例子没有体现这一点,下面的例子根据这一点来实现)
- 如果静态工厂方法需要参数,使用为其配置
(一个舞台类,可传名字)
package ;
/**
* @author LinJie
* @Description:一个舞台类,可以给舞台赋值(其实这里我只想演示传参)
*/
public class Stage {
private String name;
/**
* @param name the name to set
*/
public void setName(String name) {
= name;
}
//搭建舞台
public void Show() {
(name+"舞台搭建完成");
}
}
(静态工厂方法实现类)
package ;
/**
* @author LinJie
* @Description:这是一个静态工厂方法的类,用于创建单例舞台
*/
public class staticfactory {
private static Stage instance;
public static Stage getInstance() {
if(instance == null){
instance = new Stage();
}
return instance;
}
}
测试类
package ;
import ;
import ;
import ;
public class staticFactoryBean {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("");
Stage stage = (Stage) ("stage");
();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="/schema/beans"
xmlns:xsi="http:///2001/XMLSchema-instance"
xsi:schemaLocation="
/schema/beans /schema/beans/">
<!-- 静态工厂实例化Bean -->
<!-- class:这里不再是Bean实例的实现类,而是生成Bean实例的静态工厂类 -->
<bean class="" factory-method="getInstance">
<property name="name" value="haha"></property>
</bean>
</beans>
结果
注意:这里的class属性的值不是Bean实例的实现类,而是生成Bean实例的静态工厂类
三、工厂方法实例化Bean(Instance Factory Method)
(一个Bean实例的实现类)
package ;
/**
* @author LinJie
* @Description
*/
public class ABean {
//利用工厂方法实例化Bean成功显示
public void show() {
("工厂方法实例化Bean成功");
}
}
(工厂方法)
package ;
/**
* @author LinJie
* @Description:工厂方法实例化Bean
*/
public class FactoryBean {
public ABean getABean() {
return new ABean();
}
}
测试类
package ;
import ;
import ;
import ;
public class FactoryTest {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("");
ABean abean = (ABean) ("abean");
();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="/schema/beans"
xmlns:xsi="http:///2001/XMLSchema-instance"
xsi:schemaLocation="
/schema/beans /schema/beans/">
<!-- 工厂实例化Bean -->
<bean class=""></bean>
<bean factory-bean="factorybean" factory-method="getABean"></bean>
</beans>
结果
在applicationContext的属性中
- factory-bean:指定工厂方法所在的工厂类实例(即工厂方法bean的id,用法与ref类似)
- factory-method:还是指定工厂方法名
- 也可以通过来指定方法调用参数
四、FactoryBean实例化Bean
FactoryBean是Spring容器提供的一种可以扩展容器对象实例化逻辑的接口,请不要将其与容器名称BeanFactory相混淆。FactoryBean,其主语是Bean,定语是Factory,也就是说,它本身与其他注册到容器的对象一样,只是一个Bean而已,只不过这里类型的Bean本身就是生产对象的工厂
应用场景
当某些对象的实例话过程过于烦琐,通过XML配置过于复杂,使我们宁愿使用Java代码来完成这个实例化过程的时候,或者,某些第三方库不能直接注册到Spring容器中的时候,就可以实现接口,给出自己的对象实例化代码。当然实现自定义工厂也是可以的。但是FactoryBean是Spring的标准
使用方法
1、创建FactoryBean的实现类
2、实现以下三个方法
- public String getObject() throws Exception:该方法返回该FactoryBean“生产”的对象。我们需要实现该方法以给出自己对象实例化逻辑
- public Class<?> getObjectType():该方法仅返回getObject()方法所返回的对象的类型。如果预先无法确定,则返回null
- public boolean isSingleton() :该方法返回结果用于表明,getObject()“生产”的对象是否要以singleton(单例)形式存于容器中。如果以singleton形式存在,则返回true,否则返回false
nameFactoryBean实现类
package ;
import ;
public class nameFactoryBean<T> implements FactoryBean<String> {
//该方法返回该FactoryBean“生产”的对象
//我们需要实现该方法以给出自己对象实例化逻辑
@Override
public String getObject() throws Exception {
return new String("linjie");
}
//该方法仅返回getObject()方法所返回的对象的类型
//如果预先无法确定,则返回null
@Override
public Class<?> getObjectType() {
return null;
}
//该方法返回结果用于表明,getObject()“生产”的对象是否要以singleton(单例)形式存于容器中
//如果以singleton形式存在,则返回true,否则返回false
@Override
public boolean isSingleton() {
return false;
}
}
测试类
package ;
import ;
import ;
import ;
public class factorybeanTest {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("");
String bean = (String) ("bean");
(bean);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="/schema/beans"
xmlns:xsi="http:///2001/XMLSchema-instance"
xsi:schemaLocation="
/schema/beans /schema/beans/">
<!-- FactoryBean实例化Bean -->
<bean class=""></bean>
</beans>
结果
FactoryBean和BeanFactory区别
- FactoryBean:就是笔者上文阐述的,以Bean结尾,表示它是个Bean,它并不是简单的Bean,而是一个能生产对象或者修饰对象的工厂Bean
- BeanFactory:它是Spring IoC容器的一种形式,提供完整的IoC服务支持,也可以看出主语是Factory,即是一个管理Bean的工厂,关于BeanFactory可以转战笔者另一篇博客/w_linux/article/details/80025048
参考
《Spring揭秘》
《Spring IN ACTION》