装饰者模式源码解析(spring-session mybatis jdk servlet)

那在JDK中体现最明显的,就是JAVA IO方面的一些类,那在JAVA IO中,我们为了增加缓存,我们使用BufferedReader,

那现在我们来看一下,那因为增加缓存的功能,类有很多,子类也就需要很多,那现在我们看一下,BufferedReader

public class BufferedReader extends Reader

看一下,他首先继承了Reader,进来

public abstract class Reader implements Readable, Closeable

是一个抽象类,接着回来,然后BufferedReader把Reader放到自己的类中,是in

private Reader in;

public BufferedReader(Reader in, int sz) {
	super(in);
	if (sz <= 0)
		throw new IllegalArgumentException("Buffer size <= 0");
	this.in = in;
	cb = new char[sz];
	nextChar = nChars = 0;
}

通过Reader来构造BufferedReader,现在我们来类比一下,抽象的煎饼,抽象的装饰者,构造的时候,用抽象的煎饼来构造他,

和这里是一致的,正因为在JDK中的JAVA IO,采用了装饰者模式,我们可以无限次的进行装饰转换,转换的目标啊,就是为了

得到我们想要的数据类型流对象,

class BufferedInputStream extends FilterInputStream 

同理BufferedInputStream也是一样的,

class BufferedOutputStream extends FilterOutputStream

这两个类我们只做一个,首先它继承FilterInputStream,而FilterInputStream又继承FilterInputStream,

class FilterInputStream extends InputStream

那InputStream是一个抽象类,

public abstract class InputStream implements Closeable

接着回来,看一下BufferedInputStream他的构造器,这里有两个,随便看一个就可以,

public BufferedInputStream(InputStream in) {
	this(in, DEFAULT_BUFFER_SIZE);
}

public BufferedInputStream(InputStream in, int size) {
	super(in);
	if (size <= 0) {
		throw new IllegalArgumentException("Buffer size <= 0");
	}
	buf = new byte[size];
}

传入的InputStream,是BufferedInputStream父类的父类,我们看一下fill这个方法,在这个fill这个方法中,

private void fill() throws IOException {
	byte[] buffer = getBufIfOpen();
	if (markpos < 0)
		pos = 0;            /* no mark: throw away the buffer */
	else if (pos >= buffer.length)  /* no room left in buffer */
		if (markpos > 0) {  /* can throw away early part of the buffer */
			int sz = pos - markpos;
			System.arraycopy(buffer, markpos, buffer, 0, sz);
			pos = sz;
			markpos = 0;
		} else if (buffer.length >= marklimit) {
			markpos = -1;   /* buffer got too big, invalidate mark */
			pos = 0;        /* drop buffer contents */
		} else if (buffer.length >= MAX_BUFFER_SIZE) {
			throw new OutOfMemoryError("Required array size too large");
		} else {            /* grow buffer */
			int nsz = (pos <= MAX_BUFFER_SIZE - pos) ?
					pos * 2 : MAX_BUFFER_SIZE;
			if (nsz > marklimit)
				nsz = marklimit;
			byte nbuf[] = new byte[nsz];
			System.arraycopy(buffer, 0, nbuf, 0, pos);
			if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
				// Can't replace buf if there was an async close.
				// Note: This would need to be changed if fill()
				// is ever made accessible to multiple threads.
				// But for now, the only way CAS can fail is via close.
				// assert buf == null;
				throw new IOException("Stream closed");
			}
			buffer = nbuf;
		}
	count = pos;
	int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
	if (n > 0)
		count = n + pos;
}

就实现读取流数据,我们再 看一下FileInputStream

class FilterInputStream extends InputStream

那这里的方法相对简单一些,我们看几个关键的,首先是构造器,传入File,然后装饰成FileInputStream,

public FileInputStream(File file) throws FileNotFoundException {
	String name = (file != null ? file.getPath() : null);
	SecurityManager security = System.getSecurityManager();
	if (security != null) {
		security.checkRead(name);
	}
	if (name == null) {
		throw new NullPointerException();
	}
	if (file.isInvalid()) {
		throw new FileNotFoundException("Invalid file path");
	}
	fd = new FileDescriptor();
	fd.attach(this);
	path = name;
	open(name);
}

那FileInputStream的父类,我们看一下,InputStream是抽象类,我们现在来看一下他的UML,他有很多子类,

这里面有超多的子类,我们看一下FilterInputStream,再打开几个实现,为了更好地对比,这里面打开三个,

然后我们看一下FilterInputStream他的子类也有很多,例如刚刚讲的BufferedInputStream,还有DataInputStream,

还有很多,比如LineInputStream,那我们可以看到在装饰者模式中,我们的类是非常的多,但是FilterInputStream,

是一个装饰者下面都是实际的装饰者,还有一些类我们就不显示到这里了,那Reader和Writer流中

装饰者模式源码解析(spring-session mybatis jdk servlet)_第1张图片

也是一样的,大同小异,我们在使用IO流的时候,因为类的数量比较多,有时候我们也记不住,但是如果我们能够识别出,

哪些是抽象被装饰,哪些是实际被装饰者,哪些是抽象装饰者,哪些是实体装饰者,那其实对我们理解来说是非常有益处的,

那这些就是装饰者模式在JDK中的应用,那Spring中,你再看一个类,TransactionAwareCacheDecorator,聪明的可以看出来,

这个类是处理Spring缓存和同步事务的相关类,那从类里面我们可以看出来,它实现了一个Cache

装饰者模式源码解析(spring-session mybatis jdk servlet)_第2张图片

又组合到这里,我们是不是可以认为他是一个Cache的装饰者,因为这个类名也是非常的明显,然后他的构造器也是非常的

明显,把Cache放到入参里面,Spring这个类的主要目的是提供缓存,和Spring事务的同步级别,感兴趣的可以看一下,

在Servlet中也大量的使用了装饰者模式,包括我们所讲的单点登录,也对Servlet进行了包装,我们讲过有这么一个类,

private final class SessionRepositoryRequestWrapper

private final class SessionRepositoryRequestWrapper
			extends HttpServletRequestWrapper {
	// HttpServletResponse 实例
	private final HttpServletResponse response;
	// ServletContext 实例
	private final ServletContext servletContext;
        // requestedSession session对象
        private S requestedSession; 
        // 是否缓存 session
        private boolean requestedSessionCached;
	// sessionId
	private String requestedSessionId;
	// sessionId 是否有效
	private Boolean requestedSessionIdValid;
	// sessionId 是否失效
	private boolean requestedSessionInvalidated;
	
	// 省略方法
}

装饰者模式在Spring Session和Servlet中的使用,首先这个类继承了HttpServletRequestWrapper,我们看一下

HttpServletRequestWrapper这个类是Servlet里面的类,Session是Spring Session包下的类,他继承了这个类,

然后在构建他自己的时候呢,又把HttpServletRequest传入进来,

private SessionRepositoryRequestWrapper(HttpServletRequest request,
		HttpServletResponse response, ServletContext servletContext) {
	super(request);
	this.response = response;
	this.servletContext = servletContext;
}

那他继承了Wrapper,传入的又是ServletRequest,那他两是什么关系呢,

public class HttpServletRequestWrapper extends ServletRequestWrapper implements
        HttpServletRequest

HttpServletRequestWrapper继承ServletRequestWrapper并且实现了HttpServletRequest,

这样就很好理解了,也就是说我们继承的HttpServletRequestWrapper这个类,也就实现了HttpServletRequest接口,

那在构建他的时候,我们就可以把被装饰的对象,传入进来,然后生成我们自己包装好的类,这里可以看出来,

HttpServletRequestWrapper他包装了HttpServletRequest,而SessionRepositoryRequestWrapper呢,又借力打力,

他又借着HttpServletRequestWrapper继续包装,我们再看一下HttpServletRequestWrapper这个类,在构造的时候,

也是同样的,把要实现的,要装饰的,作为构造的一个入参,同时我们再看一下,在这个类中getSession,我们看一下这个方法,

我们可以看到明显是override,明显是覆盖了这个方法,包括获取Servlet上下文,也同理获取getRequestedSessionId,

这个HttpServletRequestWrapper实现了HttpServletRequest接口,而HttpServletRequest又继承了SevletRequest接口,

继续往上看,SevletRequest他目前是最顶级的,那我们看一下ServletRequest,在他这个层次呢,有没有自己的呢

public class ServletRequestWrapper implements ServletRequest

ServletRequestWrapper他本身也存在自己的Wrapper包装器,注意这里的Request

private ServletRequest request;

正是父类ServletRequest实现的这个类,构造的时候也是同理

public ServletRequestWrapper(ServletRequest request) {
	if (request == null) {
		throw new IllegalArgumentException("Request cannot be null");
	}
	this.request = request;
}

我们看一下UML,因为这里面有很多,包装类和非包装类,我们随便打开几个来看一下,他们的结构,一直到Spring Session的

使用当中,首先HttpServletRequest跑不掉,他呢作为一个接口,继承了ServletRequest接口,我们再看一下他下面的

HttpServletRequestWrapper,现在还在Servlet中,同时ServletRequest他下面的实现还有一个ServletRequestWrapper,

ServletRequestWrapper这里是一个类,HttpServletRequestWrapper这个是一个接口,在这个接口下边继续包装,那继续来看,

SessionRepositoryRequestWrapper,这些都是装饰者的使用

装饰者模式源码解析(spring-session mybatis jdk servlet)_第3张图片

因为这里属性比较多,如果打开的话,这个图就不容易看了,我们刚刚在类中所讲的委托对象,记住就可以了,

那这里面用的也是通过委托的方式,那这条线是指Request这条线,那在Response中,是一样的,这里说一下,

因为装饰者模式,和适配器模式,都可以成为包装器,所以结尾都可能是Wrapper,但是具体的是适配器模式,

还是装饰者模式呢,我们还是要看类里面的实现,以及类与类之间的结构,所以我们不能因为是Wrapper就认为是

装饰者模式,或者看到Wrapper就是适配器模式,有些地方就把这两个叫做包装模式,或者称Wrapper这个类为

包装器,那具体用什么模式呢,我们还要具体看,那在Mybatis当中,是怎么使用装饰者模式的呢,我们看一下Mybatis中

Cache这个类,叫这个类名的特别多,我们直接写一下包名,org.apache.ibatis.cache.Cache,我们找到ibatis.cache这个

包下的Cache这个类,可以看到这个就是一个接口

public interface Cache {

  String getId();

  int getSize();

  void putObject(Object key, Object value);

  Object getObject(Object key);

  Object removeObject(Object key);

  void clear();

  ReadWriteLock getReadWriteLock();

}

同时这里有getId,getSize,读写锁,获取他的size,我们看一下这个包在哪里,我们可以看一下他的包叫decorators,

更明显了,这些类都是用来装饰Cache的,我们随便看一个,FifoCache先进先出算法

package org.apache.ibatis.cache.decorators;

public class FifoCache implements Cache 

他呢实现Cache这个类,里面看他的命名delegate

  private final Cache delegate;
  private final LinkedList keyList;
  private int size;

  public FifoCache(Cache delegate) {
    this.delegate = delegate;
    this.keyList = new LinkedList();
    this.size = 1024;
  }

他这个命名还是非常好的,也就是先进先出这个Cache呢,都是委托给Cache来做的,不过在里面加了自己的实现,

比如说这里面的keyList,使用的是LinkedList,同时new一个LinkedList,LinkedList是一个双端队列,还有LruCache,

这个你们会比较熟悉

/*
 * Lru (first in, first out) cache decorator
 */
public class LruCache implements Cache 

最近最少使用的装饰者,这个注释写的很明显,而且我在看这段源码的时候,我发现这里面对象的命名,相对容易理解一些,

并且注释非常精简,这个也是值得我们学习的地方,那从名字我们来看一下,ScheduledCache这个肯定是调度缓存方面的,

SerializedCache还有序列号和反序列化的一些缓存,软引用SoftCache,还有同步缓存SynchronizedCache,还有多线程并发

访问,这里可以看一下,里面的方法都是有同步方法修饰的,

public class SynchronizedCache implements Cache {

  private Cache delegate;

  public SynchronizedCache(Cache delegate) {
    this.delegate = delegate;
  }

  public String getId() {
    return delegate.getId();
  }

  public int getSize() {
    acquireReadLock();
    try {
      return delegate.getSize();
    } finally {
      releaseReadLock();
    }
  }

  public void putObject(Object key, Object object) {
    acquireWriteLock();
    try {
      delegate.putObject(key, object);
    } finally {
      releaseWriteLock();
    }
  }

  public Object getObject(Object key) {
    acquireReadLock();
    try {
      return delegate.getObject(key);
    } finally {
      releaseReadLock();
    }
  }

  public Object removeObject(Object key) {
    acquireWriteLock();
    try {
      return delegate.removeObject(key);
    } finally {
      releaseWriteLock();
    }
  }


  public void clear() {
    acquireWriteLock();
    try {
      delegate.clear();
    } finally {
      releaseWriteLock();
    }
  }

  public ReadWriteLock getReadWriteLock() {
    return delegate.getReadWriteLock();
  }

  public int hashCode() {
    return delegate.hashCode();
  }

  public boolean equals(Object obj) {
    return delegate.equals(obj);
  }

  private void acquireReadLock() {
    getReadWriteLock().readLock().lock();
  }

  private void releaseReadLock() {
    getReadWriteLock().readLock().unlock();
  }

  private void acquireWriteLock() {
    getReadWriteLock().writeLock().lock();
  }

  private void releaseWriteLock() {
    getReadWriteLock().writeLock().unlock();
  }

}

那这个就更好理解,他只是说,在Cache上一层包装了一下,并且都加了方法,还有一个关于事务的一个Cache

public class TransactionalCache implements Cache

事务性的一个缓存,那有兴趣的非常建议,把Mybatis Cache这些类的源码看一遍,这些都是对Cache的一些装饰,

刚刚一起看了JDK的源码,TOMCAT,SpringSession中的,还有Mybatis中的,关于装饰者模式的一个使用,那装饰者模式

在源码框架中,应用的如此广泛,包括我们使用的JDK,所以装饰者模式是我们非常值得学习的一个设计模式,希望你们

呢能够理解透,巩固好,同时呢,触类旁通,在我们自己的业务中,来抽象出来可以使用装饰者模式的一些业务模型,

当然我们不是为了使用设计模式而使用设计模式,一定是使用对应场景的时候,才来使用他

 

你可能感兴趣的:(装饰者模式源码解析(spring-session mybatis jdk servlet))