一、mina的框架回顾

  责任链模式在mina中有重要的作用,其中Filter机制就是基于责任链实现的。

从上图看到消息的接受从IoService层先经过Filter层过滤处理后最后交给IoHander,消息的发送则是反过来从IoHander层经过Filter层再到IoService层。
我们来想想这样做的好处:
第一点就是可以把消息从原始字节封装成对象方便处理,或者从对象到原始字节,那这就是decode和encode过程。
第二点过滤消息,业务层只需要处理感兴趣的消息。
当然还有其他...
再来想想Filter层是怎么实现的呢
从图中看到接收消息和发送消息经过Filter层是相反处理的,那么每个Filter就必须知道前一个和后一个Filter,我们就很容易想到了双向链表结构。那么让我们来看看Filter层具体是如何实现,即mina中责任链模式的实现。

二、mina中filter

2.1、mina中的filterchain包结构

  • IoFilter接口:Filter层的每个filter都是对上图IoFilter接口的实现。
  • IoFilterChainBuilder接口和DefaultIoFilterChainBuilder实现不再细讲,从字面意思就是IoFilterChain的建造者。
  • IoFilterEvent是代表filter事件,IoFilterLifeCycleException是指filter中出现循环链表异常。
下面的图是我们要重点讲解的几个类的关系

我们重点讲解的只有四个类,为什么会出现这么多的类呢,有些类使内部实现,不太好表示就都画出来了,先来说明下
IoFilter接口:NextFilter接口是其内部接口。
--IoFilterAdapter类:对IoFilter接口的实现,是所有Filter的基类。
IoFilterChain接口:Entry接口是其内部接口。
--DefaultIoFilterChain类:是对IoFilterChain接口的实现,有EntryImpl,HeadFilter,TailFilter三个内部类,其中EntryImpl类中又有NextFilter接口的内部实现。
最主要的一个接口IoFilterChain和它的一个内部接口Entry,其中IoFilterChain代表了过滤器的容器,它本身就是一个对象引用形成的链表结构,默认的实现DefaultIoFilterChain其实有对链表头(head)的引用,找到头后就可以顺着头向下找,一直找到尾(tail),Entry是对IoFilter和NextFilter的封装整合接口,它属于链表IoFilterChain中的元素。指向当前和下一个过滤器。
 
HeadFilter类只对发送消息处理方法重载,TailFilter类只对接受消息处理方法重载。想一想便得知,HeadFilter是发送消息最后的处理节点,TailFilter是接受消息最后的处理节点。最后的节点处理就是将写消息交给Io线程处理,将读消息交给IoHander的业务层处理。所以说HeadFilter和TailFilter只需对某一方消息处理,反面消息默认交给下一个节点处理。

三、mina的filter中实现类

3.0、NextFilter接口

  NextFilter接口看上去和IoFilter接口差不多,但NextFilter接口代表的是“下一个filter”,这里的下是抽象的,因为在mina的各种链中,处理顺序有的是从头到尾,有的是从尾到头,而这里的下就代表了熟悉中的下一个filter。

3.1、HeadFilter

  HeadFilter类只对发送消息处理方法重载,HeadFilter是发送消息最后的处理节点。

3.1.1、方法列表

3.1.2、调用IoService(边界)

关键代码见下面,将待发送的数据(字节)写到缓冲队列里,或直接调用flush()写到系统缓冲区。

  1. WriteRequestQueue writeRequestQueue = s.getWriteRequestQueue();
  2.  
  3. if (!s.isWriteSuspended()) {
  4. if (writeRequestQueue.size() == 0) {
  5. // We can write directly the message
  6. s.getProcessor().write(s, writeRequest);
  7. } else {
  8. s.getWriteRequestQueue().offer(s, writeRequest);
  9. s.getProcessor().flush(s);
  10. }
  11. } else {
  12. s.getWriteRequestQueue().offer(s, writeRequest);
  13. }

3.1.1、全部源码

  1. private class HeadFilter extends IoFilterAdapter {
  2. @SuppressWarnings("unchecked")
  3. @Override
  4. public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
  5.  
  6. AbstractIoSession s = (AbstractIoSession) session;
  7.  
  8. // Maintain counters.
  9. if (writeRequest.getMessage() instanceof IoBuffer) {
  10. IoBuffer buffer = (IoBuffer) writeRequest.getMessage();
  11. // I/O processor implementation will call buffer.reset()
  12. // it after the write operation is finished, because
  13. // the buffer will be specified with messageSent event.
  14. buffer.mark();
  15. int remaining = buffer.remaining();
  16.  
  17. if (remaining == 0) {
  18. // Zero-sized buffer means the internal message
  19. // delimiter.
  20. s.increaseScheduledWriteMessages();
  21. } else {
  22. s.increaseScheduledWriteBytes(remaining);
  23. }
  24. } else {
  25. s.increaseScheduledWriteMessages();
  26. }
  27.  
  28. WriteRequestQueue writeRequestQueue = s.getWriteRequestQueue();
  29.  
  30. if (!s.isWriteSuspended()) {
  31. if (writeRequestQueue.size() == 0) {
  32. // We can write directly the message
  33. s.getProcessor().write(s, writeRequest);
  34. } else {
  35. s.getWriteRequestQueue().offer(s, writeRequest);
  36. s.getProcessor().flush(s);
  37. }
  38. } else {
  39. s.getWriteRequestQueue().offer(s, writeRequest);
  40. }
  41. }
  42.  
  43. @SuppressWarnings("unchecked")
  44. @Override
  45. public void filterClose(NextFilter nextFilter, IoSession session) throws Exception {
  46. ((AbstractIoSession) session).getProcessor().remove(session);
  47. }
  48. }

3.2、TailFilter  

TailFilter类只对接受消息处理方法重载。

3.2.1、方法列表

3.2.2、调用IoHandler(边界)

先判断待处理数据类型,如果不满足条件则继续读取数据。否则,调用handler类来接收处理消息。

  1. public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {
  2. AbstractIoSession s = (AbstractIoSession) session;
  3. if (!(message instanceof IoBuffer)) {
  4. s.increaseReadMessages(System.currentTimeMillis());
  5. } else if (!((IoBuffer) message).hasRemaining()) {
  6. s.increaseReadMessages(System.currentTimeMillis());
  7. }
  8.  
  9. try {
  10. session.getHandler().messageReceived(s, message);
  11. } finally {
  12. if (s.getConfig().isUseReadOperation()) {
  13. s.offerReadFuture(message);
  14. }
  15. }
  16. }

3.2.3、全部源码

  1. private static class TailFilter extends IoFilterAdapter {
  2. @Override
  3. public void sessionCreated(NextFilter nextFilter, IoSession session) throws Exception {
  4. try {
  5. session.getHandler().sessionCreated(session);
  6. } finally {
  7. // Notify the related future.
  8. ConnectFuture future = (ConnectFuture) session.removeAttribute(SESSION_CREATED_FUTURE);
  9. if (future != null) {
  10. future.setSession(session);
  11. }
  12. }
  13. }
  14.  
  15. @Override
  16. public void sessionOpened(NextFilter nextFilter, IoSession session) throws Exception {
  17. session.getHandler().sessionOpened(session);
  18. }
  19.  
  20. @Override
  21. public void sessionClosed(NextFilter nextFilter, IoSession session) throws Exception {
  22. AbstractIoSession s = (AbstractIoSession) session;
  23. try {
  24. s.getHandler().sessionClosed(session);
  25. } finally {
  26. try {
  27. s.getWriteRequestQueue().dispose(session);
  28. } finally {
  29. try {
  30. s.getAttributeMap().dispose(session);
  31. } finally {
  32. try {
  33. // Remove all filters.
  34. session.getFilterChain().clear();
  35. } finally {
  36. if (s.getConfig().isUseReadOperation()) {
  37. s.offerClosedReadFuture();
  38. }
  39. }
  40. }
  41. }
  42. }
  43. }
  44.  
  45. @Override
  46. public void sessionIdle(NextFilter nextFilter, IoSession session, IdleStatus status) throws Exception {
  47. session.getHandler().sessionIdle(session, status);
  48. }
  49.  
  50. @Override
  51. public void exceptionCaught(NextFilter nextFilter, IoSession session, Throwable cause) throws Exception {
  52. AbstractIoSession s = (AbstractIoSession) session;
  53. try {
  54. s.getHandler().exceptionCaught(s, cause);
  55. } finally {
  56. if (s.getConfig().isUseReadOperation()) {
  57. s.offerFailedReadFuture(cause);
  58. }
  59. }
  60. }
  61.  
  62. @Override
  63. public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {
  64. AbstractIoSession s = (AbstractIoSession) session;
  65. if (!(message instanceof IoBuffer)) {
  66. s.increaseReadMessages(System.currentTimeMillis());
  67. } else if (!((IoBuffer) message).hasRemaining()) {
  68. s.increaseReadMessages(System.currentTimeMillis());
  69. }
  70.  
  71. try {
  72. session.getHandler().messageReceived(s, message);
  73. } finally {
  74. if (s.getConfig().isUseReadOperation()) {
  75. s.offerReadFuture(message);
  76. }
  77. }
  78. }
  79.  
  80. @Override
  81. public void messageSent(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
  82. session.getHandler().messageSent(session, writeRequest.getMessage());
  83. }
  84.  
  85. @Override
  86. public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
  87. nextFilter.filterWrite(session, writeRequest);
  88. }
  89.  
  90. @Override
  91. public void filterClose(NextFilter nextFilter, IoSession session) throws Exception {
  92. nextFilter.filterClose(session);
  93. }
  94. }

3.3、Entry接口

  Entry接口是IoFilterChain接口的一个内部接口,Entry是对IoFilter和NextFilter的封装整合接口,它属于链表IoFilterChain中的元素。指向当前和下一个过滤器。其中IoFilterChain接口代表了过滤器的容器,它本身就是一个对象引用形成的链表结构,默认的实现DefaultIoFilterChain其实有对链表头(head)的引用,找到头后就可以顺着头向下找,一直找到尾(tail)。

Entry接口与IoFilterChain接口关系如下:

3.4、EntryImpl类

  EntryImpl是Entry接口的实现类,类的成员中含两个接口,filter和nextFilter,他们所用的接口是不一样的,至于为什么不用同一个接口,我想可能是因为接口职责单一的原则吧。

从EntryImpl类的构造方法看到,EntryImpl中保持对上一个节点和下一个节点引用,双向链表结构,name即过滤层名称,filter即过滤层的具体实现,而nextFilter是在构造方法中的内部实现。

  1. private class EntryImpl implements Entry {
  2. private EntryImpl prevEntry;
  3.  
  4. private EntryImpl nextEntry;
  5.  
  6. private final String name;
  7.  
  8. private IoFilter filter;
  9.  
  10. private final NextFilter nextFilter;

3.4.2、IoFilter接口和NextFilter接口:

虽然有IoFilter和NextFilter两个接口,接口方法都差不多,但最后真正业务的执行者还是IoFilter的实现,

IoFilter:IoFilterAdapter做为它的默认实现,完成了适配器的功能,以后的类可以直接继承它而不用实现IoFilter接口,想实现哪个方法只需要覆盖IoFilterAdapter的类的方法即可。

NextFilter:只起到转发的作用,看EntryImpl中的匿名的NextFilter实现类中,基本每个方法都是调用callNextSessionIdle()来完成转发的。

3.4.3、全部源码

  1. private class EntryImpl implements Entry {
  2. private EntryImpl prevEntry;
  3.  
  4. private EntryImpl nextEntry;
  5.  
  6. private final String name;
  7.  
  8. private IoFilter filter;
  9.  
  10. private final NextFilter nextFilter;
  11.  
  12. private EntryImpl(EntryImpl prevEntry, EntryImpl nextEntry, String name, IoFilter filter) {
  13. if (filter == null) {
  14. throw new IllegalArgumentException("filter");
  15. }
  16. if (name == null) {
  17. throw new IllegalArgumentException("name");
  18. }
  19.  
  20. this.prevEntry = prevEntry;
  21. this.nextEntry = nextEntry;
  22. this.name = name;
  23. this.filter = filter;
  24. this.nextFilter = new NextFilter() {
  25. public void sessionCreated(IoSession session) {
  26. Entry nextEntry = EntryImpl.this.nextEntry;
  27. callNextSessionCreated(nextEntry, session);
  28. }
  29. public void sessionOpened(IoSession session) {
  30. Entry nextEntry = EntryImpl.this.nextEntry;
  31. callNextSessionOpened(nextEntry, session);
  32. }
  33. public void sessionClosed(IoSession session) {
  34. Entry nextEntry = EntryImpl.this.nextEntry;
  35. callNextSessionClosed(nextEntry, session);
  36. }
  37. public void sessionIdle(IoSession session, IdleStatus status) {
  38. Entry nextEntry = EntryImpl.this.nextEntry;
  39. callNextSessionIdle(nextEntry, session, status);
  40. }
  41. public void exceptionCaught(IoSession session, Throwable cause) {
  42. Entry nextEntry = EntryImpl.this.nextEntry;
  43. callNextExceptionCaught(nextEntry, session, cause);
  44. }
  45. public void messageReceived(IoSession session, Object message) {
  46. Entry nextEntry = EntryImpl.this.nextEntry;
  47. callNextMessageReceived(nextEntry, session, message);
  48. }
  49. public void messageSent(IoSession session, WriteRequest writeRequest) {
  50. Entry nextEntry = EntryImpl.this.nextEntry;
  51. callNextMessageSent(nextEntry, session, writeRequest);
  52. }
  53. public void filterWrite(IoSession session, WriteRequest writeRequest) {
  54. Entry nextEntry = EntryImpl.this.prevEntry;
  55. callPreviousFilterWrite(nextEntry, session, writeRequest);
  56. }
  57. public void filterClose(IoSession session) {
  58. Entry nextEntry = EntryImpl.this.prevEntry;
  59. callPreviousFilterClose(nextEntry, session);
  60. }
  61. public String toString() {
  62. return EntryImpl.this.nextEntry.name;
  63. }
  64. };
  65. }public void addAfter(String name, IoFilter filter) {
  66. DefaultIoFilterChain.this.addAfter(getName(), name, filter);
  67. }
  68.  
  69. public void addBefore(String name, IoFilter filter) {
  70. DefaultIoFilterChain.this.addBefore(getName(), name, filter);
  71. }
  72.  
  73. public void remove() {
  74. DefaultIoFilterChain.this.remove(getName());
  75. }
  76.  
  77. public void replace(IoFilter newFilter) {
  78. DefaultIoFilterChain.this.replace(getName(), newFilter);
  79. }
  80. }

3.5、IoFilterEvent

  

  在org.apache.mina.core.filterchain包下我们可以看到类IoFilterEvent,可以看到它实现了基于事件的处理模型,当一个事件(比如接收到消息)发生后会触发相应的事件,进而调用过滤器链的消息处理功能进行消息的处理和转发,这块其实会有线程池的参与完成,会在以后的文章中说明这块。
  1. public class IoEvent implements Runnable {
  2. private final IoEventType type;
  3.  
  4. private final IoSession session;
  5.  
  6. private final Object parameter;
  7.  
  8. public IoEvent(IoEventType type, IoSession session, Object parameter) {
  9. if (type == null) {
  10. throw new IllegalArgumentException("type");
  11. }
  12. if (session == null) {
  13. throw new IllegalArgumentException("session");
  14. }
  15. this.type = type;
  16. this.session = session;
  17. this.parameter = parameter;
  18. }
  19.  
  20. public void run() {
  21. fire();
  22. }
  23.  
  24. public void fire() {
  25. switch (getType()) {
  26. case MESSAGE_RECEIVED:
  27. getSession().getFilterChain().fireMessageReceived(getParameter());
  28. break;
  29. case MESSAGE_SENT:
  30. getSession().getFilterChain().fireMessageSent((WriteRequest) getParameter());
  31. break;
  32. case WRITE:
  33. getSession().getFilterChain().fireFilterWrite((WriteRequest) getParameter());
  34. break;
  35. case CLOSE:
  36. getSession().getFilterChain().fireFilterClose();
  37. break;
  38. case EXCEPTION_CAUGHT:
  39. getSession().getFilterChain().fireExceptionCaught((Throwable) getParameter());
  40. break;
  41. case SESSION_IDLE:
  42. getSession().getFilterChain().fireSessionIdle((IdleStatus) getParameter());
  43. break;
  44. case SESSION_OPENED:
  45. getSession().getFilterChain().fireSessionOpened();
  46. break;
  47. case SESSION_CREATED:
  48. getSession().getFilterChain().fireSessionCreated();
  49. break;
  50. case SESSION_CLOSED:
  51. getSession().getFilterChain().fireSessionClosed();
  52. break;
  53. default:
  54. throw new IllegalArgumentException("Unknown event type: " + getType());
  55. }
  56. }
  57.  
  58. }

IoFilterEvent 实现类

  1. public class IoFilterEvent extends IoEvent {
  2. /** A logger for this class */
  3. static Logger LOGGER = LoggerFactory.getLogger(IoFilterEvent.class);
  4.  
  5. /** A speedup for logs */
  6. static boolean DEBUG = LOGGER.isDebugEnabled();
  7. private final NextFilter nextFilter;
  8.  
  9. public IoFilterEvent(NextFilter nextFilter, IoEventType type, IoSession session, Object parameter) {
  10. super(type, session, parameter);
  11.  
  12. if (nextFilter == null) {
  13. throw new IllegalArgumentException("nextFilter must not be null");
  14. }
  15. this.nextFilter = nextFilter;
  16. }
  17. public NextFilter getNextFilter() {
  18. return nextFilter;
  19. }
  20.  
  21. @Override
  22. public void fire() {
  23. IoSession session = getSession();
  24. NextFilter nextFilter = getNextFilter();
  25. IoEventType type = getType();
  26.  
  27. if (DEBUG) {
  28. LOGGER.debug("Firing a {} event for session {}", type, session.getId());
  29. }
  30.  
  31. switch (type) {
  32. case MESSAGE_RECEIVED:
  33. Object parameter = getParameter();
  34. nextFilter.messageReceived(session, parameter);
  35. break;
  36.  
  37. case MESSAGE_SENT:
  38. WriteRequest writeRequest = (WriteRequest) getParameter();
  39. nextFilter.messageSent(session, writeRequest);
  40. break;
  41.  
  42. case WRITE:
  43. writeRequest = (WriteRequest) getParameter();
  44. nextFilter.filterWrite(session, writeRequest);
  45. break;
  46.  
  47. case CLOSE:
  48. nextFilter.filterClose(session);
  49. break;
  50.  
  51. case EXCEPTION_CAUGHT:
  52. Throwable throwable = (Throwable) getParameter();
  53. nextFilter.exceptionCaught(session, throwable);
  54. break;
  55.  
  56. case SESSION_IDLE:
  57. nextFilter.sessionIdle(session, (IdleStatus) getParameter());
  58. break;
  59.  
  60. case SESSION_OPENED:
  61. nextFilter.sessionOpened(session);
  62. break;
  63.  
  64. case SESSION_CREATED:
  65. nextFilter.sessionCreated(session);
  66. break;
  67.  
  68. case SESSION_CLOSED:
  69. nextFilter.sessionClosed(session);
  70. break;
  71.  
  72. default:
  73. throw new IllegalArgumentException("Unknown event type: " + type);
  74. }
  75.  
  76. }
  77. }

3.6、IoFilterLifeCycleException

mina中有3处使用了它,

DefaultIoFilterChain.clear()
DefaultIoFilterChain.deregister(EntryImpl entry)
DefaultIoFilterChain.register(EntryImpl prevEntry, String name, IoFilter filter)
也就是在加入/删除filter时,为双向链表的每个元素对比是否已经存在,如果存在则有存在环的风险。抛异常。
 
读过程:

示例讲解filter如何工作:
fireSessionOpened方法获取当前的头节点,然后调用callNextSessionOpened方法,而callNextSessionOpened方法是从entry中获取filter和nextfitler,触发filter的sessionOpened方法,同时将nextfilter作为参数传进去,而filter层如果对这个消息感兴趣可以处理完成后调用nextfilter的sessionOpened方法,不感兴趣的话,可能消息到此就结束了。
再回到上面EntryImpl中对NextFilter的实现,我们看到NextFilter收到sessionOpen消息,获取当前节点的下一个节点,然后触发IoFilterChain的callNextSessionOpened方法,即上图所示。再然后就是传递到下一节点处理,要么filter层拦截过滤结束,要不就是传到最后一层由TailFilter交给业务层处理。而写消息恰好相反,nextFilter是获取前一个节点,这就实现了双向过滤的功能。
到这我们就明白了为什么EntryImpl还有NextFilter选择内部类实现了。
NextFilter其实是起到中转站的作用,收到Reveceive消息转交给后一节点,收到Send消息转交给前一个消息。那我们再来想想为什么要用NextFilter来作为中转呢?我想应该是接口隔离的原则。Filter只需要关心如何处理接受到的消息,至于如何转交到下一个Filter不应该由他实现。
 
参考:http://www.iteye.com/topic/1124504
参考:http://blog.csdn.net/qarkly112649/article/details/37498251

mina中责任链模式的实现的更多相关文章

  1. es6 class中责任链模式与AOP结合

    责任链模式大家应该都清楚,比如JS中的冒泡,Java中的拦截器.过滤器,都运用到了责任链模式. 可以看我之前的文章介绍责任链的:https://www.cnblogs.com/wuguanglin/p ...

  2. Activiti工作流学习笔记(四)——工作流引擎中责任链模式的建立与应用原理

    原创/朱季谦 本文需要一定责任链模式的基础,主要分成三部分讲解: 一.简单理解责任链模式概念 二.Activiti工作流里责任链模式的建立 三.Activiti工作流里责任链模式的应用 一.简单理解责 ...

  3. C#设计模式-责任链模式

    在现实生活中,有很多请求并不是一个人说了就算的,例如面试时的工资,低于1万的薪水可能技术经理就可以决定了,但是1万~1万5的薪水可能技术经理就没这个权利批准,可能就需要请求技术总监的批准,所以在面试的 ...

  4. C#设计模式(21)——责任链模式

    一.引言 在现实生活中,有很多请求并不是一个人说了就算的,例如面试时的工资,低于1万的薪水可能技术经理就可以决定了,但是1万~1万5的薪水可能技术经理就没这个权利批准,可能就需要请求技术总监的批准,所 ...

  5. 设计模式学习之责任链模式(Chain of Responsibility,行为型模式)(22)

    参考:http://www.cnblogs.com/zhili/p/ChainOfResponsibity.html 一.引言 在现实生活中,有很多请求并不是一个人说了就算的,例如面试时的工资,低于1 ...

  6. Java设计模式13:责任链模式

    前言 来菜鸟这个大家庭10个月了,总得来说比较融入了环境,同时在忙碌的工作中也深感技术积累不够,在优秀的人身边工作必须更加花时间去提升自己的技术能力.技术视野,所以开一个系列文章,标题就轻松一点叫做最 ...

  7. 责任链模式的使用-Netty ChannelPipeline和Mina IoFilterChain分析

    本文来自网易云社区 作者:乔安然 1. Chain of Responsiblity 定义: 使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将这个对象连成一条链,并沿着这条链 ...

  8. [工作中的设计模式]责任链模式chain

    一.模式解析 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象决定处理此请求.发出这个请求的客户端并不知 ...

  9. java设计模式之责任链模式以及在java中作用

    责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象决定处理此请求.发出这个请求的客户端并不知道链上的哪一个 ...

随机推荐

  1. tomcat_下载

    1. http://tomcat.apache.org/ 2. 3.

  2. MVVM4

    原地址(内容更丰富):http://www.cnblogs.com/888h/category/269092.html MVVM架构的简单解析   图1 View.ViewModel.Model之间的 ...

  3. python面向对象编程学习

    python面向对象编程 基本概念理解 面向对象编程--Object Oriented Programming,简称OOP,是一种程序设计思想.OOP把对象作为程序的基本单元,一个对象包含了数据和操作 ...

  4. mysql中的左连接右连接内连接

    一. 初始化SQL语句 /*join 建表语句*/ drop database if exists test; create database test; use test; /* 左表t1*/ dr ...

  5. Educational Codeforces Round 23D

    给n个数求每个子区间的价值,区间的价值是最大值-最小值 套路题= =,分别算最大值和最小值的贡献,用并查集维护,把相邻点连一条边,然后sort,求最大是按边价值(两个点的最大价值)小的排,求最小是按最 ...

  6. iOS自动化探索(二)WDA API的使用

    前面我们已经安装好了WebdriverAgent, 现在可以用Facebook官方提供的API来进行一些操作 WDA API官方页面: https://github.com/facebook/WebD ...

  7. Prism开发人员指南5-WPF开发 文档翻译(纯汉语版)

    2014四月       Prism以示例和文档的形式帮助你更简单的设计丰富灵活易维护的WPF程序.其中使用的设计模式体现了一些重要的设计原则,例如分离关注点和松耦合,Prism帮助你利用松耦合组件设 ...

  8. C3P0使用详解

    定义: C3P0是一个开源的JDBC连接池,目前使用它的开源项目有Hibernate,Spring等. 数据库连接池的基本思想就是为数据库连接建立一个"缓冲池".预先在缓冲池中放入 ...

  9. UI- 不易记知识点汇总

    1.static: 所有的全局变量都是静态变量,而局部变量只有定义时加上类型修饰符static,才为局部静态变量. 静态变量并不是说其就不能改变值,不能改变值的量叫常量. 其拥有的值是可变的 ,而且它 ...

  10. 通过TortoiseSVN checkout的文件前面没有“状态标识”

    问题描述:安装完成VisualSVN Server.VisualSVn和TortoiseSVN后,然后通过SVN Server新建Repository(仓库),用Visual Studio新建一个So ...