Spring学习 之容器扩展点:FactoryBean

mac2024-06-01  59

文章目录

区别FactoryBean和BeanFactory区别FactoryBean和普通Bean使用场景 FactoryBean接口是SpringIoC容器实例化逻辑的扩展点。 什么叫扩展点? Spring处理Bean生命周期的时候在Bean初始化前后回调自定义方法就是扩展点。

区别FactoryBean和BeanFactory

BeanFactory直译是生产Bean的工厂,在Srping中就是容器,常用的ApplicationContext就是它的一个继承类。我们也可以直接使用BeanFactory示例:

BeanFactory factory = new XmlBeanFactory(new FileSystemResource("c:/piratebean.xml"));

而FactoryBean顾名思义就是一个bean,但这个bean和普通bean有点不同。

区别FactoryBean和普通Bean

Spring中有两种类型的Bean,一种是普通Bean,另一种是工厂Bean即FactoryBean,这两种Bean都被容器管理。 工厂Bean是实现了org.springframework.beans.factory.FactoryBean<T>接口的Bean,从ApplicationContext的getBean()方法获取的对象不是该类的一个实例,而是该类的getObject()方法所返回的对象。

当我们需要获取FactoryBean实例本身而不是它所产生的bean,则要使用&符号。 比如,现有FactoryBean,id为”playerBean”,在容器上调用getBean(“playerBean”)将返回FactoryBean产生的bean。调用getBean("&playerBean")将返回FactoryBean它本身的实例。

使用场景

当需要在类中撰写复杂的初始化程序,此时使用java编码比使用XML配置更容易表达。我们常接触的代理类就可以用这个接口实现(最后有例子)。 在Spring框架内部,有很多地方有FactoryBean的实现类,它们在很多应用如(Spring的AOP、ORM、事务管理)及与其它第三框架(ehCache)集成时都有体现。

示例: 足球运动员踢球。

// 运动员动作接口 public interface PlayerActionInterface { void shoot(); void pass(); } // 实现一个足球运动员 public class FootballPlayer implements PlayerActionInterface { String name;//球员名字 String team;//所在球队 // 省略getter、setter }

beans.xml中配置:

<bean id="footballPlayer" class="com.mvc.FootballPlayer"> <property name="name" value="C.罗纳尔多"></property> </bean>

现在有一个新的需求,要求每个球员在做射门和传球这两个动作之前,先要“观察进攻及防守队员跑位”;做完动作后要完成“无球跑动”。 本着开闭原则,在不修改原有类的基础上,我们创建一个代理类来实现。

import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class PlayerFactory implements FactoryBean<PlayerActionInterface>, InitializingBean, DisposableBean { /*被代理的PlayerActionInterface实现对象,通过XML注入*/ private PlayerActionInterface target; /*本类生成的代理对象*/ private PlayerActionInterface proxyObj; @Override public void afterPropertiesSet() throws Exception { System.out.println("afterPropertiesSet"); proxyObj = (PlayerActionInterface) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[] { Class.forName("com.mvc.PlayerActionInterface") }, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("method:" + method.getName()); System.out.println("观察进攻及防守队员跑位"); Object result = method.invoke(target, args); System.out.println("无球跑动"); return result; } }); } @Override public PlayerActionInterface getObject() throws Exception { System.out.println("getObject"); return proxyObj; } @Override public void destroy() throws Exception { System.out.println("distory"); } @Override public Class<?> getObjectType() { return proxyObj == null ? Object.class : proxyObj.getClass(); } @Override public boolean isSingleton() { return true; } public PlayerActionInterface getTarget() { return target; } public void setTarget(PlayerActionInterface target) { this.target = target; } }

可以看到在PlayerFactory这个类,我们实现了FactoryBean接口的三个方法。其中getObject()方法返回的是代理对象proxyObj。 这个对象是在bean初始化的时候回调InitializingBean接口的实现方法afterPropertiesSet()里创建的。使用了Proxy.newProxyInstance给原来的类创建了一个代理类,该代理类在方法调用的前后都加上了动作。

最后在beans.xml中定义:

<bean id="playerfacory" class="com.mvc.PlayerFactory"> <property name="target" ref="footballPlayer"></property> </bean>

业务代码调用:

import com.mvc.PlayerActionInterface; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class IOCTest { @Test public void test(){ ApplicationContext ctx= new ClassPathXmlApplicationContext("applicationContext.xml"); // 最终调用getObject来获取实例对象 Object obj1 = ctx.getBean("playerfacory"); // 加上&,表示获取的是实例的本身 Object obj2 = ctx.getBean("&playerfacory"); System.out.println("ctx.getBean(\"playerfacory\"):"+obj1.getClass().getName()); System.out.println("ctx.getBean(\"&playerfacory\"):"+obj2.getClass().getName()); System.out.println("-----------------------"); PlayerActionInterface smone = ctx.getBean("playerfacory", PlayerActionInterface.class); smone.shoot(); System.out.println(""); smone.pass(); } }

输出:

afterPropertiesSet getObject ctx.getBean("playerfacory"):com.sun.proxy.$Proxy3 ctx.getBean("&playerfacory"):com.mvc.PlayerFactory ----------------------- method:shoot 观察进攻及防守队员跑位 C.罗纳尔多射门 无球跑动 method:pass 观察进攻及防守队员跑位 C.罗纳尔多边路传中 无球跑动
最新回复(0)