说明:
1. 本文基于Spring-Framework 5.1.x版本讲解
2. 建议读者对Mybatis有基本的使用经验
这一篇我们讲讲org.springframework.beans.factory.FactoryBean接口,这个接口功能非常强大,可以集成不同的中间件或组件到Spring容器中来,可以说该接口是打通Spring与外界沟通的重要桥梁,是Spring非常重要的一个拓展点。 不少人会拿BeanFactory与FactoryBean做比较,其实这两个接口根本就没有可比性,完全不是一个’层次‘的产物。废话不多说,让我们开始吧。
先看下FatoryBean接口在源码中的定义
/**
* Interface to be implemented by objects used within a {@link BeanFactory} which
* are themselves factories for individual objects. If a bean implements this
* interface, it is used as a factory for an object to expose, not directly as a
* bean instance that will be exposed itself.
*
* <p><b>NB: A bean that implements this interface cannot be used as a normal bean.</b>
* A FactoryBean is defined in a bean style, but the object exposed for bean
* references ({@link #getObject()}) is always the object that it creates.
*
* <p>FactoryBeans can support singletons and prototypes, and can either create
* objects lazily on demand or eagerly on startup. The {@link SmartFactoryBean}
* interface allows for exposing more fine-grained behavioral metadata.
*
* <p>This interface is heavily used within the framework itself, for example for
* the AOP {@link org.springframework.aop.framework.ProxyFactoryBean} or the
* {@link org.springframework.jndi.JndiObjectFactoryBean}. It can be used for
* custom components as well; however, this is only common for infrastructure code.
*
* <p><b>{@code FactoryBean} is a programmatic contract. Implementations are not
* supposed to rely on annotation-driven injection or other reflective facilities.</b>
* {@link #getObjectType()} {@link #getObject()} invocations may arrive early in the
* bootstrap process, even ahead of any post-processor setup. If you need access to
* other beans, implement {@link BeanFactoryAware} and obtain them programmatically.
*
* <p><b>The container is only responsible for managing the lifecycle of the FactoryBean
* instance, not the lifecycle of the objects created by the FactoryBean.</b> Therefore,
* a destroy method on an exposed bean object (such as {@link java.io.Closeable#close()}
* will <i>not</i> be called automatically. Instead, a FactoryBean should implement
* {@link DisposableBean} and delegate any such close call to the underlying object.
*
* <p>Finally, FactoryBean objects participate in the containing BeanFactory's
* synchronization of bean creation. There is usually no need for internal
* synchronization other than for purposes of lazy initialization within the
* FactoryBean itself (or the like).
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 08.03.2003
* @param <T> the bean type
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.aop.framework.ProxyFactoryBean
* @see org.springframework.jndi.JndiObjectFactoryBean
*/
public interface FactoryBean<T> {
T getObject() throws Exception;
Class<?> getObjectType();
default boolean isSingleton() { return true; }
}
从接口描述信息中我们可以得到以下几点关键信息:
1. 实现了BeanFactory接口的Bean,实际上对外暴露的是getObject方法返回的对象;
2. FactoryBean支持创建单例和原型Bean,可以通过懒加载或容器启动时加载的方式创建Bean,可以实现SmartFactoryBean接口来控制更多的Bean创建方式;
3. Spring只会管理FactoryBean对象本身,通过FactoryBean#getObject创建出来的对象的生命周期则不会由Spring管理。 也就是说通过getObject返回的对象本身即使实现了生命周期接口,也不会被调用;
第2、3点不在我们的讨论范围之内,也比较好理解,我们把关注点放在第1点这上。 通过getObject方法来创建对象? 我直接用@Bean的方式不行吗? 其实是可以的,在创建简单对象的场景中,直接使用@Bean甚至比使用BeanFactory的方式更合理、更简单。 那BeanFactory的使用场景到底是什么? 如果实现一个需求,@Bean与BeanFactory两种创建Bean的方式如何选择?
提供下我的思路供参考:
1. 如果你已经明确了要创建的Bean对象的Class的时候,这时候直接使用@Bean即可,如果无法拿到Class,则使用BeanFactory的方式。例如,你作为一个框架编写者,创建哪些Bean往往是由上层的框架使用者来决定的,这种情况下使用@Bean肯定是不行的,所以一定要用BeanFactory
2. 假设你可以拿到要创建的Bean对象的Class,但是不想暴露太多的Bean对象创建细节,如复杂的Bean创建过程,这种情况也可以使用BeanFactory 。但是什么才是复杂的Bean?如何不暴露创建细节?这个就需要你自己有非常足够多的说服力了。
所以个人认为,第一点才是决策的关键
我们在来简单解释下为什么说文中开始部分提到的BeanFactory与FactoryBean不是同一个层次的产物,相信你大概有所体会: BeanFactory 是Bean工厂、是容器,我们创建的Bean都在BeanFactory 中;而FactoryBean 只是容器创建Bean提供的一种方式而已, 可以说FactoryBean 就是为BeanFactory 服务的。
好,我们下面再来举个Spring集成Mybatis的例子加深下对FactoryBean 的理解
我们的业务代码中操作数据库,少不了使用@Resource、@Autowired等注解注入Mybatis的Mapper对象的场景, 这就说明这些Mapper对象的实例一定在Spring容器的管理之中,再往下想想,这些Mapper对象是如何被Spring纳入其中的? 其实就少不了 FactoryBean 接口的功劳, 想象一下你站在Mybatis框架实现者的角度,使用@Bean注解知否能实现?
我们看下框架实际是如何集成的,这里免不了贴一下源码,大家耐心看。
一、我们要知道Mapper文件的位置, Spring才能绑我们扫描, 但是光扫描还不行,因为Spring只会把扫描出来的Class转换为beanDefinition并放在DefaultListableBeanFactory#beanDefinitionMap容器中,Spring就是根据beanDefinitionMap的内容来帮我们创建Bean的, 可是现阶段我们只有Mapper的Class,直接把Mapper的Class放到beanDefinitionMap中,Spring只会创建该Class的实例,那结果肯定是无法实例化的。我们需要找到一种类似于’占位符‘的机制,先把这个’占位符‘放入beanDefinitionMap 中,在实际使用时,在通过’占位符‘把实际的Bean对象构建出来
这时候FactoryBean 就登场了,FactoryBean类似于上述中的’占位符‘, 由于FactoryBean 的特性,我们完全可以把FactoryBean 先注册到beanDefinitionMap中,在实际使用阶段,我们可以通过getObject方法从SqlSessionFactory中拿到Mybatis为我们创建好的的Mapper代理对象了
package org.mybatis.spring.mapper;
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
private String sqlSessionFactoryBeanName;
// MapperFactoryBean 实现了FactoryBean接口
private MapperFactoryBean<?> mapperFactoryBean = new MapperFactoryBean<Object>();
/**
* 实际扫描Mapper的的方法
*/
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 扫描出来的所有Mapper的BeanDefinition集合
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
// 调用下面的方法对Mapper对应的BeanDefinition做处理
processBeanDefinitions(beanDefinitions);
return beanDefinitions;
}
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
// 把Mapper的Class以构造方法的形式传进去
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
//注意这里,是设置的MapperFactoryBean的Class,意味着Spring要帮我们创建MapperFactoryBean对象
definition.setBeanClass(this.mapperFactoryBean.getClass());
//给MapperFactoryBean对象种注入sqlSessionFactory对象,在getObject()方法会用
definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
}
}
}
二、 到这里我们已经把MapperFactoryBean注册到beanDefinitionMap中了,后续我们在业务代码中注入Mapper的时候,实际注入的是MapperFactoryBean#getObject方法返回的代理对象,我们在来看下getObject的实现
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
private Class<T> mapperInterface;
// 有参构造方法的mapperInterface接口在上一步传入
public MapperFactoryBean(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
@Override
public T getObject() throws Exception {
// getSqlSession()实际就是通过我们的SqlSessionFactory对象调用openSession获取出来的
return getSqlSession().getMapper(this.mapperInterface);
}
@Override
public Class<T> getObjectType() {
return this.mapperInterface;
}
@Override
public boolean isSingleton() {
return true;
}
}
好,到了这里相信你已经明白了Spring是如何集成Mybatis的了
这一篇主要讲了FactoryBean 的用法以及与BeanFactory的简单比较,说明了他们的区别,并且借助于Spring集成Mybatis的实际案例了解了FactoryBean 在实际中的用法,其实FactoryBean的身影无处不在,在AOP以及Spring与其他框架的集成中会经常用到。
在最后简单说明一下:在实际工作中FactoryBean 接口用的并不是很多,因为大部分我们都在实现业务需求,直接使用@Bean方式创建Bean即可; 如果你是一位中间件、框架开发者,亦或是为了代码的可拓展性实现一个功能,那FactoryBean 将是你的不二之选。
1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,
我一直在为使用acts_as_list的模型实现一些不错的交互界面,这些界面可以对我的mRails应用程序中的列表进行排序。我有一个排序函数,在每次拖放之后使用sortable_elementscript.aculo.us函数调用并设置每条记录的位置。这是在拖放完成后处理排序的Controller操作示例:defsortparams[:documents].each_with_indexdo|id,index|Document.update_all(['position=?',index+1],['id=?',id])endend现在我正在尝试对嵌套集模型(acts_as_nested
最近在工作中,看到一些新手测试同学,对接口测试存在很多疑问,甚至包括一些从事软件测试3,5年的同学,在聊到接口时,也是一知半解;今天借着这个机会,对接口测试做个实战教学,顺便总结一下经验,分享给大家。计划拆分成4个模块跟大家做一个分享,(接口测试、接口基础知识、接口自动化、接口进阶)感兴趣的小伙伴记得关注,希望对你的日常工作和求职面试,带来一些帮助。注:文章较长有5000多字,希望小伙伴们认真看完,当然有些内容对小白同学不是太友好,如果你需要详细了解其中的一些概念或者名词,请在文章之后留言,后续我将针对大家的疑问,整理输出一些大家感兴趣的文章。随着开发模式的迭代更新,前后端分离已不是新的概念,
我正在尝试在我的Rails应用程序中使用模型来从外部API检索信息。我想做的是以类似于ActiveRecord模型提供的方式(特别是关联,以及相同风格的可链接查询方法)访问我的数据模型(可能包含来自多个API调用的信息)。我最初的直觉是重新创建我想要的ActiveRecord部分并合并此API。不想“重新发明轮子”并确切地看到添加更多功能需要多少工作让我退后一步并重新评估如何处理这个问题。我找到了在没有表的情况下使用ActiveRecord的方法(请参阅:Railscast#193TablelessModel和博客文章here)并研究了ActiveRecord。因为ActiveMode
原文:UsetheIComparableandIComparerinterfacesinVisualCSharp本文介绍了在VisualC#中如何使用IComparer和IComparable接口。概要本文同时讨论了IComparable和IComparer接口,原因有两点。这两个接口经常一起使用。虽然接口类似且名称相似,但它们却有不同的用途。如果你有一个支持IComparer的类型数组(例如字符串或整数),你可以对它进行排序而不需要提供任何对IComparer的显式引用(译注:意思是把一个IComparer的实现类作为参数传递给排序方法)。在这种情况下,数组元素会被转换为IComparer的
如果你想在自己的应用中使用淘宝的数据,那么对接淘宝数据接口是必不可少的一步。本文将介绍如何对接API淘宝数据接口,以便你能够顺利获取和使用淘宝的数据。步骤一:获取AppKey和AppSecret首先,在淘宝开放平台申请API接口之前,需要先注册为淘宝开发者并创建应用。创建应用后,你将得到一个AppKey和AppSecret,这两个参数需要在调用API接口时用于身份验证。步骤二:确定需要调用的API接口在淘宝开放平台的开发文档中,你将找到所有可用的API接口。你需要根据你的需求找到需要调用的API接口,例如查询商品、店铺等信息。在找到需要的API接口后,你需要了解该接口的请求参数和返回结
我正在使用delayed_job_webgem来监控延迟的作业。https://github.com/ejschmitt/delayed_job_web可以使用我的routes.rb中的这一行访问它:match"/delayed_job"=>DelayedJobWeb,:anchor=>false我站点的每个其他区域都需要使用Devisegem登录。我如何使它也需要登录?在自述文件中,他们建议将以下内容添加到config.rb:ifRails.env.production?DelayedJobWeb.useRack::Auth::Basicdo|username,password|us
根据officialrubyAboutpage用C扩展Ruby比用Perl更容易。我不是(perl)XS专家,但我发现使用Inline::C快速简单地编写一些东西非常简单,那么为什么在Ruby中更容易呢?WritingCextensionsinRubyiseasierthaninPerlorPython,withaveryelegantAPIforcallingRubyfromC.ThisincludescallsforembeddingRubyinsoftware,foruseasascriptinglanguage.ASWIGinterfaceisalsoavailable.那些做
Ruby是否与其他OOP语言(例如:PHP)不同,使接口(interface)变得无用?它有某种替代品吗?编辑:一些说明:在其他语言中(例如:PHP),您并不“需要”接口(interface)(它们在代码级别不是强制性的)。你用它们来订立契约(Contract),改进软件的架构。因此,'在ruby中你不需要接口(interface)/在其他语言中你需要接口(interface)因为XXX'的断言是错误的。不,混合不是接口(interface),它们是完全不同的东西(PHP5.4实现了混合)。你用过接口(interface)吗?是的,PHP是OOP。语言不断发展,欢迎来到现在。
问题标题的可怕之处是我要解决的问题。示例:在Ruby中,Enumerable是某种意义上的接口(interface),我可以实现某些东西并将其记录为:defmyfancymethod(please_pass_me_an_Enumerable_here)但另一方面,Enumerable是一种接口(interface)的放大,它以#each作为其方法之一。如果我有课classFoodefeach:barendend对于那些不熟悉Ruby的人来说,如果你在一个类中混入Enumerable模块,你会得到许多只依赖于#each方法来提供诸如#map之类的方法,#select等我可以说我的Foo类