上文最后提到jackrabbit的检索默认实现类QueryImpl,先熟悉一下该类的继承层次

QueryImpl继承自抽象类AbstractQueryImpl,而抽象类实现了Query接口(JCR的接口)

Query接口源码如下:

/*** A <code>Query</code> object.*/
public interface Query {/*** A string constant representing the XPath query language as defined in JCR* 1.0.** @deprecated As of JCR 2.0, this language is deprecated.*/public static final String XPATH = "xpath";/*** A string constant representing the SQL query language as defined in JCR* 1.0.** @deprecated As of JCR 2.0, this language is deprecated.*/public static final String SQL = "sql";/*** A string constant representing the JCR-SQL2 query language.** @since JCR 2.0*/public static final String JCR_SQL2 = "JCR-SQL2";/*** A string constant representing the JCR-JQOM query language.** @since JCR 2.0*/public static final String JCR_JQOM = "JCR-JQOM";/*** Executes this query and returns a <code>{@link QueryResult}</code>* object.* <p>* If this <code>Query</code> contains a variable (see {@link* javax.jcr.query.qom.BindVariableValue BindVariableValue}) which has not* been bound to a value (see {@link Query#bindValue}) then this method* throws an <code>InvalidQueryException</code>.** @return a <code>QueryResult</code> object* @throws InvalidQueryException if the query contains an unbound variable.* @throws RepositoryException   if another error occurs.*/public QueryResult execute() throws InvalidQueryException, RepositoryException;/*** Sets the maximum size of the result set to <code>limit</code>.** @param limit a <code>long</code>* @since JCR 2.0*/public void setLimit(long limit);/*** Sets the start offset of the result set to <code>offset</code>.** @param offset a <code>long</code>* @since JCR 2.0*/public void setOffset(long offset);/*** Returns the statement defined for this query.* <p>* If the language of this query is JCR-SQL2 or another string-based* language, this method will return the statement that was used to create* this query.* <p>* If the language of this query is JCR-JQOM, this method will return the* JCR-SQL2 equivalent of the JCR-JQOM object tree. This is the standard* serialization of JCR-JQOM and is also the string stored in the* <code>jcr:statement</code> property if the query is persisted. See {@link* #storeAsNode(String)}.** @return the query statement.*/public String getStatement();/*** Returns the language set for this query. This will be one of the query* language constants returned by {@link QueryManager#getSupportedQueryLanguages}.** @return the query language.*/public String getLanguage();/*** If this is a <code>Query</code> object that has been stored using {@link* Query#storeAsNode} (regardless of whether it has been <code>save</code>d* yet) or retrieved using {@link QueryManager#getQuery}), then this method* returns the path of the <code>nt:query</code> node that stores the* query.** @return path of the node representing this query.* @throws ItemNotFoundException if this query is not a stored query.* @throws RepositoryException   if another error occurs.*/public String getStoredQueryPath() throws ItemNotFoundException, RepositoryException;/*** Creates a node of type <code>nt:query</code> holding this query at* <code>absPath</code> and returns that node.* <p>* This is  a session-write method and therefore requires a* <code>Session.save()</code> to dispatch the change.* <p>* The <code>absPath</code> provided must not have an index on its final* element. If ordering is supported by the node type of the parent node* then the new node is appended to the end of the child node list.* <p>* An <code>ItemExistsException</code> will be thrown either immediately, on* dispatch or on persists, if an item at the specified path already exists* and same-name siblings are not allowed. Implementations may differ on* when this validation is performed.* <p>* A <code>PathNotFoundException</code> will be thrown either immediately,* on dispatch or on persists, if the specified path implies intermediary* nodes that do not exist. Implementations may differ on when this* validation is performed.* <p>* A <code>ConstraintViolationException</code>will be thrown either* immediately, on dispatch or on persists, if adding the node would violate* a node type or implementation-specific constraint or if an attempt is* made to add a node as the child of a property. Implementations may differ* on when this validation is performed.* <p>* A <code>VersionException</code> will be thrown either immediately, on* dispatch or on persists, if the node to which the new child is being* added is read-only due to a checked-in node. Implementations may differ* on when this validation is performed.* <p>* A <code>LockException</code> will be thrown either immediately, on* dispatch or on persists, if a lock prevents the addition of the node.* Implementations may differ on when this validation is performed.** @param absPath absolute path the query should be stored at* @return the newly created node.* @throws ItemExistsException          if an item at the specified path already*                                      exists, same-name siblings are not allowed and this implementation*                                      performs this validation immediately.* @throws PathNotFoundException        if the specified path implies intermediary*                                      <code>Node</code>s that do not exist or the last element of*                                      <code>relPath</code> has an index, and this implementation performs this*                                      validation immediately.* @throws ConstraintViolationException if a node type or*                                      implementation-specific constraint is violated or if an attempt is made*                                      to add a node as the child of a property and this implementation performs*                                      this validation immediately.* @throws VersionException             if the node to which the new child is being*                                      added is read-only due to a checked-in node and this implementation*                                      performs this validation immediately.* @throws LockException                if a lock prevents the addition of the node and*                                      this implementation performs this validation immediately.* @throws UnsupportedRepositoryOperationException*                                      if persistent queries are*                                      not supported.* @throws RepositoryException          if another error occurs or if the*                                      <code>absPath</code> provided has an index on its final element.*/public Node storeAsNode(String absPath) throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException, UnsupportedRepositoryOperationException, RepositoryException;/*** Binds the given <code>value</code> to the variable named* <code>varName</code>.** @param varName name of variable in query* @param value   value to bind* @throws IllegalArgumentException      if <code>varName</code> is not a valid*                                       variable in this query.* @throws javax.jcr.RepositoryException if an error occurs.* @since JCR 2.0*/public void bindValue(String varName, Value value) throws IllegalArgumentException, RepositoryException;/*** Returns the names of the bind variables in this query. If this query does* not contains any bind variables then an empty array is returned.** @return the names of the bind variables in this query.* @throws RepositoryException if an error occurs.* @since JCR 2.0*/public String[] getBindVariableNames() throws RepositoryException;
}

抽象类AbstractQueryImpl只有一个init抽象方法,显然是要求子类实现

/*** Defines common initialisation methods for all query implementations.*/
public abstract class AbstractQueryImpl implements Query {/*** Initialises a query instance from a query string.** @param sessionContext component context of the current session* @param handler   the query handler of the search index.* @param statement the query statement.* @param language  the syntax of the query statement.* @param node      a nt:query node where the query was read from or*                  <code>null</code> if it is not a stored query.* @throws InvalidQueryException if the query statement is invalid according*                               to the specified <code>language</code>.*/public abstract void init(SessionContext sessionContext, QueryHandler handler,String statement, String language, Node node)throws InvalidQueryException;
}

QueryImpl类的源码如下:

/*** Provides the default implementation for a JCR query.*/
public class QueryImpl extends AbstractQueryImpl {/*** The logger instance for this class*/private static final Logger log = LoggerFactory.getLogger(QueryImpl.class);/*** Component context of the current session*/protected SessionContext sessionContext;/*** The query statement*/protected String statement;/*** The syntax of the query statement*/protected String language;/*** The actual query implementation that can be executed*/protected ExecutableQuery query;/*** The node where this query is persisted. Only set when this is a persisted* query.*/protected Node node;/*** The query handler for this query.*/protected QueryHandler handler;/*** Flag indicating whether this query is initialized.*/private boolean initialized = false;/*** The maximum result size*/protected long limit = -1;/*** The offset in the total result set*/protected long offset = 0;/*** @inheritDoc*/public void init(SessionContext sessionContext, QueryHandler handler,String statement, String language, Node node)throws InvalidQueryException {checkNotInitialized();this.sessionContext = sessionContext;this.statement = statement;this.language = language;this.handler = handler;this.node = node;this.query = handler.createExecutableQuery(sessionContext, statement, language);setInitialized();}/*** This method simply forwards the <code>execute</code> call to the* {@link ExecutableQuery} object returned by* {@link QueryHandler#createExecutableQuery}.* {@inheritDoc}*/public QueryResult execute() throws RepositoryException {checkInitialized();long time = System.currentTimeMillis();QueryResult result = sessionContext.getSessionState().perform(new SessionOperation<QueryResult>() {public QueryResult perform(SessionContext context)throws RepositoryException {return query.execute(offset, limit);}public String toString() {return "query.execute(" + statement + ")";}});if (log.isDebugEnabled()) {time = System.currentTimeMillis() - time;NumberFormat format = NumberFormat.getNumberInstance();format.setMinimumFractionDigits(2);format.setMaximumFractionDigits(2);String seconds = format.format((double) time / 1000);log.debug("executed in " + seconds + " s. (" + statement + ")");}return result;}/*** {@inheritDoc}*/public String getStatement() {checkInitialized();return statement;}/*** {@inheritDoc}*/public String getLanguage() {checkInitialized();return language;}/*** {@inheritDoc}*/public String getStoredQueryPath()throws ItemNotFoundException, RepositoryException {checkInitialized();if (node == null) {throw new ItemNotFoundException("not a persistent query");}return node.getPath();}/*** {@inheritDoc}*/public Node storeAsNode(String absPath)throws ItemExistsException,PathNotFoundException,VersionException,ConstraintViolationException,LockException,UnsupportedRepositoryOperationException,RepositoryException {checkInitialized();try {Path p = sessionContext.getQPath(absPath).getNormalizedPath();if (!p.isAbsolute()) {throw new RepositoryException(absPath + " is not an absolute path");}String relPath = sessionContext.getJCRPath(p).substring(1);Node queryNode =sessionContext.getSessionImpl().getRootNode().addNode(relPath, sessionContext.getJCRName(NT_QUERY));// set properties
            queryNode.setProperty(sessionContext.getJCRName(JCR_LANGUAGE), language);queryNode.setProperty(sessionContext.getJCRName(JCR_STATEMENT), statement);node = queryNode;return node;} catch (NameException e) {throw new RepositoryException(e.getMessage(), e);}}/*** {@inheritDoc}*/public String[] getBindVariableNames() {return new String[0];}/*** Throws an {@link IllegalArgumentException} as XPath and SQL1 queries* have no bind variables.** @throws IllegalArgumentException always thrown*/public void bindValue(String varName, Value value)throws IllegalArgumentException {throw new IllegalArgumentException("No such bind variable: " + varName);}/*** Sets the maximum size of the result set.** @param limit new maximum size of the result set*/public void setLimit(long limit) {if (limit < 0) {throw new IllegalArgumentException("limit must not be negativ");}this.limit = limit;}/*** Sets the start offset of the result set.** @param offset new start offset of the result set*/public void setOffset(long offset) {if (offset < 0) {throw new IllegalArgumentException("offset must not be negativ");}this.offset = offset;}//-----------------------------< internal >---------------------------------/*** Sets the initialized flag.*/protected void setInitialized() {initialized = true;}/*** Checks if this query is not yet initialized and throws an* <code>IllegalStateException</code> if it is already initialized.*/protected void checkNotInitialized() {if (initialized) {throw new IllegalStateException("already initialized");}}/*** Checks if this query is initialized and throws an* <code>IllegalStateException</code> if it is not yet initialized.*/protected void checkInitialized() {if (!initialized) {throw new IllegalStateException("not initialized");}}}

先看一下它的初始化方法

 /*** @inheritDoc*/public void init(SessionContext sessionContext, QueryHandler handler,String statement, String language, Node node)throws InvalidQueryException {checkNotInitialized();this.sessionContext = sessionContext;this.statement = statement;this.language = language;this.handler = handler;this.node = node;this.query = handler.createExecutableQuery(sessionContext, statement, language);setInitialized();}

这里的handler还是SearchManager初始化该类时传过来的SearchIndex类型的对象,我们从这里可以看到,ExecutableQuery类型的query成员变量时通过handler(SearchIndex类型)创建的

/*** The actual query implementation that can be executed*/protected ExecutableQuery query;

QueryImpl类的真正检索方法如下:

/*** This method simply forwards the <code>execute</code> call to the* {@link ExecutableQuery} object returned by* {@link QueryHandler#createExecutableQuery}.* {@inheritDoc}*/public QueryResult execute() throws RepositoryException {checkInitialized();long time = System.currentTimeMillis();QueryResult result = sessionContext.getSessionState().perform(new SessionOperation<QueryResult>() {public QueryResult perform(SessionContext context)throws RepositoryException {return query.execute(offset, limit);}public String toString() {return "query.execute(" + statement + ")";}});if (log.isDebugEnabled()) {time = System.currentTimeMillis() - time;NumberFormat format = NumberFormat.getNumberInstance();format.setMinimumFractionDigits(2);format.setMaximumFractionDigits(2);String seconds = format.format((double) time / 1000);log.debug("executed in " + seconds + " s. (" + statement + ")");}return result;}

我们可以看到是调用ExecutableQuery query成员变量的execute方法

现在回顾头来查看一下SearchIndex的createExecutableQuery方法

/*** Creates a new query by specifying the query statement itself and the* language in which the query is stated.  If the query statement is* syntactically invalid, given the language specified, an* InvalidQueryException is thrown. <code>language</code> must specify a query language* string from among those returned by QueryManager.getSupportedQueryLanguages(); if it is not* then an <code>InvalidQueryException</code> is thrown.** @param sessionContext component context of the current session* @param statement the query statement.* @param language the syntax of the query statement.* @throws InvalidQueryException if statement is invalid or language is unsupported.* @return A <code>Query</code> object.*/public ExecutableQuery createExecutableQuery(SessionContext sessionContext, String statement, String language)throws InvalidQueryException {QueryImpl query = new QueryImpl(sessionContext, this, getContext().getPropertyTypeRegistry(),statement, language, getQueryNodeFactory());query.setRespectDocumentOrder(documentOrder);return query;}

可以看到,该方法实际返回的是org.apache.jackrabbit.core.query.lucene.QueryImpl类型的对象

org.apache.jackrabbit.core.query.lucene.QueryImpl类继承自抽象类org.apache.jackrabbit.core.query.lucene.AbstractQueryImpl,而抽象类org.apache.jackrabbit.core.query.lucene.AbstractQueryImpl实现了org.apache.jackrabbit.core.query.ExecutableQuery接口

(这里面命名与org.apache.jackrabbit.core.query包命名相同,容易使人混淆)

ExecutableQuery接口的源码如下:

/*** Specifies an interface for a query object implementation that can just be* executed.* @see QueryImpl*/
public interface ExecutableQuery {/*** Executes this query and returns a <code>{@link QueryResult}</code>.* @param offset the offset in the total result set* @param limit the maximum result size** @return a <code>QueryResult</code>* @throws RepositoryException if an error occurs*/QueryResult execute(long offset, long limit) throws RepositoryException;}

抽象类org.apache.jackrabbit.core.query.lucene.AbstractQueryImpl源码如下:

/*** <code>AbstractQueryImpl</code> provides a base class for executable queries* based on {@link SearchIndex}.*/
public abstract class AbstractQueryImpl implements ExecutableQuery {/*** Component context of the current session*/protected final SessionContext sessionContext;/*** The actual search index*/protected final SearchIndex index;/*** The property type registry for type lookup.*/protected final PropertyTypeRegistry propReg;/*** If <code>true</code> the default ordering of the result nodes is in* document order.*/private boolean documentOrder = true;protected final PerQueryCache cache = new PerQueryCache();/*** Creates a new query instance from a query string.** @param sessionContext component context of the current session* @param index   the search index.* @param propReg the property type registry.*/public AbstractQueryImpl(SessionContext sessionContext, SearchIndex index,PropertyTypeRegistry propReg) {this.sessionContext = sessionContext;this.index = index;this.propReg = propReg;}/*** If set <code>true</code> the result nodes will be in document order* per default (if no order by clause is specified). If set to* <code>false</code> the result nodes are returned in whatever sequence* the index has stored the nodes. That sequence is stable over multiple* invocations of the same query, but will change when nodes get added or* removed from the index.* <p/>* The default value for this property is <code>true</code>.* @return the current value of this property.*/public boolean getRespectDocumentOrder() {return documentOrder;}/*** Sets a new value for this property.** @param documentOrder if <code>true</code> the result nodes are in* document order per default.** @see #getRespectDocumentOrder()*/public void setRespectDocumentOrder(boolean documentOrder) {this.documentOrder = documentOrder;}/*** @return the query object model factory.* @throws RepositoryException if an error occurs.*/protected QueryObjectModelFactory getQOMFactory()throws RepositoryException {Workspace workspace = sessionContext.getSessionImpl().getWorkspace();return workspace.getQueryManager().getQOMFactory();}/*** Returns <code>true</code> if this query node needs items under* /jcr:system to be queried.** @return <code>true</code> if this query node needs content under*         /jcr:system to be queried; <code>false</code> otherwise.*/public abstract boolean needsSystemTree();
}

org.apache.jackrabbit.core.query.lucene.QueryImpl类的源码如下:

/*** Implements the {@link org.apache.jackrabbit.core.query.ExecutableQuery}* interface.*/
public class QueryImpl extends AbstractQueryImpl {/*** The logger instance for this class*/private static final Logger log = LoggerFactory.getLogger(QueryImpl.class);/*** The default selector name 's'.*/public static final Name DEFAULT_SELECTOR_NAME = NameFactoryImpl.getInstance().create("", "s");/*** The root node of the query tree*/protected final QueryRootNode root;/*** Creates a new query instance from a query string.** @param sessionContext component context of the current session* @param index     the search index.* @param propReg   the property type registry.* @param statement the query statement.* @param language  the syntax of the query statement.* @param factory   the query node factory.* @throws InvalidQueryException if the query statement is invalid according*                               to the specified <code>language</code>.*/public QueryImpl(SessionContext sessionContext, SearchIndex index,PropertyTypeRegistry propReg, String statement, String language,QueryNodeFactory factory) throws InvalidQueryException {super(sessionContext, index, propReg);// parse query according to language// build query tree using the passed factorythis.root = QueryParser.parse(statement, language, sessionContext, factory);}/*** Executes this query and returns a <code>{@link QueryResult}</code>.** @param offset the offset in the total result set* @param limit the maximum result size* @return a <code>QueryResult</code>* @throws RepositoryException if an error occurs*/public QueryResult execute(long offset, long limit) throws RepositoryException {if (log.isDebugEnabled()) {log.debug("Executing query: \n" + root.dump());}// build lucene queryQuery query = LuceneQueryBuilder.createQuery(root, sessionContext.getSessionImpl(),index.getContext().getItemStateManager(),index.getNamespaceMappings(), index.getTextAnalyzer(),propReg, index.getSynonymProvider(),index.getIndexFormatVersion(),cache);OrderQueryNode orderNode = root.getOrderNode();OrderQueryNode.OrderSpec[] orderSpecs;if (orderNode != null) {orderSpecs = orderNode.getOrderSpecs();} else {orderSpecs = new OrderQueryNode.OrderSpec[0];}Path[] orderProperties = new Path[orderSpecs.length];boolean[] ascSpecs = new boolean[orderSpecs.length];for (int i = 0; i < orderSpecs.length; i++) {orderProperties[i] = orderSpecs[i].getPropertyPath();ascSpecs[i] = orderSpecs[i].isAscending();}return new SingleColumnQueryResult(index, sessionContext, this, query,new SpellSuggestion(index.getSpellChecker(), root),getColumns(), orderProperties, ascSpecs,orderProperties.length == 0 && getRespectDocumentOrder(),offset, limit);}/*** Returns the columns for this query.** @return array of columns.* @throws RepositoryException if an error occurs.*/protected ColumnImpl[] getColumns() throws RepositoryException {SessionImpl session = sessionContext.getSessionImpl();QueryObjectModelFactory qomFactory =session.getWorkspace().getQueryManager().getQOMFactory();// get columnsMap<Name, ColumnImpl> columns = new LinkedHashMap<Name, ColumnImpl>();for (Name name : root.getSelectProperties()) {String pn = sessionContext.getJCRName(name);ColumnImpl col = (ColumnImpl) qomFactory.column(sessionContext.getJCRName(DEFAULT_SELECTOR_NAME), pn, pn);columns.put(name, col);}if (columns.size() == 0) {// use node type constraintLocationStepQueryNode[] steps = root.getLocationNode().getPathSteps();final Name[] ntName = new Name[1];steps[steps.length - 1].acceptOperands(new DefaultQueryNodeVisitor() {public Object visit(AndQueryNode node, Object data) throws RepositoryException {return node.acceptOperands(this, data);}public Object visit(NodeTypeQueryNode node, Object data) {ntName[0] = node.getValue();return data;}}, null);if (ntName[0] == null) {ntName[0] = NameConstants.NT_BASE;}NodeTypeImpl nt = session.getNodeTypeManager().getNodeType(ntName[0]);PropertyDefinition[] propDefs = nt.getPropertyDefinitions();for (PropertyDefinition pd : propDefs) {QPropertyDefinition propDef = ((PropertyDefinitionImpl) pd).unwrap();if (!propDef.definesResidual() && !propDef.isMultiple()) {columns.put(propDef.getName(), columnForName(propDef.getName()));}}}// add jcr:path and jcr:score if not selected alreadyif (!columns.containsKey(NameConstants.JCR_PATH)) {columns.put(NameConstants.JCR_PATH, columnForName(NameConstants.JCR_PATH));}if (!columns.containsKey(NameConstants.JCR_SCORE)) {columns.put(NameConstants.JCR_SCORE, columnForName(NameConstants.JCR_SCORE));}return columns.values().toArray(new ColumnImpl[columns.size()]);}/*** Returns <code>true</code> if this query node needs items under* /jcr:system to be queried.** @return <code>true</code> if this query node needs content under*         /jcr:system to be queried; <code>false</code> otherwise.*/public boolean needsSystemTree() {return this.root.needsSystemTree();}/*** Returns a column for the given property name and the default selector* name.** @param propertyName the name of the property as well as the column.* @return a column.* @throws RepositoryException if an error occurs while creating the column.*/protected ColumnImpl columnForName(Name propertyName) throws RepositoryException {Workspace workspace = sessionContext.getSessionImpl().getWorkspace();QueryObjectModelFactory qomFactory =workspace.getQueryManager().getQOMFactory();String name = sessionContext.getJCRName(propertyName);return (ColumnImpl) qomFactory.column(sessionContext.getJCRName(DEFAULT_SELECTOR_NAME), name, name);}
}

最重要的查询方法是

/*** Executes this query and returns a <code>{@link QueryResult}</code>.** @param offset the offset in the total result set* @param limit the maximum result size* @return a <code>QueryResult</code>* @throws RepositoryException if an error occurs*/public QueryResult execute(long offset, long limit) throws RepositoryException {if (log.isDebugEnabled()) {log.debug("Executing query: \n" + root.dump());}// build lucene queryQuery query = LuceneQueryBuilder.createQuery(root, sessionContext.getSessionImpl(),index.getContext().getItemStateManager(),index.getNamespaceMappings(), index.getTextAnalyzer(),propReg, index.getSynonymProvider(),index.getIndexFormatVersion(),cache);OrderQueryNode orderNode = root.getOrderNode();OrderQueryNode.OrderSpec[] orderSpecs;if (orderNode != null) {orderSpecs = orderNode.getOrderSpecs();} else {orderSpecs = new OrderQueryNode.OrderSpec[0];}Path[] orderProperties = new Path[orderSpecs.length];boolean[] ascSpecs = new boolean[orderSpecs.length];for (int i = 0; i < orderSpecs.length; i++) {orderProperties[i] = orderSpecs[i].getPropertyPath();ascSpecs[i] = orderSpecs[i].isAscending();}return new SingleColumnQueryResult(index, sessionContext, this, query,new SpellSuggestion(index.getSpellChecker(), root),getColumns(), orderProperties, ascSpecs,orderProperties.length == 0 && getRespectDocumentOrder(),offset, limit);}

---------------------------------------------------------------------------

本系列Apache Jackrabbit源码研究系本人原创

转载请注明出处 博客园 刺猬的温驯

本文链接 http://www.cnblogs.com/chenying99/archive/2013/04/07/3003304.html

Apache Jackrabbit源码研究(五)相关推荐

  1. Apache Camel源码研究之Rest

    本文以Camel2.24.3 + SpringBoot2.x 为基础简单解读Camel中的Rest组件的源码级实现逻辑. 0. 目录 1. 前言 2. 源码解读 2.1 启动时 2.1.1 `Rest ...

  2. Apache Camel源码研究之Intercept

    Intercept作为一个极其强大的扩展机制,其理念几乎存在于所有知名框架中,诸如Spring,Mybatis,Tomcat等等都无一例外地提供了相应的支持,在保持自身框架本身整洁的同时,实现对各类业 ...

  3. Apache Camel源码研究之Language

    Apache Camel通过Language将Expression和Predicate的构造操作合并在一起,减少了概念,也降低了扩展难度,是的整体架构更加清晰. 1. 概述 Apache Camel为 ...

  4. Apache Tika源码研究(七)

    tika怎样加载Parser实现类的,怎样根据文档的mime类型调用相应的Parser实现类,本文接着分析 先熟悉一下tika的解析类的相关接口和类的UML模型: Parser接口的源码如下: /** ...

  5. WebRTC源码研究(4)web服务器工作原理和常用协议基础

    文章目录 WebRTC源码研究(4)web服务器工作原理和常用协议基础 前言 做WebRTC 开发为啥要懂服务器开发知识 1. Web 服务器简介 2. Web 服务器的类型 3. Web 服务器的工 ...

  6. WebRTC源码研究(4)web服务器工作原理和常用协议基础(转载)

    前言 前面3篇博客分别对WebRTC框架的介绍,WebRTC源码目录,WebRTC的运行机制进行了介绍,接下来讲解一点关于服务器原理的知识.后面博客会写关于WebRTC服务器相关的开发,目前git上面 ...

  7. 一起谈.NET技术,.NET Framework源码研究系列之---万法归宗Object

    经过前面三篇关于.NET Framework源码研究系列的随笔,相信大家都发现其实.NET Framework的实现其实并不复杂,也许跟我们自己做的项目开发差不多.本人也是这样的看法.不过,经过仔细深 ...

  8. Nginx源码研究之nginx限流模块详解

    这篇文章主要介绍了Nginx源码研究之nginx限流模块详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看看吧 高并发系统有三把利器:缓存.降级和限流: 限流的目的是通过对并 ...

  9. 一点一点看JDK源码(五)java.util.ArrayList 后篇之forEach

    一点一点看JDK源码(五)java.util.ArrayList 后篇之forEach liuyuhang原创,未经允许禁止转载 本文举例使用的是JDK8的API 目录:一点一点看JDK源码(〇) 代 ...

最新文章

  1. 《Java从入门到精通》第九章学习笔记
  2. PHP源码设置超出隐藏,怎样隐藏文本的超出部分
  3. 简单的表单提交 by MVC 2
  4. BZOJ 1257 [CQOI2007]余数之和sum ——Dirichlet积
  5. VTK:图表之DepthFirstSearchIterator
  6. Google调查了人们过去24小时的观看记录,发现了这些......
  7. 牛客网_PAT乙级_1027在霍格沃茨找零钱(20)
  8. 《犯罪心理学》读书笔记(part7)--犯罪心理的主观差异(中上)
  9. U盘版便携式Linux制作, casper-rw 解析
  10. .NET 原理之 ViewState
  11. WARNING: A newer version of conda exists.
  12. 软件开发工具--自考2019年4月
  13. 64位内核第三讲,Windbg的使用.以及命令
  14. COSCon'21 大数据(D)论坛介绍
  15. 小德,真的没想到,去年的千年德三子到现如今的现象级
  16. php匹配ubb,UBB类 php UBB 解析实现代码
  17. 让博客Docker化,轻松上手Docker
  18. xmind 使用教程
  19. delphi10.2的Date/time 实用程序单元DateUtils.pas
  20. 回车符号和换行符号的区别

热门文章

  1. odoo开发笔记--一个模块显示两个一级菜单
  2. Oracle计划将ZGC项目提交给OpenJDK
  3. Linux vmstat命令详细解读
  4. 一种NVMe SSD友好的数据存储系统设计
  5. Hibernate(2)——Hibernate的实现原理总结和对其模仿的demo
  6. 关于 automation服务器对象不能创建
  7. C#基础解析之Ⅱ【运算符和条件结构】
  8. Java读写文件,中文乱码解决
  9. 拜读及分析Element源码-alert组件篇
  10. centos6.9配置LAMT页面500错误解决