preface

The other day, a reader asked Ali what design patterns the Spring framework uses. , the answer is not very good, so I plan to write an article about this!

  • The article was first published on an official account (Yuebanfeiyu), and then synchronized to the personal website: xiaoflyfish.cn/
  • Experience: One and a half years of experience sharing (including Ali Meituan Toutiao and Jd Didi)

Wechat search: month with flying fish, make a friend, into the interview exchange group

  • The public account responds to 666 backstage, you can get free electronic books

Feel good, hope to like, look, forward to support, thank you

The proxy pattern

Alleged agent, it is to point to it and be a proxy object implements the same interface, the client must through agent can interact with the proxy target class, and the agent is in the process of interaction commonly (interactive) before and after, certain processing, such as pre-processing before the call this method, call this method after the post processing.

Proxy is divided into static proxy and dynamic proxy two ways, Spring AOP adopts the dynamic proxy way

Spring implements AOP by dynamically generating the proxy class of the target object and setting interceptors in the method of the proxy class. It enhances the function of the proxy method by executing the logic in the interceptor.

About dynamic proxy can see my previous article, write very detailed: dynamic proxy summary, you want to know all here, no nonsense!

The strategy pattern

As we mentioned earlier, Spring AOP is implemented through dynamic proxies.

In terms of code implementation, Spring supports two dynamic proxy implementations, one provided by the JDK and the other provided by Cglib.

Spring dynamically selects different dynamic proxy implementations at run time. This application scenario is actually a typical application scenario of the policy pattern.

We only need to define a policy interface that can be implemented by different policy classes. Corresponding to Spring source code, AopProxy is the policy interface, JdkDynamicAopProxy, CglibAopProxy are two policy classes that realize the AopProxy interface.

The AopProxy interface is defined as follows:

In the policy pattern, the creation of policies is generally implemented through the factory method. Corresponding to Spring source code, AopProxyFactory is a factory class interface, and DefaultAopProxyFactory is a default factory class used to create AopProxy objects.

The source code is as follows:

The typical application scenario of the policy mode is to dynamically determine which policy to use based on environment variables, state values, and calculation results.

Corresponding to the Spring source code, we can see the code implementation of the createAopProxy() function in the DefaultAopProxyFactory class just given.

Line 10 is the judgment condition for dynamically selecting which policy to use.

Decorator mode

As we know, caching is generally used in conjunction with databases. If the write cache succeeds but the database transaction is rolled back, there will be dirty data in the cache.

To solve this problem, we need to put cached writes and database writes into the same transaction, and either succeed or fail.

To do this, Spring uses the decorator pattern.

TransactionAwareCacheDecorator increased the support for the transaction, the transaction commit and rollback of the Cache data.

TransactionAwareCacheDecorator implemented Cache interface, and entrust all operating targetCache, the transaction function is added to the write operation of them. This is the typical application scenario and code implementation of the decorator pattern.

The singleton pattern

In the singleton mode, only one instance of a class can be generated during the entire system running

In Spring, beans can be defined in two patterns: Prototype (multiple instances) and Singleton (Singleton). Spring beans are Singleton by default.

So how does Spring implement the singleton pattern?

The answer is through a singleton registry, specifically using HashMap. The simplified code is as follows:

public class DefaultSingletonBeanRegistry {
    
    // The thread-safe container ConcurrentHashMap is used to store various singleton objects
    private final Map singletonObjects = new ConcurrentHashMap;

    protected Object getSingleton(String beanName) {
    // Get the Object from the HashMap
    Object singletonObject = singletonObjects.get(beanName);
    
    // If not, create an object instance by reflection and add it to the HashMap
    if (singletonObject == null) {
      singletonObjects.put(beanName,
                           Class.forName(beanName).newInstance());
   }
   
   // Return an object instance
   returnsingletonObjects.get(beanName); }}Copy the code

The above code is logically clear: go to the HashMap and get the singleton, then create one to add to the HashMap.

Simple Factory model

Here’s a scenario:

When an object A needs to call A method of an object B, we need to create A new instance of B in A. Its disadvantage is that once the requirements change, such as the need to use class C to replace B, we need to rewrite the method of class A.

If you have 100 classes in your application that are coupled to B in a certain way, it’s hard to change.

Using the simple Factory pattern:

The simple factory pattern, also known as the static factory approach, is essentially a factory class that dynamically decides which product class to create based on incoming parameters.

BeanFactory in Spring is the embodiment of the simple factory pattern. BeanFactory is a core interface in Spring IOC container, which is defined as follows:

We can through its specific implementation class (such as ClassPathXmlApplicationContext) to obtain a Bean:

BeanFactory bf = new ClassPathXmlApplicationContext("spring.xml");
FlyFish flyFishBean = (FlyFish) bf.getBean("flyfishBean");
Copy the code

As you can see from the code above, the consumer doesn’t need to come up with a new object herself, but instead gets the object instance through the factory class method getBean. This is a typical simple factory pattern, except that Spring uses reflection to create beans.

Factory method pattern

In a simple factory, the factory class does all the logical judgment and instance creation. If you do not want to judge in the factory class, you can provide different factories for different products, different factories produce different products, and each factory only corresponds to a corresponding object, which is the factory method pattern.

FactoryBean in Spring is the embodiment of this idea. FactoryBean can be understood as FactoryBean. Let’s take a look at its definition:

We define a class FlyFishFactoryBean to implement the FactoryBean interface, essentially new a FlyFish object in the getObject method. So we get an instance of the FlyFish generated by the factory via getBean(ID), not an instance of the FlyFishFactoryBean itself, like the following:

BeanFactory bf = new ClassPathXmlApplicationContext("spring.xml");
FlyFish flyFishBean = (FlyFish) bf.getBean("flyfishBean");
Copy the code

Observer model

The observer pattern implemented in Spring consists of three parts: Event (equivalent to a message), Listener (equivalent to an observer), Publisher sender (equivalent to an observed)

Let’s take a look at an example of how the Spring observer pattern is used

/ / the Event Event
public class DemoEvent extends ApplicationEvent {
  private String message;

  public DemoEvent(Object source, String message) {
    super(source);
  }

  public String getMessage(a) {
    return this.message; }}// Listener Listener
@Component
public class DemoListener implements ApplicationListener {
  @Override
  public void onApplicationEvent(DemoEvent demoEvent) { String message = demoEvent.getMessage(); System.out.println(message); }}// Publisher sender
@Component
public class DemoPublisher {
  @Autowired
  private ApplicationContext applicationContext;

  public void publishEvent(DemoEvent demoEvent) {
    this.applicationContext.publishEvent(demoEvent); }}Copy the code

From the code, we can see that there are three main parts of the work:

  • Define an event (DemoEvent) that inherits ApplicationEvent;
  • Define an ApplicationListener (DemoListener).
  • Define a sender (DemoPublisher) that calls ApplicationContext to send event messages.

Where is the observer registered in Spring’s implementation? How do you register?

Spring registers the observer with the ApplicationContext object.

In fact, specific to the source code, the ApplicationContext just one interface, specific code implementation contains in AbstractApplicationContext its implementation classes. I put the code associated with observer mode as follows. You just need to focus on how it sends events and registers listeners.

From the above code, we found that the real message is sent, actually is done through ApplicationEventMulticaster this class.

The source code for the following class, which I have only extracted the most important part of, is the multicastEvent() message sending function, which supports both asynchronous non-blocking and synchronous blocking observer modes through the thread pool.

With the skeleton code of the Spring-provided observer pattern, if we want to implement the sending and listening of an event in Spring, we only need to define the event, define the listener, and send the event to the ApplicationContext. The Spring framework does the rest.

In fact, this shows the extensibility of the Spring framework, which allows you to extend new events and listeners without having to change any code.

Template pattern

Here’s a question we’re often asked in job interviews:

Describe the major steps involved in the Spring Bean creation process.

This involves the template pattern. It also demonstrates the extensibility of Spring. Using the template pattern, Spring lets users customize the Bean creation process.

Here’s a picture of the entire life cycle of a Spring Bean, clear and simple:

For more details, check out the previous article: The use of Spring’s clever extension points

If you look closely at the source code, you’ll see that the template pattern is actually implemented not in the standard abstract class way, but rather in a somewhat similar way to the Callback, where the function to be executed is wrapped as an object (for example, the initialization method is wrapped as an InitializingBean object). Pass to the template (BeanFactory) for execution.

Observer and template patterns, which help us create extension points that allow users of the framework to customize framework functionality without modifying the source code.

Adapter mode

In Spring MVC, the most common way to define a Controller is to use the @Controller annotation to indicate that a class is a Controller class, and the @requesMapping annotation to indicate the URL of the function

However, we can also define a Controller by having the class implement the Controller interface or the Servlet interface.

I have written three examples of code for each of these definitions, as follows:

// Method 1: use @controller and @requestMapping
@Controller
public class DemoController {
    @RequestMapping("/FlyFish")
    public ModelAndView getEmployeeName(a) {
        ModelAndView model = new ModelAndView("FlyFish");        
        model.addObject("message"."FlyFish");       
        returnmodel; }}// Method 2: Implement Controller interface + XML configuration file: configure the mapping between DemoController and URL
public class DemoController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        ModelAndView model = new ModelAndView("FlyFish");
        model.addObject("message"."FlyFish");
        returnmodel; }}// Method 3: implement Servlet interface + XML configuration file: configure the mapping between DemoController class and URL
public class DemoServlet extends HttpServlet {
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    this.doPost(req, resp);
  }
  
  @Override
  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    resp.getWriter().write("Hello World."); }}Copy the code

When the application starts, the Spring container loads these Controller classes and parses the URL handlers into Handler objects and stores them in the HandlerMapping object. When a request comes in, the DispatcherServlet searches for the Handler corresponding to the request URL from the HanderMapping, calls the Handler corresponding to the function code, and returns the execution result to the client.

However, the functions of controllers defined in different ways (function names, input parameters, return values, etc.) are not uniform.

The DispatcherServlet calls the service() method. The DispatcherServlet calls different functions depending on the type of Controller.

Spring utilizes the adapter pattern, where we adapt functions in the Controller class that are defined in different ways to a unified function definition.

Let’s look at the code implementation of Spring in detail.

Spring defines a uniform interface HandlerAdapter and an adapter class for each Controller.

These adapter class include: AnnotationMethodHandlerAdapter, SimpleControllerHandlerAdapter, SimpleServletHandlerAdapter etc.

In the DispatcherServlet class, we don’t need to treat different Controller objects differently. We just call the Handle () function of the HandlerAdapter

The last

Feel there is harvest, hope to help like, forward ha, thank you, thank you

Month with flying fish, make a friend

The public account responds to 666 backstage, you can get free electronic books