本文完整代码地址:https://github.com/yu-linfeng/BlogRepositories/tree/master/repositories/factorybeanjava
FactoryBean
和BeanFactory
因为在命名上极其类似,一直以来困扰了很多的开发者。git
BeanFactory
,耳熟能详的Spring核心接口,提供IoC容器的最基本功能。但要解释FactoryBean
一句话可能就说不清楚了。咱们将从下面的例子逐步说明,FactoryBean是什么,它提供了什么样的能力。程序员
/** * 布料 * 包含颜色属性 * Created by OKevin On 2019/9/3 **/ public class Cloth { private Red red; //省略setter/getter方法 }
当初始化一个Cloth对象时,我但愿Red对象也被赋值,此时我将在Cloth的构造方法中new一个Red对象。github
public Cloth() { red = new Red(); }
可是随着业务的发展,我但愿Cloth的颜色属性将是Blue蓝色,这时我将修改代码将Red和Blue类抽象出一个Color接口,Cloth代码将重构:spring
/** * 布料 * 包含颜色属性 * Created by OKevin On 2019/9/3 **/ public class Cloth { private Color color; public Cloth() { color = new Blue(); } //省略setter/getter方法 }
业务又进一步发展,Cloth类中的颜色属性将会根据必定的条件赋值为Red红色,此时咱们将代码继续重构:测试
/** * 布料 * 包含颜色属性 * Created by OKevin On 2019/9/3 **/ public class Cloth { private Color color; public Cloth() { if (condition()) { color = new Blue(); } else { color = new Red(); } } //省略setter/getter方法 }
这样的代码的确能运行,但若是有新的条件继续加入到业务中,此时咱们又将改动Cloth类的构造方法,而咱们认为Cloth方法是一个比较核心的业务对象,不该该常常对它进行修改,而且在构造方法中对于Color对象建立过于冗余,不符合单一职责的原则,因此咱们将Color对象的建立过程经过工厂方法模式来完成。代理
咱们再次将Cloth类进行以下重构(为了使示例代码更加简洁,下面的示例将只建立Red对象):日志
/** * 布料 * 包含颜色属性 * Created by OKevin On 2019/9/3 **/ public class Cloth { private Color color; public Cloth() { color = StaticColorFactory.newInstance(); } //省略setter/getter方法 }
/** * 静态工厂方法 * Created by OKevin On 2019/9/3 **/ public class StaticColorFactory { public static Color getInstance() { return new Red(); } }
若是咱们在Spring容器中要经过静态工厂方法,建立具体的对象实例应该怎么作呢?code
众所周知,要将一个对象实例交由Spring容器管理,咱们一般是经过如下XML配置:xml
<bean id="cloth" class="com.coderbuff.bean.Cloth"> <property name="color" ref="red"/> </bean> <bean id="red" class="com.coderbuff.bean.Red" />
但此时,Red对象实例并非由Spring容器管理,而是由静态工厂方法建立的,此时咱们应该讲XML配置修改成如下方式:
<bean id="cloth" class="com.coderbuff.bean.Cloth"> <property name="color" ref="red"/> </bean> <bean id="red" class="com.coderbuff.factory.StaticColorFactory" factory-method="getInstance" />
这是Spring支持静态工厂方法建立对象实例的特定方式。这样咱们就能在Spring中经过静态工厂方法建立对象实例。
有静态工厂方法,就有非静态工厂方法,区别就是方法不是静态的。
/** * 实例工厂方法 * Created by OKevin On 2019/9/3 **/ public class ColorFactory { public Color getInstance() { return new Red(); } }
实例工厂方法在Spring中XML配置略有不一样:
<bean id="cloth" class="com.coderbuff.bean.Cloth"> <property name="color" ref="red"/> </bean> <bean id="colorFactory" class="com.coderbuff.factory.ColorFactory"/> <bean id="red" factory-bean="colorFactory" factory-method="getInstance"/>
经过配置能够看到,咱们须要首先在Spring中实例化工厂,再经过工厂对象实例化Red对象。
在有了对工厂方法在Spring中建立对象实例的认识后,FactoryBean实际上就是为咱们简化这个操做。下面咱们将经过FactoryBean来建立Red对象。
/** * Created by OKevin On 2019/9/3 **/ public class ColorFactoryBean implements FactoryBean<Color> { public Color getObject() throws Exception { return new Red(); } public Class<?> getObjectType() { return Red.class; } public boolean isSingleton() { return false; } }
经过实现FactoryBean的方式,XML配置以下:
<bean id="cloth" class="com.coderbuff.bean.Cloth"> <property name="color" ref="red"/> </bean> <bean id="red" class="com.coderbuff.factory.ColorFactoryBean"/>
这样就不用像工厂方法那样配置相应的属性,直接按照普通的Bean注入便可,因为Spring内部作了特殊处理,此时名称为“red”的Bean并非ColorFactoryBean,而是它方法中getObject中返回的对象。若是实在想要获取ColorFactoryBean的对象实例,则在Bean的名称前加入“&”便可(“&red”)。
看到这里,是否对FactoryBean有了一点认识呢?FactoryBean在Spring中最为典型的一个应用就是用来建立AOP的代理对象。
咱们知道AOP其实是Spring在运行时建立了一个代理对象,也就是说这个对象,是咱们在运行时建立的,而不是一开始就定义好的,这很符合工厂方法模式。更形象地说,AOP代理对象经过Java的反射机制,在运行时建立了一个代理对象,在代理对象的目标方法中根据业务要求织入了相应的方法。这个对象在Spring中就是——ProxyFactoryBean。
咱们将经过比较“古老”的方式建立一个Red对象的切面,在它的print方法执行前和执行后分别执行一条语句。之因此古总是由于咱们每每经过注解的方式,而不会这么折磨本身去写一个切面对象。
/** * 环绕通知 * Created by OKevin On 2019/9/4 **/ public class LogAround implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("调用目标方法【前】打印日志"); Object result = invocation.proceed(); System.out.println("调用目标方法【后】打印日志"); return result; } }
此时咱们须要ProxyFactoryBean的介入为咱们建立一个代理对象并由Spring容器管理,根据上面ColorFactoryBean的经验,ProxyFacoryBean也应该以下配置:
<bean id="xxx" class="org.springframework.aop.framework.ProxyFactoryBean" />
答案是确定的,只是ProxyFactoryBean多了几个参数,既然是生成代理对象,那么目标对象、目标方法就必不可少,实际的XLM配置以下:
<bean id="logAround" class="com.coderbuff.aop.LogAround"/> <bean id="red" class="com.coderbuff.bean.Red"/> <bean id="proxyRed" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces" value="com.coderbuff.bean.Color"/> <property name="interceptorNames" value="logAround"/> <property name="target" ref="red"/> <property name="proxyTargetClass" value="true"/> </bean>
经过测试程序,ProxyFactoryBean的确生成了一个代理对象。
public class ProxyFactoryBeanTest { private ClassPathXmlApplicationContext ctx; @Before public void init() { ctx = new ClassPathXmlApplicationContext("spring-proxyfactorybean.xml"); } @Test public void testProxyFactory() { Red proxyRed = (Red) ctx.getBean("proxyRed"); proxyRed.print(); } }
因此,FactoryBean为咱们实例化Bean提供了一个更为灵活的方式,咱们能够经过FactoryBean建立出更为复杂的Bean实例。
本文完整代码地址:https://github.com/yu-linfeng/BlogRepositories/tree/master/repositories/factorybean