标签:
Zebra-Dao是为了解决用户访问数据库进行一步方式而封装的Dao,目前java异步访问方式主要有线程池技术、消息中间件技术等。
**目前数据库异步访问方案主要有以下两种。
1.业务在使用时自己将每一次的dao调用放到异步线程池中去。 优点:简单,不需要架构支持什么。缺点: 因为是业务迁移,迁移的代价比较大。
2.使用google的async-mysql-connector。优点:背后实现是基于NIO的方式,性能更高。缺点:异步的jdbc接口上层没有任何的DAO框架支持,公司业务基本无法使用。**
而目前常用的DAO框架mybatis和hibernate都是基于同步方式访问数据库,一旦数据库操作时间过长,线程将会一直阻塞下去。这对一些要求时效比较快的业务显然无法满足。因此Zebra尝试在mybatis基础上异步化封装。Zebra主要基于在Spring-mybatis基础上做了相应的封装,低层仍然采用mybatis。下面将对Zebra-Dao具体封装过程及原理做简要分析。
Zebra的核心配置类是ZebraMapperScannerConfigurer ,该类在MapperScannerConfigurer基础上增减了线程池配置功能,因为Zebra-Dao是基础线程池来进行异步化封装。该类实
现了BeanDefinitionRegistryPostProcessor, InitializingBean,ApplicationContextAware, BeanNameAware这几个接口,通过实现这几个接口,在Spring初始化过程中为Mapper接口创
建了动态代理类,并将其注册到Spring容器中。事实上Zebra-Dao为Mapper对象重新创建了一次动态代理过程,在分析Zebra-Dao过程中发现,原有Spring-mybatis其实已经为接口创建
了动态代理,更准确地讲是Spring-mybatis是动态地实现了Mapper接口,深入源码中发现Spring-mybatis并没有在代理类中调用 method.invoke(object)方法。Zebra-Dao在Spring-mybatis
动态实现接口的基础上获取起实现接口对象,在该对象的基础上进一步利用JDK动态代理技术将spring-mybatis动态实现的接口对象创建了代理类,并在代理类中进行异步化操作数据库。
实际使用接口时,将会将会调用代理类的method.invoke(object)。在改方法中Zebra-Dao将操作数据库封装成线程扔给线程池去执行,从而实现了异步化处理。下面本文对整个初始化过程中
源码进行浅要分析。
下面将以UserMapper接口作为示例。
public interface UserMapper {
public void findUserById(@Param("userId") int userId, AsyncDaoCallback<UserEntity> callback);
public UserEntity findUserById(@Param("userId") int userId);
@TargetMethod(name = "findUserById")
public Future<UserEntity> findUserByIdByFuture(@Param("userId") int userId);
public List<UserEntity> getAll();
@TargetMethod(name = "getAll")
public Future<List<UserEntity>> getAll1();
public int insertUser(UserEntity user);
@TargetMethod(name = "insertUser")
public void insertUser1(UserEntity user, AsyncDaoCallback<Integer> callback);
@TargetMethod(name = "insertUser")
public Future<Integer> insertUser2(UserEntity user);
}
在Zebra-Dao的使用过程中需要对ZebraMapperScannerConfigurer进行配置,与spring-mybatis唯一区别的是需要配置线程池。
<bean class="com.dianping.zebra.dao.mybatis.ZebraMapperScannerConfigurer">
<property name="basePackage" value="com.dianping.zebra.dao.mapper" />
<property name="corePoolSize" value="${zebra-dao.executepool.corePoolSize}"></property>
</bean>
ZebraMapperScannerConfigurer重新实现了Spring-mybatis中的MapperScannerConfigurer类,该类的主要功能是将base 包中搜索所有下面所有 interface,并将其注册到 Spring Bean容器中,其注册的class bean是ZebraMapperFactoryBean。 下面将对这个类中的重要方法做一个介绍。
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
ZebraClassPathMapperScanner scanner = new ZebraClassPathMapperScanner(registry);
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setSqlSessionFactory(this.sqlSessionFactory);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
scanner.registerFilters();
//为接口创建了动态代理
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
该方法将在spring初始化时执行。主要用于是搜索 base packages 下的所有mapper class,并将其注册至 spring 的 benfinitionHolder中。 其中主要方法在scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));执行,该方法调用了父类ClassPathBeanDefinitionScanner的scan方法,其中csan方法中调用了doScan(basePackages);改方法
被protected修饰,用于子类实现,在Zebra-Dao中设计了ZebraClassPathMapperScanner类,该类重写了父类ClassPathBeanDefinitionScanner的doSan(basePackages)方法。
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
/**调用父类方法获取制定包下面所有接口的BeanDefinitionHolder对象,在Spring初始化Bean过程,Spring 首先会将需 要初始化的所有class先通过BeanDefinitionRegistry进行注册,并且将该Bean的一些属性信息(如scope、className、beanName等)保存至BeanDefinitionHolder中;Zebra-Dao这里首先会调用Spring中的ClassPathBeanDefinitionScanner.doScan方法,将所有Mapper接口的class注册至BeanDefinitionHolder实例中,然后返回一个Set<BeanDefinitionHolder>,其它包含了所有搜索到的Mapper class BeanDefinitionHolder对象。*/
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
logger.warn("No MyBatis mapper was found in ‘" + Arrays.toString(basePackages)
+ "‘ package. Please check your configuration.");
} else {
/** 中for循环中替换当前所有 holder的 className为ZebraMapperFactoryBea.class,并且将 mapper interface 的class name setter 至 MapperFactoryBean属性为 mapperInterface 中*/
for (BeanDefinitionHolder holder : beanDefinitions) {
GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
if (logger.isDebugEnabled()) {
logger.debug("Creating MapperFactoryBean with name ‘" + holder.getBeanName() + "‘ and ‘"
+ definition.getBeanClassName() + "‘ mapperInterface");
}
//把接口的类型设置进去
definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
//设置Bean的真实类型ZebraMapperFactoryBean
definition.setBeanClass(ZebraMapperFactoryBean.class);
//是否把Mapper接口加入到Mybatis的Config当中去
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false;
//如果sqlSessionFactoryBeanName的名字不为空 则在Spring容器中查询
//适合多数据源
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {definition.getPropertyValues().add("sqlSessionFactory",
new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
}
//如果sqlSessionTemplateBeanName的名字不为空 则在Spring容器中查询,适合多数据源
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate",
new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
if (explicitFactoryUsed) {
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
if (!explicitFactoryUsed) {
if (logger.isDebugEnabled()) {
logger.debug("Enabling autowire by type for MapperFactoryBean with name ‘" + holder.getBeanName()
+ "‘.");
}
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
}
return beanDefinitions;
}
到此为止Zebra-Dao的初始化基本完成。当我们使用Mapper接口注入实例时,由于Zebra-Dao给接口注册的都是ZebraMapperFactoryBean,因此Mapper接口实例将需要ZebraMapperFactoryBean
来创建。下面将进入到ZebraMapperFactoryBean类中,有个方法checkDaoConfig()
@Override
protected void checkDaoConfig() {
super.checkDaoConfig();
notNull(this.mapperInterface, "Property ‘mapperInterface‘ is required");
Configuration configuration = getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
//将mapper接口添加到全局配置对象中;
configuration.addMapper(this.mapperInterface);
} catch (Throwable t) {
logger.error("Error while adding the mapper ‘" + this.mapperInterface + "‘ to configuration.", t);
throw new IllegalArgumentException(t);
} finally {
ErrorContext.instance().reset();
}
}
}
该方法将会在spring初始化后执行,因为该方法重写了父类SqlSessionDaoSupport中checkDaoConfig() 方法,而SqlSessionDaoSupport实现了抽象类中DaoSupport抽象方法checkDaoConfig(),DaoSupport同时也实现了InitializingBean接口并在接口方法中调用了抽象方法checkDaoConfig(),因此spring初始化后最终调用的是上述方法。上面方法中先会检测当前需要创建的 mapperInterface在全局的 Configuration中是否存在,如果不存在则添加。该方法调用完成之后,就开始调用 getObject方法来产生mapper实例,看到ZebraMapperFactoryBean.getObject的该方法代码如下:
public T getObject() throws Exception {
T mapper = getSqlSession().getMapper(this.mapperInterface);
AsyncMapperProxy<T> asyncMapperProxy = new AsyncMapperProxy<T>(mapper);
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, asyncMapperProxy);
}
/**在SqlSession中的getMapper()方法中继续调用getConfiguration().getMapper(type, this);跟踪进去发现接着调用了mapperRegistry.getMapper(type, sqlSession); 方法,再跟踪进去将会发现调用了下面方法*/
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null)
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
改方法中获取到了Mapper接口实例对象,该对象是一个代理对象。进一步深入可以发现在代理对象中执行了此方法操作数据库
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
到此为止我们获得了接口的代理对象,对此,进一步回到
public T getObject() throws Exception {
T mapper = getSqlSession().getMapper(this.mapperInterface);
AsyncMapperProxy<T> asyncMapperProxy = new AsyncMapperProxy<T>(mapper);
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, asyncMapperProxy);
}
方法中,此时我们的mapper对象就是接口的代理对象,也可以说是动态实现对象,获得此对象后此时该对象的一些列操作还是一个同步操作,为了实现异步操作,Zebra-Dao进一步对该对象做了异步化封装。
将mapper封装成一个异步代理类AsyncMapperProxy,最后进一步使用动态代理技术对为mapper对象创建代理对象。下面我们将到AsyncMapperProxy一探究竟。
未完待续
标签:
原文地址:http://blog.csdn.net/wenbo20182/article/details/51931692