2021SC@SDUSC
这篇博客中我将继续分析IncludeTransformer.java里的两个内部类class IncludeElement和class IncludeXMLPipe。
文章目录
IncludeTransformer.java(下)
class IncludeElement
1.属性:
//参数控制递归包括处理。
private boolean recursive;
//参数控制并行(在多个线程)包括处理。
private boolean parallel;
//参数控制并行(在多个线程)包括处理递归。
private boolean recursiveParallel;
//源URI。
private String base;
//include元素的src属性中声明的要包含的源URI。
protected String source;
//指示源内容是否必须解析为XML或包含为文本的标志。
protected boolean parse;
//mime类型提示。
protected String mimeType;
//指示是否剥离根元素的标志。
protected boolean stripRoot;
//缓冲区收集回退内容。
protected SaxBuffer fallback;
//提供给包含的源的参数的Map。
protected Map parameters;
//当前捕获的参数名。
protected String parameter;
//当前参数值(作为StringBuffer)。
protected StringBuffer value;
2.方法:
构造函数:
- 创建include元素。
- 传入的参数有:
String base, boolean parallel, boolean recursive, boolean recursiveParallel
成员函数:
public void process(SaxBuffer buffer)
- 处理元素进入缓冲区。
- 这不能被共享缓冲区,因为如果调用回退,它必须被清理。
- 直接调用process0(buffer, buffer);
public void process(ContentHandler contentHandler, LexicalHandler lexicalHandler)
- 重载上一个成员函数。
- 将URI加载到提供的处理程序中,处理回退。
- 如果有回退,就调用process(buffer);,否则调用process0(contentHandler, lexicalHandler);
private void process0(ContentHandler contentHandler, LexicalHandler lexicalHandler)
- 将URI加载到提供的处理程序中。
- 设置这个线程的环境。
- 通过判断base是否为空,来决定resolver.resolveURI()的参数。
- 如果validity不为空,就进行同步处理。
- 如果parse和recursive均存在,则直接调用toSAX()函数;如果只有parse存在,则创建一个IncludeXMLConsumer对象,设置setIgnoreRootElement,最后调用toSAX()函数;否则,就直接将source转换为utf-8的字符。
- 最后是一些异常的处理,包括IOException,ProcessingException等。
class IncludeXMLPipe
1.属性:
配置:
//表示这是根管道(由变转换器拥有)还是嵌套管道。
private final boolean root;
//参数控制递归包括处理。
private boolean recursive;
//参数控制并行(在多个线程)包括处理。
private boolean parallel;
//参数控制并行(在多个线程)包括处理递归。
private boolean recursiveParallel;
当前状态:
//XMLConsumers栈。
private final Stack consumers = new Stack();
//include命名空间中嵌套元素的当前深度。
private int depth;
//用于解析包含源的Base URI。
private String base;
//在include元素中声明的要被包含的源。
private IncludeElement element;
//如果启用并行处理,则这个布尔值告诉我们是否已经开始缓冲。
private boolean buffering;
//在打开并行处理时,用于缓冲事件的IncludeBuffer。
//此对象也用作线程计数器线程的锁。
private SaxBuffer buffer;
//包含线程/任务计数器(如果并行执行)。
private int threads;
2.方法:
构造函数:
public IncludeXMLPipe() {
root = true;
}
public IncludeXMLPipe(ContentHandler contentHandler, LexicalHandler lexicalHandler,boolean recursive, boolean parallel, boolean recursiveParallel) {
root = false;
setContentHandler(contentHandler);
setLexicalHandler(lexicalHandler);
this.recursive = recursive;
this.parallel = parallel;
this.recursiveParallel = recursiveParallel;
}
创建一个新的IncludeXMLPipe实例。
成员函数:
public void recycle()
- 完成处理。
private void push(XMLConsumer consumer)
- 将当前用户推入堆栈,用新的用户替换。
private void pop()
- 从堆栈中取出用户,替换当前用户。
public void setDocumentLocator(Locator locator)
- 设置文档定位器。
- 能够解析文档基本URI。
public void startDocument()
- 接收XML文档开始的通知。
public void endDocument()
- 接收XML文档结束的通知。
- 这是该行的结束——处理缓冲事件。
public void startElement(String uri, String localName, String qName, Attributes atts)
- 接收元素开始的通知。
- 检查名称空间声明。
- 如果相等,则include命名空间中嵌套元素的当前深度减1。
if (INCLUDE_ELEMENT.equals(localName) && depth == 0)
- 包含不会在这里发生,但当我们关闭这个标签时。
- 在包含之前进行检查(我们不想要嵌套的东西),记住我们试图包含的源代码。
- 然后进行处理,默认为“xml”,默认为“text / xml”/,默认为false。
- 忽略嵌套的内容,最后结束。
if (FALLBACK_ELEMENT.equals(localName) && depth == 1)
- 如果这是一个回退参数,则捕获它的内容。
- 检查我们是否在正确的上下文中,缓冲区回退,完成。
if (PARAMETER_ELEMENT.equals(localName) && depth == 1)
- 如果这是一个参数,那么确保我们准备好了。
- 检查我们是否在正确的上下文中。
- 获取并处理参数名,完成。
if (depth < 2)
- 错误的元素。
public void endElement(String uri, String localName, String qName)
- 接收元素结束的通知。
- 检查名称空间声明。
- 如果相等,则include命名空间中嵌套元素的当前深度减1。
if (INCLUDE_ELEMENT.equals(localName) && depth == 0)
- 当我们关闭include元素时,包含将在这里发生。
- 结束忽略嵌套内容。
- 获取打开元素和include时发现的源代码。
- 并行处理检查,然后结束。
if (FALLBACK_ELEMENT.equals(localName) && depth == 1)
- 结束缓冲回退内容,该元素已完成。
if (PARAMETER_ELEMENT.equals(localName) && depth == 1)
- 存储参数名称和值,参数名和值是URL编码的,所以像"&“或”="(有特殊含义)这样的奇怪字符可以完美地传递,完成。
- 如果不是我们的名称空间,请继续传递事件。
public void characters(char[] data, int offset, int length)
- 接收字符通知。
- 如果我们有一个要添加的参数值,让我们添加这个块。
- 线程管理:
int incrementThreads()
- 活动线程递增计数器。
void decrementThreads()
- 活动线程递减计数器。
private void waitForThreads()
- 等待直到没有活跃线程。
- 如果线程数目大于0,就在日志里进行记录,并等待。
3.其他说明:
一个在内部类里的内部类:
在单独的线程中加载包含源的缓冲区。只有当源完全加载时,才可能对加载的缓冲区进行流处理。如果加载未完成,toSAX方法将阻塞。
private class IncludeBuffer extends SaxBuffer implements Runnable {
private IncludeElement element;
private int thread;
private boolean finished;
private SAXException e;
//构造函数
public IncludeBuffer(IncludeElement element) {
this.element = element;
RunnableManager runnable = null;
try {
runnable = (RunnableManager) IncludeTransformer.this.manager.lookup(RunnableManager.ROLE);
runnable.execute(IncludeTransformer.this.threadPool, this);
} catch (final ServiceException e) {
//以防我们无法生成一个线程
throw new CascadingRuntimeException(e.getMessage(), e);
} finally {
IncludeTransformer.this.manager.release(runnable);
}
//递增活动线程计数器
this.thread = incrementThreads();
}
//将源的内容加载到这个缓冲区中。
public void run() {
try {
if (getLogger().isDebugEnabled()) {
getLogger().debug("Thread #" + thread + " loading <" + element.source + ">");
}
//设置这个线程的环境
RequestContextHolder.setRequestAttributes(attributes);
EnvironmentHelper.enterProcessor(processor, environment);
try {
element.process(this);
} catch (SAXException e) {
this.e = e;
} finally {
EnvironmentHelper.leaveProcessor();
RequestContextHolder.resetRequestAttributes();
}
} catch (ProcessingException e) {
//无法设置线程的环境
this.e = new SAXException(e);
} finally {
synchronized (this) {
this.finished = true;
notify();
}
//确保活动线程计数器是递减的
decrementThreads();
}
if (getLogger().isDebugEnabled()) {
if (this.e == null) {
getLogger().debug("Thread #" + thread + " loaded <" + element.source + ">");
} else {
getLogger().debug("Thread #" + thread + " failed to load <" + element.source + ">", this.e);
}
}
}
//该缓冲区的流内容时,它是完全加载。如果加载未完成,此方法将阻塞。
public void toSAX(ContentHandler contentHandler)
throws SAXException {
synchronized (this) {
if (!this.finished) {
try {
wait();
} catch (InterruptedException e) { /* ignored */ }
// Don't continue waiting if interrupted.
}
}
if (this.e != null) {
throw this.e;
}
super.toSAX(contentHandler);
}
}
4.关键字synchronized
synchronized是Java中的关键字,被Java原生支持,是一种最基本的同步锁。
它修饰的对象有以下几种:
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象。
2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象。
3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象。
4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。