头图

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:

HttpServletRequest 对象

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 methods unmodifiableList(List) similar to 0616f9b5b7d27e, used for decoration of different types of collections
  • synchronizedList(List) , synchronizedSet(Set) , synchronizedMap(Map) and other methods use synchronized decorate the related methods in List, Set, Map, and return a thread-safe collection
  • checkedList(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 decorated InputStream mark and reset methods
  • CipherInputStream uses an encryption algorithm (such as AES) to encrypt or decrypt the data InputStream
  • DeflaterInputStream , InflaterInputStream use the deflate compression algorithm to compress or decompress the data InputStream

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


叉叉哥
3.8k 声望60 粉丝