深入了解Spring中的FactoryBean
factorybean
和beanfactory
由于在命名上极其相似,一直以来困扰了不少的开发者。
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对象。
public cloth() { red = new red(); }
但是随着业务的发展,我希望cloth的颜色属性将是blue蓝色,这时我将修改代码将red和blue类抽象出一个color接口,cloth代码将重构:
/** * 布料 * 包含颜色属性 * 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容器中要通过静态工厂方法,创建具体的对象实例应该怎么做呢?
众所周知,要将一个对象实例交由spring容器管理,我们通常是通过以下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对象。
factorybean
/** * 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。
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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。