MongoRepository动态代理及jpa方法解析源码分析
public interface FzkRepository extends MongoRepository<Fzk, String> {
Fzk findByName(String name);
}
@RestController
public class TestController {
@Autowired
private FzkRepository fzkReposiroty;
}
为什么一个接口,没有实现类就能被注入?
首先如果想使用MongoRepository,一定会在配置中加入@EnableMongoRepositories,就从EnableMongoRepositories开始
@Import(MongoRepositoriesRegistrar.class)
跟踪进入RepositoryBeanDefinitionRegistrarSupport
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
....
AnnotationRepositoryConfigurationSource configurationSource = new AnnotationRepositoryConfigurationSource(annotationMetadata, getAnnotation(), resourceLoader, environment, registry);
if (annotationMetadata.getAnnotationAttributes(getAnnotation().getName()) == null) {
return;
}
RepositoryConfigurationExtension extension = getExtension();
RepositoryConfigurationUtils.exposeRegistration(extension, registry, configurationSource); RepositoryConfigurationDelegate delegate = new RepositoryConfigurationDelegate(configurationSource, resourceLoader,
environment); delegate.registerRepositoriesIn(registry, extension);
}
没什么可说的,只有使用了EnableMongoRepositories才会继续进行,注册bean交给了RepositoryConfigurationDelegate 来进行。继续进入RepositoryConfigurationDelegate.registerRepositoriesIn方法
public List<BeanComponentDefinition> registerRepositoriesIn(BeanDefinitionRegistry registry, RepositoryConfigurationExtension extension) { extension.registerBeansForRoot(registry, configurationSource); RepositoryBeanDefinitionBuilder builder = new RepositoryBeanDefinitionBuilder(registry, extension, resourceLoader, environment); List<BeanComponentDefinition> definitions = new ArrayList<BeanComponentDefinition>(); for (RepositoryConfiguration<? extends RepositoryConfigurationSource> configuration : extension .getRepositoryConfigurations(configurationSource, resourceLoader, inMultiStoreMode)) { BeanDefinitionBuilder definitionBuilder = builder.build(configuration); extension.postProcess(definitionBuilder, configurationSource); if (isXml) { extension.postProcess(definitionBuilder, (XmlRepositoryConfigurationSource) configurationSource); } else { extension.postProcess(definitionBuilder, (AnnotationRepositoryConfigurationSource) configurationSource); } AbstractBeanDefinition beanDefinition = definitionBuilder.getBeanDefinition(); String beanName = beanNameGenerator.generateBeanName(beanDefinition, registry); if (LOGGER.isDebugEnabled()) { LOGGER.debug(REPOSITORY_REGISTRATION, extension.getModuleName(), beanName, configuration.getRepositoryInterface(), extension.getRepositoryFactoryClassName()); } beanDefinition.setAttribute(FACTORY_BEAN_OBJECT_TYPE, configuration.getRepositoryInterface()); registry.registerBeanDefinition(beanName, beanDefinition); definitions.add(new BeanComponentDefinition(beanDefinition, beanName)); } return definitions; }
扫描实现了MongoRepository的接口并生成RepositoryConfigurationSource的逻辑在RepositoryConfigurationExtensionSupport.getRepositoryConfigurations方法中
public <T extends RepositoryConfigurationSource> Collection<RepositoryConfiguration<T>> getRepositoryConfigurations(
T configSource, ResourceLoader loader, boolean strictMatchesOnly) {
....
Set<RepositoryConfiguration<T>> result = new HashSet<RepositoryConfiguration<T>>(); for (BeanDefinition candidate : configSource.getCandidates(loader)) {
RepositoryConfiguration<T> configuration = getRepositoryConfiguration(candidate, configSource);
if (!strictMatchesOnly || configSource.usesExplicitFilters()) {
result.add(configuration);
continue;
}
Class<?> repositoryInterface = loadRepositoryInterface(configuration, loader);
if (repositoryInterface == null || isStrictRepositoryCandidate(repositoryInterface)) {
result.add(configuration);
}
}
return result;
}
继续跟踪RepositoryConfigurationSourceSupport.getCandidates
public Collection<BeanDefinition> getCandidates(ResourceLoader loader) {
RepositoryComponentProvider scanner = new RepositoryComponentProvider(getIncludeFilters(), registry);
scanner.setConsiderNestedRepositoryInterfaces(shouldConsiderNestedRepositories());
scanner.setEnvironment(environment);
scanner.setResourceLoader(loader);
for (TypeFilter filter : getExcludeFilters()) {
scanner.addExcludeFilter(filter);
}
Set<BeanDefinition> result = new HashSet<BeanDefinition>();
for (String basePackage : getBasePackages()) {
Set<BeanDefinition> candidate = scanner.findCandidateComponents(basePackage);
result.addAll(candidate);
}
return result;
}
RepositoryComponentProvider.findCandidateComponents,先由父类ClassPathScanningCandidateComponentProvider处理
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + (basePackage) + '/' + this.resourcePattern;
Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
......for (Resource resource : resources) {
......
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
if (isCandidateComponent(metadataReader)) {
......
}
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) { //1
if (tf.match(metadataReader, this.metadataReaderFactory)) {
return false;
}
}
for (TypeFilter tf : this.includeFilters) { //2
if (tf.match(metadataReader, this.metadataReaderFactory)) {
return isConditionMatch(metadataReader);
}
}
return false;
}
private boolean isConditionMatch(MetadataReader metadataReader) {
if (this.conditionEvaluator == null) {
this.conditionEvaluator = new ConditionEvaluator(getRegistry(), getEnvironment(), getResourceLoader());
}
return !this.conditionEvaluator.shouldSkip(metadataReader.getAnnotationMetadata());
}
判断条件,1.如果是注解了NoRepositoryBean就不处理,2.判断了是Repository后,继续判断是否有@Condition。找到了所有复合条件的类后,组成RepositoryConfiguration后,继续跟踪RepositoryConfigurationDelegate.registerRepositoriesIn,组成BeanDefinitionBuilder,构件出MongoRepositoryFactoryBean。
public class MongoRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable>
extends RepositoryFactoryBeanSupport<T, S, ID> public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>, S, ID extends Serializable>
implements InitializingBean, RepositoryFactoryInformation<S, ID>, FactoryBean<T>, BeanClassLoaderAware,
BeanFactoryAware, ApplicationEventPublisherAware
MongoRepositoryFactoryBean间接实现InitializingBean,因此他会实现afterPropertiesSet方法
在AbstractApplicationContext -> refresh() -> finishBeanFactoryInitialization()阶段,实例化bean时进行实例化。
这个例子里,TestController注入了FzkRepository ,实例化TestController时发现需要注入FzkRepository 会先实例化FzkRepository。
在DefaultListableBeanFactory的preInstantiateSingletons()时
public void preInstantiateSingletons() throws BeansException {
......
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return ((SmartFactoryBean<?>) factory).isEagerInit();
}
}, getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
else {
getBean(beanName);
}
}
}
......
}
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
......
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
......
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}......
return (T) bean;
}
多次调用进入AbstractAutowireCapableBeanFactory.doCreateBean。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
......
// Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}......
}
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
......
invokeInitMethods(beanName, wrappedBean, mbd);
......
} protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
throws Throwable {
......
((InitializingBean) bean).afterPropertiesSet();
......
}
上面说了由BeanDefinitionBuilder构件出MongoRepositoryFactoryBean,而MongoRepositoryFactoryBean实现了InitializingBean,这里进入到了MongoRepositoryFactoryBean.afterPropertiesSet
public void afterPropertiesSet() { super.afterPropertiesSet();
Assert.notNull(operations, "MongoTemplate must not be null!"); if (!mappingContextConfigured) {
setMappingContext(operations.getConverter().getMappingContext());
}
}
进入到父累RepositoryFactoryBeanSupport.afterPropertiesSet
public void afterPropertiesSet() { this.factory = createRepositoryFactory();
......
if (publisher != null) {
this.factory.addRepositoryProxyPostProcessor(new EventPublishingRepositoryProxyPostProcessor(publisher));
} this.repositoryMetadata = this.factory.getRepositoryMetadata(repositoryInterface); if (!lazyInit) {
initAndReturn();
}
} private T initAndReturn() {
......
if (this.repository == null) {
this.repository = this.factory.getRepository(repositoryInterface, customImplementation);
}
return this.repository;
}
这里的factory是MongoRepositoryFactory,接下来,才是创建代理的部分
public <T> T getRepository(Class<T> repositoryInterface, Object customImplementation) {
......
// Create proxy
ProxyFactory result = new ProxyFactory();
result.setTarget(target);
result.setInterfaces(new Class[] { repositoryInterface, Repository.class });
......
result.addAdvice(new QueryExecutorMethodInterceptor(information, customImplementation, target, projectionFactory)); return (T) result.getProxy(classLoader);
}
第一重点是ProxyFactory,这里target是SimpleMongoRepository,repositoryInterface是FzkRepository(自己的接口)。
另一个重点是增加了一个切入点QueryExecutorMethodInterceptor。看看QueryExecutorMethodInterceptor
public QueryExecutorMethodInterceptor(RepositoryInformation repositoryInformation, Object customImplementation,
Object target, ProjectionFactory projectionFactory) {
......
this.resultHandler = new QueryExecutionResultHandler();
this.repositoryInformation = repositoryInformation;
this.customImplementation = customImplementation;
this.target = target; QueryLookupStrategy lookupStrategy = getQueryLookupStrategy(queryLookupStrategyKey,
RepositoryFactorySupport.this.evaluationContextProvider);
lookupStrategy = lookupStrategy == null ? getQueryLookupStrategy(queryLookupStrategyKey) : lookupStrategy;
Iterable<Method> queryMethods = repositoryInformation.getQueryMethods();
......
for (Method method : queryMethods) {
RepositoryQuery query = lookupStrategy.resolveQuery(method, repositoryInformation, projectionFactory,
namedQueries); invokeListeners(query);
queries.put(method, query);
}
}
首先他是一个MethodInterceptor,一个有个invoke方法,这个方法之后在说。现在先看构造器里干了什么。
首先构件一个MongoRepositoryFactory$MongoQueryLookupStrategy类型的lookupStrategy 。处理jpa写法的方法就是由它来处理,这里的例子是findByName方法。继续跟踪,看看是怎么将findByName解析成{"name" : ***}的。写了这么多,终于快到终点了。继续看,进入到MongoQueryLookupStrategy.resolveQuery
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory,
NamedQueries namedQueries) { MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, factory, mappingContext);
String namedQueryName = queryMethod.getNamedQueryName(); if (namedQueries.hasQuery(namedQueryName)) {
String namedQuery = namedQueries.getQuery(namedQueryName);
return new StringBasedMongoQuery(namedQuery, queryMethod, operations, EXPRESSION_PARSER,
evaluationContextProvider);
} else if (queryMethod.hasAnnotatedQuery()) {
return new StringBasedMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider);
} else {
return new PartTreeMongoQuery(queryMethod, operations);
}
}
这里,无@Query注解,进入PartTreeMongoQuery分支
public PartTreeMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations) { super(method, mongoOperations); this.processor = method.getResultProcessor();
this.tree = new PartTree(method.getName(), processor.getReturnedType().getDomainType());
this.isGeoNearQuery = method.isGeoNearQuery();
this.context = mongoOperations.getConverter().getMappingContext();
} public class PartTree {
private static final String KEYWORD_TEMPLATE = "(%s)(?=(\\p{Lu}|\\P{InBASIC_LATIN}))";
private static final String QUERY_PATTERN = "find|read|get|query|stream";
private static final String COUNT_PATTERN = "count";
private static final String EXISTS_PATTERN = "exists";
private static final String DELETE_PATTERN = "delete|remove";
private static final Pattern PREFIX_TEMPLATE = Pattern.compile( //
"^(" + QUERY_PATTERN + "|" + COUNT_PATTERN + "|" + EXISTS_PATTERN + "|" + DELETE_PATTERN + ")((\\p{Lu}.*?))??By");
public PartTree(String source, Class<?> domainClass) {
......
Matcher matcher = PREFIX_TEMPLATE.matcher(source);
if (!matcher.find()) {
this.subject = new Subject(null);
this.predicate = new Predicate(source, domainClass);
} else {
this.subject = new Subject(matcher.group(0));
this.predicate = new Predicate(source.substring(matcher.group().length()), domainClass);
}
}
}
最终生成的query,已经将name(key)解析出来了。
至此,终于解析完,怎么创建的代理,怎么根据方法名来解析sql。最后就是使用点
上面说了,使用时,会调用QueryExecutorMethodInterceptor.invoke
public Object invoke(MethodInvocation invocation) throws Throwable { Object result = doInvoke(invocation); return resultHandler.postProcessInvocationResult(result, invocation.getMethod());
} private Object doInvoke(MethodInvocation invocation) throws Throwable { Method method = invocation.getMethod();
Object[] arguments = invocation.getArguments(); if (isCustomMethodInvocation(invocation)) { Method actualMethod = repositoryInformation.getTargetClassMethod(method);
return executeMethodOn(customImplementation, actualMethod, arguments);
} if (hasQueryFor(method)) {
return queries.get(method).execute(arguments);
} // Lookup actual method as it might be redeclared in the interface
// and we have to use the repository instance nevertheless
Method actualMethod = repositoryInformation.getTargetClassMethod(method);
return executeMethodOn(target, actualMethod, arguments);
}
queries.get(method)得到PartTreeMongoQuery
public Object execute(Object[] parameters) { MongoParameterAccessor accessor = new MongoParametersParameterAccessor(method, parameters);
Query query = createQuery(new ConvertingParameterAccessor(operations.getConverter(), accessor)); applyQueryMetaAttributesWhenPresent(query); ResultProcessor processor = method.getResultProcessor().withDynamicProjection(accessor);
String collection = method.getEntityInformation().getCollectionName(); MongoQueryExecution execution = getExecution(query, accessor,
new ResultProcessingConverter(processor, operations, instantiators)); return execution.execute(query, processor.getReturnedType().getDomainType(), collection);
}
构件出Query得到结果返回
MongoRepository动态代理及jpa方法解析源码分析的更多相关文章
- Spring JPA实现逻辑源码分析总结
1.SharedEntityManagerCreator: entitymanager的创建入口 该类被EntityManagerBeanDefinitionRegistrarPostProcesso ...
- Spring的JDK动态代理如何实现的(源码解析)
前言 上一篇文章中提到了SpringAOP是如何决断使用哪种动态代理方式的,本文接上文讲解SpringAOP的JDK动态代理是如何实现的.SpringAOP的实现其实也是使用了Proxy和Invoca ...
- dubbo注册服务IP解析异常及IP解析源码分析
在使用dubbo注册服务时会遇到IP解析错误导致无法正常访问. 比如: 本机设置的IP为172.16.11.111, 但实际解析出来的是180.20.174.11 这样就导致这个Service永远也无 ...
- jQuery原型方法.pushStack源码分析
这次分析的方法跟前面不同,虽然pushStack也是原型方法之一,但是我们几乎从不用在页面调用,在参考手册里面也没有这个方法的使用说明,但是这个方法还是非常重要的,在使用很多jQuery的其他方式都会 ...
- RxJava2 中多种取消订阅 dispose 的方法梳理( 源码分析 )
Github 相关代码: Github地址 一直感觉 RxJava2 的取消订阅有点混乱, 这样也能取消, 那样也能取消, 没能系统起来的感觉就像掉进了盘丝洞, 迷乱… 下面说说这几种情况 几种取消的 ...
- 【Android笔记】Thread类中关于join()方法的源码分析
1.join()方法的作用: 例如有一个线程对象为Thread1,在main()方法中调用Thread1.join()方法可使得当前线程(即主线程)阻塞,而执行Thread1线程. 2.源码分析(以上 ...
- Universal-Image-Loader(UIL)使用方法&流程图&源码分析 ----- 未完
GitHub源码: Android-Universal-Image-Loader Features Multithread image loading (async or sync) 多线程加载(同步 ...
- Python学习---Django关于POST的请求解析源码分析
当有请求到来之后,先判断请求头content_type是不是[application/x-www-form-urlencoded] --> 如果是则将请求数据赋值给request.body然后解 ...
- MyBatis 源码分析 - 配置文件解析过程
* 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAl ...
随机推荐
- python笔记3 - 文件操作
file 对象使用 open 函数来创建,下面说一下对文件的操作分三步: 1.打开文件获取文件的句柄,句柄就理解为这个文件 2.通过文件句柄操作文件,读取/写入文件内容 3.关闭文件. 注意: 文件打 ...
- ApexSql Log 2016破解版&补丁
绿色破解版: http://download.csdn.net/detail/gsyifan/9316993 官网: https://www.apexsql.com/sql_tools_log.asp ...
- ACM 博弈(难)题练习 (第一弹)
第二弹: 套路&&经验总结: 1. N堆***的游戏,一般可以打表找SG函数的规律.比如CodeForces 603C 2.看起来是单轮的游戏,实际上可能拆分成一些独立的子游戏.比如C ...
- Web移动端使用localStorage缓存Js和css文件 | 强制不要缓存
1.尽量不把js放在onload事件中,而是放在由用户主动触发的事件 2.加时间戳,时间不同则会加载js而非使用缓存 强制不要缓存: <meta http-equiv=Cache-Control ...
- 170214、mybatis一级和二级缓存
mybatis一级缓存是指在内存中开辟一块区域,用来保存用户对数据库的操作信息(sql)和数据库返回的数据,如果下一次用户再执行相同的请求, 那么直接从内存中读数数据而不是从数据库读取. 其中数据的生 ...
- JDK源码分析之concurrent包(二) -- 线程池ThreadPoolExecutor
上一篇我们简单描述了Executor框架的结构,本篇正式开始并发包中部分源码的解读. 我们知道,目前主流的商用虚拟机在线程的实现上可能会有所差别.但不管如何实现,在开启和关闭线程时一定会耗费很多CPU ...
- Windows服务的调试
1.服务为其他程序调用的情况:首先停止服务,在项目中设置断点,重新启动服务,点击项目中工具,附加到进程,运行调用服务的程序,即可进入之前设置的断点,进而进行调试. 2.服务内方法为自动执行的情况:首先 ...
- Java 集合框架工具类
Collections Arrays Collections 集合框架的工具类, 里面的方法都是静态的, 可以直接使用类名调用 常用方法 1. sort(List<T> list); // ...
- 转!! Eclipse设定和修改文件字符编码格式和换行符
Window -> Preferences -> General -> Workspace : Text file encoding :Default : 选择此项将设定文件为系统默 ...
- 基于vue + typescrpt +vuecli 搭建开发环境
打算学习typeScript与vue集成,先放几个链接,留着自己学习用,后续自己写使用新的~ https://segmentfault.com/a/1190000013676663 https://j ...