Matryoshka dolls must be familiar to everyone, that is, the same kind of dolls are big and small, and then nested one by one.
In the design pattern, there is a commonly used doll pattern, called the decorator (Decorator) pattern, also known as the wrapper (Wrapper) pattern.
HttpServletRequest Matryoshka
In the web application developed by the Spring framework, if you use Spring Security or Spring Session, use the Debug mode to observe the HttpServletRequest object corresponding to a request, and you will find that this is a Russian doll:
In the figure, you can see the HttpServletRequest
object we got. The internal members contain a HttpServletRequest
object, and the internal HttpServletRequest
object contains a HttpServletRequest
object, which contains layers of layers and sets of dolls. This is a typical decorator pattern.
We know that HttpServletRequest
is an interface provided in the Servlet specification. The Servlet specification itself does not implement the HttpServletRequest
interface. The HttpServletRequest
interface is generally implemented by Servlet containers, such as Tomcat and Jetty. If frameworks such as Spring Security and Spring Session want to enhance HttpServletRequest
object, but do not change the interface of the original object, the best way is to use the decorator mode. E.g:
- Spring Security has enhanced the
HttpServletRequest.getRemoteUser()
method to return the user name of the user currently logged in through the Spring Security framework; - Spring Session enhances the
HttpServletRequest.getSession()
method. The enhanced Session replaces the default implementation of the Servlet container, and its read and write can use a centralized storage, such as Redis, which makes it convenient for multiple instances in the cluster to share the Session.
HttpServletRequestWrapper / ServletRequestWrapper
In the javax.servlet.http
package, there is a HttpServletRequestWrapper
class [source code ], inherited from ServletRequestWrapper
class [source code ]. You can see the comments on these two classes:
This class implements the Wrapper or Decorator pattern. Methods default to calling through to the wrapped request object.
Translation: This class implements the decorator pattern/wrapper pattern, and the method pattern directly calls the inner packaged request object.
ServletRequestWrapper
itself implements the ServletRequest
interface, and its construction method requires another ServletRequest
object to be passed in, and this object is assigned to the internal request object:
public class ServletRequestWrapper implements ServletRequest {
private ServletRequest request;
public ServletRequestWrapper(ServletRequest request) {
if (request == null) {
throw new IllegalArgumentException("Request cannot be null");
}
this.request = request;
}
// ...
}
ServletRequestWrapper
of the ServletRequest
interface method by 0616f9b5b7d019 is to directly call the method corresponding to the internal request object:
public String getContentType() {
return this.request.getContentType();
}
public ServletInputStream getInputStream() throws IOException {
return this.request.getInputStream();
}
public String getParameter(String name) {
return this.request.getParameter(name);
}
// ...
The above is one of the most basic decorators. We can directly take the matryoshka:
HttpServletRequest request = ...; // 已有的 request 对象
HttpServletRequest requestWrapper = new HttpServletRequestWrapper(request); // 包装后的对象
Of course, the above code does not make any sense, because requestWrapper
does not make any extensions, there is no difference between requestWrapper
object and directly using the request
The real decorator class will inherit ServletRequestWrapper
and make enhancements on this basis.
Next, let's look at how Spring Security and Spring Session HttpServletRequest
object.
Decorator implementation in Spring Security / Spring Session
In Spring Security document Servlet API Integration , you can see Spring Security framework HttpServletRequest
object getRemoteUser()
, getUserPrincipal()
, isUserInRole(String)
and other methods have been enhanced, for example getRemoteUser()
method can return the user name of the currently logged in user. Let's take a look at how Spring Security enhances these methods.
First, Spring Security provides a filter SecurityContextHolderAwareRequestFilter
to filter related requests. In SecurityContextHolderAwareRequestFilter
line 149 combined HttpServlet3RequestFactory
163 line can see, this Filter to create a new Servlet3SecurityContextHolderAwareRequestWrapper
objects, this class inherits from HttpServletRequestWrapper
class, and enhanced correlation methods. The parent class SecurityContextHolderAwareRequestWrapper
class [source code ] can be seen in the enhancement getRemoteUser()
public class SecurityContextHolderAwareRequestWrapper extends HttpServletRequestWrapper {
@Override
public String getRemoteUser() {
Authentication auth = getAuthentication();
if ((auth == null) || (auth.getPrincipal() == null)) {
return null;
}
if (auth.getPrincipal() instanceof UserDetails) {
return ((UserDetails) auth.getPrincipal()).getUsername();
}
if (auth instanceof AbstractAuthenticationToken) {
return auth.getName();
}
return auth.getPrincipal().toString();
}
// ...
}
Simply put, Spring Security filters related requests through a Filter and gets the original HttpServletRequest
object. Through a HttpServletRequestWrapper
inherited from the 0616f9b5b7d16b class, it enhances the getRemoteUser()
and other related methods, and then passes the enhanced object to subsequent business processing. HttpServletRequest
object we get in the Controller layer can be directly used getRemoteUser()
and other methods.
Spring Security and Spring Session achieve similar description will not be repeated here, are interested can look SessionRepositoryFilter
source .
Decorators in Collections
The decorator mode can not only enhance the function of the decorator, but also disable certain functions. Of course, disabling is actually a kind of "enhancement."
For example, suppose there is a List, when we need to pass this List to a third-party method to read, but because this third-party method is not trusted, in order to prevent this method from tampering with the List, we can disable the modification of the List through the decorator mode Method, decorated as a read-only List.
java.util.Collections
provides a static method unmodifiableList(List)
, which is used to encapsulate a List as a read-only List:
List<String> list = ...;
List<String> unmodifiableList = Collections.unmodifiableList(list);
As you can see from the source code of this method, the Collections.unmodifiableList(List)
method actually returns a UnmodifiableList
. UnmodifiableList
is a typical decorator. The read-related method of List directly calls the corresponding method of the decorated object, and the write-related methods are restricted, throwing UnsupportedOperationException
. The following is part of the source code UnmodifiableList
static class UnmodifiableList<E> extends UnmodifiableCollection<E>
implements List<E> {
final List<? extends E> list;
UnmodifiableList(List<? extends E> list) {
super(list);
this.list = list;
}
public E get(int index) {
return list.get(index);
}
public E set(int index, E element) {
throw new UnsupportedOperationException();
}
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
public E remove(int index) {
throw new UnsupportedOperationException();
}
public int indexOf(Object o) {
return list.indexOf(o);
}
public int lastIndexOf(Object o) {
return list.lastIndexOf(o);
}
public boolean addAll(int index, Collection<? extends E> c) {
throw new UnsupportedOperationException();
}
// ...
}
A series of other java.util.Collections
unmodifiableSet(Set)
,unmodifiableMap(Map)
and other methodsunmodifiableList(List)
similar to 0616f9b5b7d27e, used for decoration of different types of collectionssynchronizedList(List)
,synchronizedSet(Set)
,synchronizedMap(Map)
and other methods usesynchronized
decorate the related methods in List, Set, Map, and return a thread-safe collectioncheckedList(List, Class)
such as 0616f9b5b7d2d3,checkedSet(Set, Class)
,checkedMap(List, Class, Class)
and other methods return type-safe collections. If the type of elements inserted into the collection does not meet the requirements, an exception will be thrown
InputStream decorator
The decorator can not only enhance the original method of the decorator, but also add new methods to extend the function.
In java.io
package for InputStream
has a base abstraction decorator FilterInputStream
, its source code is as follows:
public class FilterInputStream extends InputStream {
protected volatile InputStream in;
protected FilterInputStream(InputStream in) {
this.in = in;
}
public int read() throws IOException {
return in.read();
}
// ...
}
Similar to the HttpServletRequestWrapper
class FilterInputStream
is a basic decorator, and its subclasses are the realization of specific decorators. DataInputStream
is one of the typical decorator implementations.
DataInputStream
used to read the basic data types from the decorated InputStream
FilterInputStream
and adds new methods, such as readByte()
, readInt()
, readFloat()
, and InputStream
.
In addition to DataInputStream
, the common subcategory decorators of FilterInputStream
BufferedInputStream
provides buffer function for decoratedInputStream
mark
andreset
methodsCipherInputStream
uses an encryption algorithm (such as AES) to encrypt or decrypt the dataInputStream
DeflaterInputStream
,InflaterInputStream
use the deflate compression algorithm to compress or decompress the dataInputStream
Decorator pattern structure
Image source: https://refactoringguru.cn/design-patterns/decorator
The following summarizes the corresponding relationship between each class and the above figure in the previous example:
- Component corresponds to
HttpServletRequest
,List
,InputStream
- Base Decorator corresponds to
HttpServletRequestWrapper
,FilterInputStream
; - Concrete Decorators correspond to
Servlet3SecurityContextHolderAwareRequestWrapper
,UnmodifiableList
,DataInputStream
Follow my public account
WeChat search Java on the road Follow me
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。