那在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流中
也是一样的,大同小异,我们在使用IO流的时候,因为类的数量比较多,有时候我们也记不住,但是如果我们能够识别出,
哪些是抽象被装饰,哪些是实际被装饰者,哪些是抽象装饰者,哪些是实体装饰者,那其实对我们理解来说是非常有益处的,
那这些就是装饰者模式在JDK中的应用,那Spring中,你再看一个类,TransactionAwareCacheDecorator,聪明的可以看出来,
这个类是处理Spring缓存和同步事务的相关类,那从类里面我们可以看出来,它实现了一个Cache
又组合到这里,我们是不是可以认为他是一个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,这些都是装饰者的使用
因为这里属性比较多,如果打开的话,这个图就不容易看了,我们刚刚在类中所讲的委托对象,记住就可以了,
那这里面用的也是通过委托的方式,那这条线是指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