能够扫描到包

@ComponentScan("org.zxp.esclientrhl")

ESCRegistrar类主要实现ImportBeanDefinitionRegistrar接口

@Configuration
public class ESCRegistrar extends AbstractESCRegister implements BeanFactoryAware,ApplicationContextAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {

实现下面方法,会在spring启动早期调用生成代理bean

public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
//扫描entity new ESIndexProcessor().scan(annotationMetadata,beanFactory,applicationContext);
//扫描接口
super.registerBeanDefinitions(beanFactory, environment, resourceLoader, annotationMetadata, registry);
}

扫描entity,通过注解配置或者启动目录扫描实体类并托管给Spring管理(和自动代理接口实现类无关,用于自动创建索引)

public void scan(AnnotationMetadata annotationMetadata,BeanFactory beanFactory,ApplicationContext applicationContext){
GetBasePackage getBasePackage = new GetBasePackage(EnableESTools.class);
ESEntityScanner scanner = new ESEntityScanner((BeanDefinitionRegistry) beanFactory);
scanner.setResourceLoader(applicationContext);
scanner.scan(getBasePackage.getEntityPackage(annotationMetadata).toArray(String[]::new));
}

通过getCandidates方法获取继承ESCRepository的接口

public Stream<BeanDefinition> getCandidates(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry, Environment environment, ResourceLoader resourceLoader) {
ESCRepositoryComponentProvider scanner = new ESCRepositoryComponentProvider(registry);
scanner.setEnvironment(environment);
scanner.setResourceLoader(resourceLoader);
//输入是basepackages,输出是BeanDefinition的Stream
return getBasePackage(annotationMetadata).flatMap(it -> scanner.findCandidateComponents(it).stream());
}

下面这两种scan不同,第一个就是扫描后能被spring识别,第二个是扫描到后返回BeanDefinition

scanner.findCandidateComponents(it)
scanner.scan(getBasePackage.getEntityPackage(annotationMetadata).toArray(String[]::new));

获取继承ESCRepository的接口(BeanDefinition)并遍历

通过BeanDefinitionBuilder给RepositoryFactorySupport传递扫描到接口的类类型、以及要生成代理bean的name

调用beanDefinitionRegistry.registerBeanDefinition(beanName, bd);将RepositoryFactorySupport托管给spring(注意RepositoryFactorySupport并不是目的,是通过RepositoryFactorySupport生成代理bean)

RepositoryFactorySupport的作用就是注册bean

public void registerBeanDefinitions(BeanFactory factory, Environment environment, ResourceLoader resourceLoader, AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
getCandidates(annotationMetadata, registry, environment, resourceLoader).forEach(beanDefinition -> {
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(RepositoryFactorySupport.class);
String beanClassName = beanDefinition.getBeanClassName();
//传入要实例化的接口
beanDefinitionBuilder.addConstructorArgValue(beanClassName);
//获取bean的定义
BeanDefinition bd = beanDefinitionBuilder.getRawBeanDefinition();
//生成beanname
String beanName = beanClassName.substring(beanClassName.lastIndexOf(".") + );
if(org.zxp.esclientrhl.auto.util.EnableESTools.isPrintregmsg()){
logger.info("generate ESCRegistrar beanClassName:" + beanClassName);
logger.info("generate ESCRegistrar beanName:" + beanName);
}
BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) factory;
//注册bean beanName是代理bean的名字 不是RepositoryFactorySupport的名字
beanDefinitionRegistry.registerBeanDefinition(beanName, bd);
});
}

repositoryInterface用于接收传入的接口类类型(准备通过动态代理生成)

通过afterPropertiesSet在RepositoryFactorySupport注册完成后生成并注册真正的代理bean

public class RepositoryFactorySupport<T extends ESCRepository<S, ID>, S, ID> implements ApplicationContextAware, ResourceLoaderAware, InitializingBean, FactoryBean<T>, BeanClassLoaderAware,
BeanFactoryAware, ApplicationEventPublisherAware {
……
private final Class<? extends T> repositoryInterface;
public RepositoryFactorySupport(Class<? extends T> repositoryInterface) {
this.repositoryInterface = repositoryInterface;
}
@Override
public void afterPropertiesSet() {
try {
this.repository = this.getRepository(repositoryInterface);
} catch (Exception e) {
logger.error("ESCRepository proxy create fail !", e);
}
}

生成代理bean的细节注意注释:

public <T> T getRepository(Class<T> repositoryInterface) throws Exception {
SimpleESCRepository target = new SimpleESCRepository(applicationContext);//传入applicationContext的目的是为了能让代理bean在运行时能通过applicationContext获取需要注入的bean
getMetadata(target);//下面单独说,获取对应实体类的类类型以及主键类型
//spring动态代理用法
ProxyFactory result = new ProxyFactory();
result.setTarget(target);
result.addAdvice(new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object result = invocation.proceed();
return result;
}
});
result.setInterfaces(this.repositoryInterface, ESCRepository.class);
T repository = (T) result.getProxy(classLoader);
return repository;
}

只要拿到了接口或者类,是能通过api获得定义接口的泛型名称的(不能获得全限定类名,有类名就可以匹配)

getEntityList()方法通过缓存的entitypaths遍历所有的entity并与之匹配

private void getMetadata(SimpleESCRepository target) throws Exception {
Type[] types = repositoryInterface.getGenericInterfaces();
ParameterizedType parameterized = (ParameterizedType) types[];
//实体类类型名称
String domainClassName = parameterized.getActualTypeArguments()[].getTypeName();
//实体类主键类型名称
String idClassName = parameterized.getActualTypeArguments()[].getTypeName();
if (org.zxp.esclientrhl.auto.util.EnableESTools.isPrintregmsg()) {
logger.info("domainClassName:" + domainClassName + " idClassName:" + idClassName);
}
//按照实体类类型名称匹配实体类类型
List<String> entityList = getEntityList();
for (int i = ; i < entityList.size(); i++) {
if (entityList.get(i).lastIndexOf("." + domainClassName) != -) {
if (target.getDomainClass() == null) {
target.setDomainClass(Class.forName(entityList.get(i)));
break;
} else {
target.setDomainClass(null);
throw new Exception("Entity Overmatched !");
}
}
}
//按照实体类主键类型名称主键类型
Map<String, Class> idTypeMap = getIdTypeMap();
if (idTypeMap.containsKey(idClassName)) {
target.setIdClass(idTypeMap.get(idClassName));
} else {
throw new Exception("Not Supported ID Type !");
}
}

实现了FactoryBean可以将生成的代理bean托管给spring

/**
* 实现了FactoryBean可以将生成的代理bean托管给spring
*
* @return
* @throws Exception
*/
@Override
public T getObject() throws Exception {
return this.repository;
}
/**
* 实现了FactoryBean可以将生成的代理bean托管给spring
*
* @return
*/
@Override
public Class<?> getObjectType() {
return repositoryInterface;
}

Spring只定义接口自动代理接口实现类的更多相关文章

  1. Spring AOP使用整理:自动代理以及AOP命令空间

    三.自动代理的实现 1.使用BeanNameAutoProxyCreator 通过Bean的name属性自动生成代理Bean. <bean class="org.springframe ...

  2. Spring框架学习08——自动代理方式实现AOP

    在传统的基于代理类的AOP实现中,每个代理都是通过ProxyFactoryBean织入切面代理,在实际开发中,非常多的Bean每个都配置ProxyFactoryBean开发维护量巨大.解决方案:自动创 ...

  3. Dubbo原理实现之代理接口的定义

    Dubbo有很多的实现采用了代码模式,Dubbo由代理工厂ProxyFactory对象创建代理对象. ProxyFactory接口的定义如下: @SPI("javassist") ...

  4. Mybatis MapperScannerConfigurer 自动扫描 将Mapper接口生成代理注入到Spring

    Mybatis MapperScannerConfigurer 自动扫描 将Mapper接口生成代理注入到Spring 非原创[只为记录],原博文地址:https://www.cnblogs.com/ ...

  5. Mybaits 源码解析 (十一)----- 设计模式精妙使用:静态代理和动态代理结合使用:@MapperScan将Mapper接口生成代理注入到Spring

    上一篇文章我们讲了SqlSessionFactoryBean,通过这个FactoryBean创建SqlSessionFactory并注册进Spring容器,这篇文章我们就讲剩下的部分,通过Mapper ...

  6. Spring Boot(九)Swagger2自动生成接口文档和Mock模拟数据

    一.简介 在当下这个前后端分离的技术趋势下,前端工程师过度依赖后端工程师的接口和数据,给开发带来了两大问题: 问题一.后端接口查看难:要怎么调用?参数怎么传递?有几个参数?参数都代表什么含义? 问题二 ...

  7. MapperScannerConfigurer 自动扫描 将Mapper接口生成代理注入到Spring

    Mybatis在与Spring集成的时候可以配置 MapperFactoryBean来生成Mapper接口的代理. 例如 <bean id="userMapper" clas ...

  8. (转)Mybatis MapperScannerConfigurer 自动扫描 将Mapper接口生成代理注入到Spring

    Mybatis MapperScannerConfigurer 自动扫描 将Mapper接口生成代理注入到Spring Mybatis在与Spring集成的时候可以配置MapperFactoryBea ...

  9. Mybatis MapperScannerConfigurer 自动扫描 将Mapper接口生成代理注入到Spring - 大新博客 - 推酷 - 360安全浏览器 7.1

    Mybatis MapperScannerConfigurer 自动扫描 将Mapper接口生成代理注入到Spring - 大新博客 时间 2014-02-11 21:08:00  博客园-所有随笔区 ...

随机推荐

  1. 海量数据搜索---demo展示百度、谷歌搜索引擎的实现

    在我们平常的生活工作中,百度.谷歌这些搜索网站已经成为了我们受教解惑的学校,俗话说得好,“有问题找度娘”.那么百度是如何在海量数据中找到自己需要的数据呢?为什么它搜索的速度如此之快?我们都知道是因为百 ...

  2. 提升RabbitMQ消费速度的一些实践

    RabbitMQ是一个开源的消息中间件,自带管理界面友好.开发语言支持广泛.没有对其它中间件的依赖,而且社区非常活跃,特别适合中小型企业拿来就用.这篇文章主要探讨提升RabbitMQ消费速度的一些方法 ...

  3. CF - 1106 E Lunar New Year and Red Envelopes DP

    题目传送门 题解: 首先要处理出每个时间点会选择哪一个线段. 对于这个问题,可以用multiset去维护信息. 当时间线开始的时候,往mutiset里面插入这个信息,当时间线结束的时候,删除这个信息. ...

  4. codeforces H. Queries for Number of Palindromes(区间dp)

    题目链接:http://codeforces.com/contest/245/problem/H 题意:给出一个字符串还有q个查询,输出每次查询区间内回文串的个数.例如aba->(aba,a,b ...

  5. yzoj P2349 取数 题解

    题意 1到n个自然数中选k个自然数要求两两不相邻,问有多少种方法,模m eg(1 3 5 ) 又是一道打表规律题,正常解法dp可以通过前缀和优化到O(N* K).另外我们可以重新定义F[I,J]表示从 ...

  6. odoo12从零开始:三、1)创建你的第一个应用模型(module)

    前言 以前,我一直都不知道为什么好多框架的入门都是“hello world”开始,当我思前想后我要如何介绍odoo的model.record.template等继承等高级特性时,发现在那之前便需要清楚 ...

  7. JavaScript漫谈之理解类型操作符typeof

    在本文中,将简述JavaScript类型系统和数据类型,以及如何使用typeof操作符执行类型检查. 还讲解了使用typeof操作符进行某些数据类型检查是不完善的,并介绍其他几种类型检查的方法. 更多 ...

  8. 小白的消费为何被迫升级?-java数据类型的转换

    背景 小白最近有点烦恼,原因也很简单,不知道为何?小白的消费不知不觉被迫升级了,请看费用清单: for (byte b = Byte.MIN_VALUE; b < Byte.MAX_VALUE; ...

  9. SqlServer2014怎样还原数据库

    场景 在SqlServer2014企业版上怎样进行数据库的还原,首先你得有一个其他数据 的备份文件. 实现 打开cmd,输入sql,打开SqlServer 2014 Management Studio ...

  10. Visual Studio Code 安装美化合集

    这是一个关于VSCode编辑器的各种配置. 你可以在这里找到VSCode 的各种操作,如果这里找不到,请移步官方文档C++ programming with Visual Studio Code以及各 ...