摘要:声明解决方案是基于源码,进行二次开发实现。其是基于层面,不存在任何的接口实现类。因而在实现的过程中,首先要解决的是如何动态实现接口的实例化。其次是如何将使接口根据注解实现相应的功能。
声明解决方案是基于Mybatis源码,进行二次开发实现。
问题领导最近跟我提了一个需求,是有关于实现类Mybatis的@Select、@Insert注解的功能。其是基于interface层面,不存在任何的接口实现类。因而在实现的过程中,首先要解决的是如何动态实现接口的实例化。其次是如何将使接口根据注解实现相应的功能。
我们先来看看Mybatis是如何实现Dao类的扫描的。
MapperScannerConfigurer.java
</>复制代码
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
ClassPathMapperScanner scanner = new ClassPathMapperScanner(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));
}
ClassPathMapperScanner是Mybatis继承ClassPathBeanDefinitionScanner类而来的。这里对于ClassPathMapperScanner的配置参数来源于我们在使用Mybatis时的配置而来,是不是还记得在使用Mybatis的时候要配置basePackage的参数呢?
接着我们就顺着scanner.scan()方法,进入查看一下里面的实现。
ClassPathBeanDefinitionScanner.java
</>复制代码
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
doScan(basePackages);
// Register annotation config processors, if necessary.
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
这里关键的代码是doScan(basePackages);,那么我们在进去看一下。可能你会看到的是Spring源码的实现方法,但这里Mybatis也实现了自己的一套,我们看一下Mybatis的实现。
ClassPathMapperScanner.java
</>复制代码
public Set doScan(String... basePackages) {
Set 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 (BeanDefinitionHolder holder : beanDefinitions) {
GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
if (logger.isDebugEnabled()) {
logger.debug("Creating MapperFactoryBean with name "" + holder.getBeanName()
+ "" and "" + definition.getBeanClassName() + "" mapperInterface");
}
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
definition.setBeanClass(MapperFactoryBean.class);
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false;
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;
}
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;
}
definition.setBeanClass(MapperFactoryBean.class);这行代码是非常关键的一句,由于在Spring中存在两种自动实例化的方式,一种是我们常用的本身的接口实例化类进行接口实例化,还有一种就是这里的自定义实例化。而这里的setBeanClass方法就是在BeanDefinitionHolder中进行配置。在Spring进行实例化的时候进行处理。
那么我们在看一下MapperFactoryBean.class
MapperFactoryBean.java
</>复制代码
public class MapperFactoryBean extends SqlSessionDaoSupport implements FactoryBean {
private Class mapperInterface;
private boolean addToConfig = true;
/**
* Sets the mapper interface of the MyBatis mapper
*
* @param mapperInterface class of the interface
*/
public void setMapperInterface(Class mapperInterface) {
this.mapperInterface = mapperInterface;
}
/**
* If addToConfig is false the mapper will not be added to MyBatis. This means
* it must have been included in mybatis-config.xml.
*
* If it is true, the mapper will be added to MyBatis in the case it is not already
* registered.
*
* By default addToCofig is true.
*
* @param addToConfig
*/
public void setAddToConfig(boolean addToConfig) {
this.addToConfig = addToConfig;
}
/**
* {@inheritDoc}
*/
@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 {
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();
}
}
}
/**
* {@inheritDoc}
*/
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
/**
* {@inheritDoc}
*/
public Class getObjectType() {
return this.mapperInterface;
}
/**
* {@inheritDoc}
*/
public boolean isSingleton() {
return true;
}
在该类中其实现了FactoryBean接口,看过Spring源码的人,我相信对其都有很深的印象,其在Bean的实例化中起着很重要的作用。在该类中我们要关注的是getObject方法,我们之后将动态实例化的接口对象放到Spring实例化列表中,这里就是入口,也是我们的起点。不过要特别说明的是mapperInterface的值是如何被赋值的,可能会有疑问,我们再来看看上面的ClassPathMapperScanner.java我们在配置MapperFactoryBean.class的上面存在一行 definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());其在之后在Spring的PostProcessorRegistrationDelegate类的populateBean方法中进行属性配置,会将其依靠反射的方式将其注入到MapperFactoryBean.class中。
而且definition.getPropertyValues().add中添加的值是注入到MapperFactoryBean对象中去的。这一点需要说明一下。
更多内容可以关注微信公众号,或者访问AppZone网站
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/67498.html
摘要:自定义注解不生效原因解析及解决方法背景项目中,自己基于实现了一套缓存注解。但是最近出现一种情况缓存竟然没有生效,大量请求被击穿到层,导致压力过大。至此,问题得到解决。 自定义注解不生效原因解析及解决方法 背景: 项目中,自己基于spring AOP实现了一套java缓存注解。但是最近出现一种情况:缓存竟然没有生效,大量请求被击穿到db层,导致db压力过大。现在我们看一下具体代码情形(代...
摘要:总结动态代理的相关原理已经讲解完毕,接下来让我们回答以下几个思考题。 【干货点】 此处是【好好面试】系列文的第12篇文章。文章目标主要是通过原理剖析的方式解答Aop动态代理的面试热点问题,通过一步步提出问题和了解原理的方式,我们可以记得更深更牢,进而解决被面试官卡住喉咙的情况。问题如下 SpringBoot默认代理类型是什么 为什么不用静态代理 JDK动态代理原理 CGLIB动态代理...
摘要:入门篇学习总结时间年月日星期三说明本文部分内容均来自慕课网。主要的功能是日志记录,性能统计,安全控制,事务处理,异常处理等等。 《Spring入门篇》学习总结 时间:2017年1月18日星期三说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com教学示例源码:https://github.com/zccodere/s...个人学习源码:https://git...
摘要:是一种特殊的增强切面切面由切点和增强通知组成,它既包括了横切逻辑的定义也包括了连接点的定义。实际上,一个的实现被拆分到多个类中在中声明切面我们知道注解很方便,但是,要想使用注解的方式使用就必须要有源码因为我们要 前言 只有光头才能变强 上一篇已经讲解了Spring IOC知识点一网打尽!,这篇主要是讲解Spring的AOP模块~ 之前我已经写过一篇关于AOP的文章了,那篇把比较重要的知...
摘要:又是什么其实就是一种实现动态代理的技术,利用了开源包,先将代理对象类的文件加载进来,之后通过修改其字节码并且生成子类。 在实际研发中,Spring是我们经常会使用的框架,毕竟它们太火了,也因此Spring相关的知识点也是面试必问点,今天我们就大话Aop。特地在周末推文,因为该篇文章阅读起来还是比较轻松诙谐的,当然了,更主要的是周末的我也在充电学习,希望有追求的朋友们也尽量不要放过周末时...
阅读 2413·2021-09-27 13:35
阅读 668·2019-08-30 15:55
阅读 930·2019-08-30 15:53
阅读 664·2019-08-30 15:52
阅读 2276·2019-08-30 12:59
阅读 2403·2019-08-29 16:42
阅读 1616·2019-08-26 18:26
阅读 2577·2019-08-26 13:48