生活随笔
收集整理的这篇文章主要介绍了
手写简版spring --9--对象作用域和FactoryBean
小编觉得挺不错的,现在分享给大家,帮大家做个参考.
一、目标
交给 Spring 管理的 Bean 对象,一定就是我们用类创建出来的 Bean 吗?创建出来的 Bean 就永远是单例的吗,没有可能是原型模式吗?在集合 Spring 框架下,我们使用的 MyBatis 框架中,它的核心作用是可以满足用户不需要实现 Dao 接口类,就可以通过 xml 或者注解配置的方式完成对数据库执行 CRUD 操作,那么在实现这样的 ORM 框架中,是怎么把一个数据库操作的 Bean 对象交给 Spring 管理的呢。因为我们在使用 Spring、MyBatis 框架的时候都可以知道,并没有手动的去创建任何操作数据库的 Bean 对象,有的仅仅是一个接口定义,而这个接口定义竟然可以被注入到其他需要使用 Dao 的属性中去了,那么这一过程最核心待解决的问题,就是需要完成把复杂且以代理方式动态变化的对象,注册到 Spring 容器中。而为了满足这样的一个扩展组件开发的需求,就需要我们在现有手写的 Spring 框架中,添加这一能力。
二、方案
关于提供一个能让使用者定义复杂的 Bean 对象,功能点非常不错,意义也非常大,因为这样做了之后 Spring 的生态种子孵化箱就此提供了,谁家的框架都可以在此标准上完成自己服务的接入。但这样的功能逻辑设计上并不复杂,因为整个 Spring 框架在开发的过程中就已经提供了各项扩展能力的接茬,你只需要在合适的位置提供一个接茬的处理接口调用和相应的功能逻辑实现即可,像这里的目标实现就是对外提供一个可以二次从 FactoryBean 的 getObject 方法中获取对象的功能即可,这样所有实现此接口的对象类,就可以扩充自己的对象功能了。MyBatis 就是实现了一个 MapperFactoryBean 类,在 getObject 方法中提供 SqlSession 对执行 CRUD 方法的操作 整体设计结构如下图:
整个的实现过程包括了两部分,一个解决单例还是原型对象,另外一个处理 FactoryBean 类型对象创建过程中关于获取具体调用对象的getObject 操作。 SCOPE_SINGLETON、SCOPE_PROTOTYPE,对象类型的创建获取方式,主要区分在于 AbstractAutowireCapableBeanFactory#createBean 创建完成对象后是否放入到内存中,如果不放入则每次获取都会重新创建。 createBean执行对象创建、属性填充、依赖加载、前置后置处理、初始化等操作后,就要开始做执行判断整个对象是否是一个 FactoryBean对象,如果是这样的对象,就需要再继续执行获取 FactoryBean 具体对象中的 getObject 对象了。整个 getBean过程中都会新增一个单例类型的判断factory.isSingleton(),用于决定是否使用内存存放对象信息。
四、实现
工程结构
small
- spring
- step
- 09
└── src├── main│ └── java│ └── cn
. bugstack
. springframework│ ├── beans│ │ ├── factory│ │ │ ├── config│ │ │ │ ├──
AutowireCapableBeanFactory . java│ │ │ │ ├──
BeanDefinition . java│ │ │ │ ├──
BeanFactoryPostProcessor . java│ │ │ │ ├──
BeanPostProcessor . java│ │ │ │ ├──
BeanReference . java│ │ │ │ ├──
ConfigurableBeanFactory . java│ │ │ │ └──
SingletonBeanRegistry . java│ │ │ ├── support│ │ │ │ ├──
AbstractAutowireCapableBeanFactory . java│ │ │ │ ├──
AbstractBeanDefinitionReader . java│ │ │ │ ├──
AbstractBeanFactory . java│ │ │ │ ├──
BeanDefinitionReader . java│ │ │ │ ├──
BeanDefinitionRegistry . java│ │ │ │ ├──
CglibSubclassingInstantiationStrategy . java│ │ │ │ ├──
DefaultListableBeanFactory . java│ │ │ │ ├──
DefaultSingletonBeanRegistry . java│ │ │ │ ├──
DisposableBeanAdapter . java│ │ │ │ ├──
FactoryBeanRegistrySupport . java│ │ │ │ ├──
InstantiationStrategy . java│ │ │ │ └──
SimpleInstantiationStrategy . java │ │ │ ├── support│ │ │ │ └──
XmlBeanDefinitionReader . java│ │ │ ├──
Aware . java│ │ │ ├──
BeanClassLoaderAware . java│ │ │ ├──
BeanFactory . java│ │ │ ├──
BeanFactoryAware . java│ │ │ ├──
BeanNameAware . java│ │ │ ├──
ConfigurableListableBeanFactory . java│ │ │ ├──
DisposableBean . java│ │ │ ├──
FactoryBean . java│ │ │ ├──
HierarchicalBeanFactory . java│ │ │ ├──
InitializingBean . java│ │ │ └──
ListableBeanFactory . java│ │ ├──
BeansException . java│ │ ├──
PropertyValue . java│ │ └──
PropertyValues . java │ ├── context│ │ ├── support│ │ │ ├──
AbstractApplicationContext . java │ │ │ ├──
AbstractRefreshableApplicationContext . java │ │ │ ├──
AbstractXmlApplicationContext . java │ │ │ ├──
ApplicationContextAwareProcessor . java │ │ │ └──
ClassPathXmlApplicationContext . java │ │ ├──
ApplicationContext . java │ │ ├──
ApplicationContextAware . java │ │ └──
ConfigurableApplicationContext . java│ ├── core
. io│ │ ├──
ClassPathResource . java │ │ ├──
DefaultResourceLoader . java │ │ ├──
FileSystemResource . java │ │ ├──
Resource . java │ │ ├──
ResourceLoader . java │ │ └──
UrlResource . java│ └── utils│ └──
ClassUtils . java└── test└── java└── cn
. bugstack
. springframework
. test├── bean│ ├──
UserDao . java│ └──
UserService . java└──
ApiTest . java
Spring 单例、原型以及 FactoryBean 功能实现类关系,如图
以上整个类关系图展示的就是添加 Bean 的实例化是单例还是原型模式以及 FactoryBean 的实现。 其实整个实现的过程并不复杂,只是在现有的 AbstractAutowireCapableBeanFactory 类以及继承的抽象类 AbstractBeanFactory 中进行扩展。 不过这次我们把 AbstractBeanFactory 继承的DefaultSingletonBeanRegistry 类,中间加了一层 FactoryBeanRegistrySupport,这个类在Spring 框架中主要是处理关于 FactoryBean 注册的支撑操作。
Bean的作用范围定义和xml解析
public class BeanDefinition { String SCOPE_SINGLETON
= ConfigurableBeanFactory . SCOPE_SINGLETON
; String SCOPE_PROTOTYPE
= ConfigurableBeanFactory . SCOPE_PROTOTYPE
; private Class beanClass
; private PropertyValues propertyValues
; private String initMethodName
; private String destroyMethodName
; private String scope
= SCOPE_SINGLETON
; private boolean singleton
= true ; private boolean prototype
= false ;
}
singleton、prototype,是本次在 BeanDefinition 类中新增加的两个属性信息,用于把从 spring.xml中解析到的 Bean 对象作用范围填充到属性中。
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { protected void doLoadBeanDefinitions ( InputStream inputStream
) throws ClassNotFoundException { for ( int i
= 0 ; i
< childNodes
. getLength ( ) ; i
++ ) { if ( ! ( childNodes
. item ( i
) instanceof Element ) ) continue ; if ( ! "bean" . equals ( childNodes
. item ( i
) . getNodeName ( ) ) ) continue ; Element bean
= ( Element ) childNodes
. item ( i
) ; String id
= bean
. getAttribute ( "id" ) ; String name
= bean
. getAttribute ( "name" ) ; String className
= bean
. getAttribute ( "class" ) ; String initMethod
= bean
. getAttribute ( "init-method" ) ; String destroyMethodName
= bean
. getAttribute ( "destroy-method" ) ; String beanScope
= bean
. getAttribute ( "scope" ) ; Class < ? > clazz
= Class . forName ( className
) ; String beanName
= StrUtil . isNotEmpty ( id
) ? id
: name
; if ( StrUtil . isEmpty ( beanName
) ) { beanName
= StrUtil . lowerFirst ( clazz
. getSimpleName ( ) ) ; } BeanDefinition beanDefinition
= new BeanDefinition ( clazz
) ; beanDefinition
. setInitMethodName ( initMethod
) ; beanDefinition
. setDestroyMethodName ( destroyMethodName
) ; if ( StrUtil . isNotEmpty ( beanScope
) ) { beanDefinition
. setScope ( beanScope
) ; } getRegistry ( ) . registerBeanDefinition ( beanName
, beanDefinition
) ; } }
}
在解析 XML 处理类 XmlBeanDefinitionReader 中,新增加了关于 Bean 对象配置中 scope 的解析,并把这个属性信息填充到 Bean 定义中。beanDefinition.setScope(beanScope)
创建和修改对象时候判断单例和原型模式
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { private InstantiationStrategy instantiationStrategy
= new CglibSubclassingInstantiationStrategy ( ) ; @Override protected Object createBean ( String beanName
, BeanDefinition beanDefinition
, Object [ ] args
) throws BeansException { Object bean
= null ; try { bean
= createBeanInstance ( beanDefinition
, beanName
, args
) ; applyPropertyValues ( beanName
, bean
, beanDefinition
) ; bean
= initializeBean ( beanName
, bean
, beanDefinition
) ; } catch ( Exception e
) { throw new BeansException ( "Instantiation of bean failed" , e
) ; } registerDisposableBeanIfNecessary ( beanName
, bean
, beanDefinition
) ; if ( beanDefinition
. isSingleton ( ) ) { addSingleton ( beanName
, bean
) ; } return bean
; } protected void registerDisposableBeanIfNecessary ( String beanName
, Object bean
, BeanDefinition beanDefinition
) { if ( ! beanDefinition
. isSingleton ( ) ) return if ( bean
instanceof DisposableBean || StrUtil . isNotEmpty ( beanDefinition
. getDestroyMethodName ( ) ) ) { registerDisposableBean ( beanName
, new DisposableBeanAdapter ( bean
, beanName
, beanDefinition
) ) ; } }
}
单例模式和原型模式的区别就在于是否存放到内存中,如果是原型模式那么就不会存放到内存中,每次获取都重新创建对象,另外非 Singleton类型的 Bean 不需要执行销毁方法。 所以这里的代码会有两处修改,一处是 createBean 中判断是否添加到addSingleton(beanName, bean);,另外一处是 registerDisposableBeanIfNecessary销毁注册中的判断 if (!beanDefinition.isSingleton()) return;。
定义 FactoryBean 接口
public interface FactoryBean < T > { T getObject ( ) throws Exception ; Class < ? > getObjectType ( ) ; boolean isSingleton ( ) ;
}
FactoryBean 中需要提供3个方法,获取对象、对象类型,以及是否是单例对象,如果是单例对象依然会被放到内存中。
实现一个 FactoryBean 注册服务
public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry { private final Map < String , Object > factoryBeanObjectCache
= new ConcurrentHashMap < String , Object > ( ) ; protected Object getCachedObjectForFactoryBean ( String beanName
) { Object object
= this . factoryBeanObjectCache
. get ( beanName
) ; return ( object
!= NULL_OBJECT
? object
: null ) ; } protected Object getObjectFromFactoryBean ( FactoryBean factory
, String beanName
) { if ( factory
. isSingleton ( ) ) { Object object
= this . factoryBeanObjectCache
. get ( beanName
) ; if ( object
== null ) { object
= doGetObjectFromFactoryBean ( factory
, beanName
) ; this . factoryBeanObjectCache
. put ( beanName
, ( object
!= null ? object
: NULL_OBJECT
) ) ; } return ( object
!= NULL_OBJECT
? object
: null ) ; } else { return doGetObjectFromFactoryBean ( factory
, beanName
) ; } } private Object doGetObjectFromFactoryBean ( final FactoryBean factory
, final String beanName
) { try { return factory
. getObject ( ) ; } catch ( Exception e
) { throw new BeansException ( "FactoryBean threw exception on object[" + beanName
+ "] creation" , e
) ; } } }
FactoryBeanRegistrySupport 类主要处理的就是关于 FactoryBean此类对象的注册操作,之所以放到这样一个单独的类里,就是希望做到不同领域模块下只负责各自需要完成的功能,避免因为扩展导致类膨胀到难以维护。 同样这里也定义了缓存操作factoryBeanObjectCache,用于存放单例类型的对象,避免重复创建。在日常使用用,基本也都是创建的单例对象 doGetObjectFromFactoryBean 是具体的获取 FactoryBean#getObject()方法,因为既有缓存的处理也有对象的获取,所以额外提供了 getObjectFromFactoryBean进行逻辑包装,这部分的操作方式是不和你日常做的业务逻辑开发非常相似。从Redis取数据,如果为空就从数据库获取并写入Redis
扩展 AbstractBeanFactory 创建对象逻辑
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory { protected < T > T doGetBean ( final String name
, final Object [ ] args
) { Object sharedInstance
= getSingleton ( name
) ; if ( sharedInstance
!= null ) { return ( T ) getObjectForBeanInstance ( sharedInstance
, name
) ; } BeanDefinition beanDefinition
= getBeanDefinition ( name
) ; Object bean
= createBean ( name
, beanDefinition
, args
) ; return ( T ) getObjectForBeanInstance ( bean
, name
) ; } private Object getObjectForBeanInstance ( Object beanInstance
, String beanName
) { if ( ! ( beanInstance
instanceof FactoryBean ) ) { return beanInstance
; } Object object
= getCachedObjectForFactoryBean ( beanName
) ; if ( object
== null ) { FactoryBean < ? > factoryBean
= ( FactoryBean < ? > ) beanInstance
; object
= getObjectFromFactoryBean ( factoryBean
, beanName
) ; } return object
; }
}
首先这里把 AbstractBeanFactory 原来继承的 DefaultSingletonBeanRegistry,修改为继承 FactoryBeanRegistrySupport。因为需要扩展出创建 FactoryBean对象的能力,所以这就想一个链条服务上,截出一个段来处理额外的服务,并把链条再链接上。 此处新增加的功能主要是在 doGetBean方法中,添加了调用 (T) getObjectForBeanInstance(sharedInstance, name) 对获取 FactoryBean 的操作。 在 getObjectForBeanInstance 方法中做具体的 instanceof判断,另外还会从 FactoryBean 的缓存中获取对象,如果不存在则调用FactoryBeanRegistrySupport#getObjectFromFactoryBean,执行具体的操作。
四、测试
事先准备
public interface IUserDao { String queryUserName ( String uId
) ;
}
这个章节我们删掉 UserDao,定义一个 IUserDao 接口,之所这样做是为了通过 FactoryBean做一个自定义对象的代理操作。
public class UserService { private String uId
; private String company
; private String location
; private IUserDao userDao
; public String queryUserInfo ( ) { return userDao
. queryUserName ( uId
) + "," + company
+ "," + location
; }
}
在 UserService 新修改了一个原有 UserDao 属性为 IUserDao,后面我们会给这个属性注入代理对象。
定义 FactoryBean 对象
public class ProxyBeanFactory implements FactoryBean < IUserDao > { @Override public IUserDao getObject ( ) throws Exception { InvocationHandler handler
= ( proxy
, method
, args
) -> { Map < String , String > hashMap
= new HashMap < > ( ) ; hashMap
. put ( "10001" , "小傅哥" ) ; hashMap
. put ( "10002" , "八杯水" ) ; hashMap
. put ( "10003" , "阿毛" ) ; return "你被代理了 " + method
. getName ( ) + ":" + hashMap
. get ( args
[ 0 ] . toString ( ) ) ; } ; return ( IUserDao ) Proxy . newProxyInstance ( Thread . currentThread ( ) . getContextClassLoader ( ) , new Class [ ] { IUserDao . class } , handler
) ; } @Override public Class < ? > getObjectType ( ) { return IUserDao . class ; } @Override public boolean isSingleton ( ) { return true ; }
}
这是一个实现接口 FactoryBean 的代理类 ProxyBeanFactory 名称,主要是模拟了 UserDao 的原有功能,类似于 MyBatis 框架中的代理操作。 getObject() 中提供的就是一个 InvocationHandler的代理对象,当有方法调用的时候,则执行代理对象的功能。
配置文件
<?xml version="1.0" encoding="UTF-8"?>
< beans> < bean id = " userService" class = " cn.bugstack.springframework.test.bean.UserService" scope = " prototype" > < property name = " uId" value = " 10001" /> < property name = " company" value = " 腾讯" /> < property name = " location" value = " 深圳" /> < property name = " userDao" ref = " proxyUserDao" /> </ bean> < bean id = " proxyUserDao" class = " cn.bugstack.springframework.test.bean.ProxyBeanFactory" />
</ beans>
在配置文件中,我们把 proxyUserDao 这个代理对象,注入到 userService 的 userDao 中。与上一章节相比,去掉了 UserDao 实现类,转而用代理类替换
单元测试(单例&原型)
@Test
public void test_prototype ( ) { ClassPathXmlApplicationContext applicationContext
= new ClassPathXmlApplicationContext ( "classpath:spring.xml" ) ; applicationContext
. registerShutdownHook ( ) ; UserService userService01
= applicationContext
. getBean ( "userService" , UserService . class ) ; UserService userService02
= applicationContext
. getBean ( "userService" , UserService . class ) ; System . out
. println ( userService01
) ; System . out
. println ( userService02
) ; System . out
. println ( userService01
+ " 十六进制哈希:" + Integer . toHexString ( userService01
. hashCode ( ) ) ) ; System . out
. println ( ClassLayout . parseInstance ( userService01
) . toPrintable ( ) ) ;
}
在 spring.xml 配置文件中,设置了 scope=”prototype” 这样就每次获取到的对象都应该是一个新的对象。 这里判断对象是否为一个会看到打印的类对象的哈希值,所以我们把十六进制哈希打印出来。
单元测试(代理对象)
@Test
public void test_factory_bean ( ) { ClassPathXmlApplicationContext applicationContext
= new ClassPathXmlApplicationContext ( "classpath:spring.xml" ) ; applicationContext
. registerShutdownHook ( ) ; UserService userService
= applicationContext
. getBean ( "userService" , UserService . class ) ; System . out
. println ( "测试结果:" + userService
. queryUserInfo ( ) ) ;
}
关于 FactoryBean 的调用并没有太多不一样,因为所有的不同都已经被 spring.xml 配置进去了。当然你可以直接调用 spring.xml 配置的对象
从测试结果来看,我们的代理类 ProxyBeanFactory 已经完美替换掉了 UserDao 的功能。 虽然看上去这一点实现并不复杂,甚至有点简单。但就是这样一点点核心内容的设计了,解决了所有需要和 Spring结合的其他框架交互链接问题。如果对此类内容感兴趣,也可以阅读小傅哥《中间件设计和开发》
五、总结
在 Spring框架整个开发的过程中,前期的各个功能接口类扩展的像膨胀了似的,但到后期在完善功能时,就没有那么难了,反而深入理解后会觉得功能的补充,都比较简单。只需要再所遇领域范围内进行扩展相应的服务实现即可。 当你仔细阅读完关于 FactoryBean 的实现以及测试过程的使用,以后再需要使用 FactoryBean开发相应的组件时候,一定会非常清楚它是如何创建自己的复杂 Bean 对象以及在什么时候初始化和调用的。遇到问题也可以快速的排查、定位和解决。 如果你在学习的过程中感觉这些类、接口、实现、继承,穿梭的很复杂,一时半会脑子还反应不过来。那么你最好的方式是动手去画画这些类关系图,梳理下实现的结构,看看每个类在干什么。看只能是知道,动手才能学会!
总结
以上是生活随笔 为你收集整理的手写简版spring --9--对象作用域和FactoryBean 的全部内容,希望文章能够帮你解决所遇到的问题。
如果觉得生活随笔 网站内容还不错,欢迎将生活随笔 推荐给好友。