This is the second part of the Spring interview questions series. The topic of this article is: Design patterns involved in Spring, and how to answer the questions in an interview as comprehensively, accurately and deeply as possible.

This post will answer only one question:

What design patterns are used in Spring? How are they achieved?

As a whole, there are 11 design patterns used in the SpringFramework:

  • Singleton pattern + prototype pattern
  • The factory pattern
  • The proxy pattern
  • The strategy pattern
  • Template method pattern
  • Observer mode
  • Adapter mode
  • Decorator mode
  • The appearance model
  • Delegate mode (not part of GoF23)

Of course, if you just say something like this, what will the interviewer think? It can’t be memorizing the answers! So not only do you need to know what you’re using, but you also need to know how and where you’re using it, so that you can conquer the interviewer with your true skill set.

Below, I will introduce the design scenarios and principles of the 11 design patterns in detail.

Since all 11 design patterns are too long to cover, they are divided into two columns.

Singleton pattern + prototype pattern

The SpringFramework IOC container contains many, many beans. By default, the Scope of beans is singleton. If the scope is explicitly declared as Prototype, then the scope of the Bean will be changed to a new, prototype Bean every time it is fetched. This knowledge should have been known at the most basic stage of the SpringFramework, so I won’t go into any more detail. The point is that if I define a Bean that declares prototype, the SpringFramework knows I want to prototype beans. But when I define the Bean without declaring Scope, why does it default to singleton?

Let’s start with the most familiar Bean registration scenario. (The prototype pattern is relatively simple, and the content is interspersed with an explanation of the singleton pattern)

The Bean’s registered

Register beans in XML mode
<bean class="com.example.demo.bean.Person" scope="singleton"/>
Copy the code

If scope=”singleton” is explicitly declared, the IDE will report yellow (warning) :

Obviously it tells you that the default value is Singleton, so there’s no need to declare it. This tip is intelligently recognized by IDEA, so it is difficult for me to find the source, but I can click on the scope and have a look at the comments in XSD:

<xsd:attribute name="scope" type="xsd:string">
    <xsd:annotation>
        <xsd:documentation><! [CDATA[ The scope of this bean: typically "singleton" (one shared instance, which will be returned by all calls to getBean with the given id), ......Copy the code

Obviously the first line of the documentation comment says that it is usually singleton’s.

Annotations drive registration beans

The annotation-driven approach uses an @scope annotation to declare the Scope:

@Scope
@Component
public class Person {}Copy the code

Open the @scope annotation to see the source code, and you can see that the @scope annotation only declares the Scope, and the default value is an empty string (not a singleton) :

public @interface Scope {

	@AliasFor("scopeName")
	String value(a) default "";
Copy the code

It declares an empty string, but in XML I’m configuring a Singleton. Why is that different? Here’s why.

The default scope annotation

Those of you who have some in-depth knowledge of the SpringFramework should realize what I’m going to say next: BeanDefinition. BeanDefinition: BeanDefinition: BeanDefinition: BeanDefinition: BeanDefinition: BeanDefinition: BeanDefinition: BeanDefinition: BeanDefinition: BeanDefinition: BeanDefinition: BeanDefinition

public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
		implements BeanDefinition.Cloneable {

	public static final String SCOPE_DEFAULT = "";
Copy the code

It is declared that the default scope is an empty string, not a Singleton.

If singleton is an empty string, you can use singleton as an empty string. Shouldn’t a single instance Bean be judged by whether the scope is singleton?

BeanDefinition: Getscope = BeanDefinition: getscope = BeanDefinition: getscope = BeanDefinition: getscope = BeanDefinition: getscope = BeanDefinition: getscope

public String getScope(a) {
    return this.scope;
}
Copy the code

The way to get the scope is very simple, and it’s nothing to see. But!! Notice that continuing down, followed by a method called isSingleton:

/**
 * Return whether this a <b>Singleton</b>, with a single shared instance
 * returned from all calls.
 * @see #SCOPE_SINGLETON
 */
@Override
public boolean isSingleton(a) {
    return SCOPE_SINGLETON.equals(this.scope) || SCOPE_DEFAULT.equals(this.scope);
}
Copy the code

If you look at this judgment, it is divided into two parts: whether ** is a Singleton, or whether it is an empty string! }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}

Bean instantiation

Beans are singleton by default. How does the SpringFramework know if these beans are singleton when the IOC container initializes them? Let’s take a look at the underlying initialization logic.

This section is a cursory introduction to the Bean initialization process. For detailed parsing, see chapter 14 of my SpringBoot source code booklet.

In AbstractBeanFactory, the getBean method calls doGetBean. This method is very long, so I just cut out the box here:

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
        @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
    final String beanName = transformedBeanName(name);
    Object bean;

    // Eagerly check singleton cache for manually registered singletons.
    // Start by checking whether the singleton cache pool already has the corresponding bean
    Object sharedInstance = getSingleton(beanName);
    / /...
    else {
        / /...
        try {
            final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            // 检查 ......

            // Create bean instance.
            if (mbd.isSingleton()) {
                // Single instance Bean
                sharedInstance = getSingleton(beanName, () -> {
                    try {
                        return createBean(beanName, mbd, args);
                    } // catch ......
                });
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }

            else if (mbd.isPrototype()) {
                / / prototype Bean
                // It's a prototype -> create a new instance.
                Object prototypeInstance = null;
                try {
                    beforePrototypeCreation(beanName);
                    // Must create an entirely new object
                    prototypeInstance = createBean(beanName, mbd, args);
                }
                finally {
                    afterPrototypeCreation(beanName);
                }
                bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            }

            else {
                // Customize scope
                String scopeName = mbd.getScope();
                final Scope scope = this.scopes.get(scopeName);
                / /...}}// catch ......
    }
    / /...
    return (T) bean;
}
Copy the code

Reading through the box process, it starts by checking to see if there are any beans in the singleton’s cache pool and goes no further. In the try part of the else block, it will fetch the BeanDefinition of the current Bean to determine the scope: If it is a singleton, then the getSingleton method is executed to create the singleton (the createBean method in the underlying lambda expression). If it is a Prototype Bean, the prototype Bean creation process is executed. If none of these is true, then it can be considered a custom scope, using a special initialization process.

So the core method for creating a single instance Bean is getSingleton. Let’s take a look at it.

private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

public Object getSingleton(String beanName, ObjectFactory
        singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    synchronized (this.singletonObjects) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            / / check
            try {
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            } // catch finally ......
            if(newSingleton) { addSingleton(beanName, singletonObject); }}returnsingletonObject; }}Copy the code

Notice the design: it first checks the singleton cache pool to see if there is a corresponding bean, and if there is not, it creates the bean. After creation, it also puts the bean into the cache pool so that it will not be created again when fetched later.

summary

So the core logic here can be summarized:

The SpringFramework implements the singleton pattern inBeanDefinitionIn the IOC container initialization phase, the Bean is created and placed in the singleton cache pool (singletonObjects) to implement a single instance of the Bean.

The factory pattern

When it comes to the factory pattern, the SpringFramework is most easily associated with factorybeans. But this is not the only factory in the SpringFramework. There are many others, as listed below.

FactoryBean

FactoryBean itself is an interface, which is itself a factory for creating objects. If a class implements the FactoryBean interface, it will no longer bea normal Bean object in itself and will not play a role in the actual business logic, but will be played by the created object.

The FactoryBean interface has three methods:

public interface FactoryBean<T> {
    // Return the created object
    @Nullable
    T getObject(a) throws Exception;

    // Return the type of the created object (i.e., generic type)
    @NullableClass<? > getObjectType();// Whether the created object is a single instance Bean or a prototype Bean, the default single instance
    default boolean isSingleton(a) {
        return true; }}Copy the code

Static factory

This approach is similar to the core factory we saw when we first learned the simple factory model, like this:

public class CalculatorFactory {
    // Simple factory
    public static Calculator getCalculator(String operationType) {
        switch (operationType) {
            case "+": 
                return new AddCalculator();
            case "-":
                return new SubtractCalculator();
            default: 
                return null; }}// Static factory
    public static Calculator getAddCalculator(a) {
        return newAddCalculator(); }}Copy the code

Using a static factory in the SpringFramework, there is no such thing as a parameter. You only need to declare a factory class and a method (so I have an extra method in the factory above) :

<bean id="addCalculator" class="com.example.demo.bean.CalculatorFactory" factory-method="getAddCalculator"/>
Copy the code

The bean you get after this registration is of type AddCalculator.

Instance factory

Instance factories are used much like static factories, except that the static factory itself is not registered into the IOC container, but the instance factory is registered with the IOC container.

By tweaking the code above, we can implement Bean registration for the instance factory:

public class CalculatorFactory {
    // Factory method
    public Calculator getAddCalculator(a) {
        return newAddCalculator(); }}Copy the code
<bean id="calculatorFactory" class="com.example.demo.bean.CalculatorFactory"/>
<bean id="addCalculator" factory-bean="calculatorFactory" factory-method="getAddCalculator"/>
Copy the code

ObjectFactory

This may seem a little strange to some of you, so I put it down to the end.

@FunctionalInterface
public interface ObjectFactory<T> {
	T getObject(a) throws BeansException;
}
Copy the code

The structure is simpler than FactoryBean, and it can be understood simply as FactoryBean, but it’s different. The ObjectFactory is usually injected into other beans as a Bean. When the corresponding Bean needs to be used, the ObjectFactory’s getObject method is invoked to get the actual Bean. FactoryBean’s getObject method is called when the SpringFramework initializes the Bean, so the timing is different.

The second argument in getSingleton’s two-argument method is the ObjectFactory type, which is used to call createBean to create a singleton.

summary

The factory pattern in the SpringFramework includes built-inFactoryBeanObjectFactory, and custom declared static factories, instance factories.

The proxy pattern

As we all know, SpringFramework two core IOC, AOP, AOP is embodied in the use of proxy pattern. But if you just say that AOP embodies the proxy pattern, that’s too bad. You need more answers to make the interviewer realize that you’ve really done your research and you really know it!

The underlying implementation of AOP

SpringFramework AOP enhancement to generate a proxy object for Bean in the core is a BeanPostProcessor: AnnotationAwareAspectJAutoProxyCreator, this name is very long, but it’s easy to remember:

  • The Annotation is not an Annotation.
  • Aware: injection
  • AspectJ: AspectJ-based AOP
  • AutoProxy: Automatic proxy
  • Creator: Creator

Does this split feel much easier to understand?

Its role as the core method is the parent class AbstractAutoProxyCreator postProcessAfterInitialization method, the bottom is called wrapIfNessary method creates proxy objects:

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if(bean ! =null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) ! = bean) {// Create an AOP proxy object
            returnwrapIfNecessary(bean, beanName, cacheKey); }}return bean;
}
Copy the code

As for the bottom, this is a big trouble, here briefly summarized, detailed proxy object creation can refer to The SpringBoot source booklet chapter 19 to learn.

Beans enhanced by AOP are treated during the initialization phase (when the object is created)AnnotationAwareAspectJAutoProxyCreatorProcess, integrate the aspects that the Bean may be covered to, and finally use JDK dynamic proxy or Cglib dynamic proxy to build and generate proxy objects according to whether the Bean has interface implementation.

Creation of a proxy object

The final dynamic proxy creation is mentioned in the summary above, but here’s a look at the source code for creating proxy objects at the lowest level that you’re familiar with.

In the JdkDynamicAopProxy, there is a getProxy method. The basic implementation is as follows:

public Object getProxy(@Nullable ClassLoader classLoader) {
    if (logger.isTraceEnabled()) {
        logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource()); } Class<? >[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    // JDK native methods
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
Copy the code

Look at the last sentence, is not suddenly familiar! This is a good place to brag about it during the interview, and the interviewer may actually think you have it all figured out.

Additional dynamic proxy created, create a proxy object in CglibAopProxy createProxyClassAndInstance method of implementation:

protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
    enhancer.setInterceptDuringConstruction(false);
    enhancer.setCallbacks(callbacks);
    return (this.constructorArgs ! =null && this.constructorArgTypes ! =null ?
            enhancer.create(this.constructorArgTypes, this.constructorArgs) :
            enhancer.create());
}
Copy the code

Look at the Enhancer#create() method here. So we know that the framework is just layered on top of what we’ve learned, and the bottom layer is still the same.

summary

The proxy pattern in the SpringFramework is embodied in AOP, which uses the post-processor to integrate the logic of the aspect (reinforcer Advice) to enhance the existing Bean (Target object) to proxy Bean using JDK or Cglib dynamic proxy.

The strategy pattern

Speaking of the policy pattern implemented in SpringFramework, actually mentioned just now: when generating proxy object, AOP will decide to use JDK dynamic proxy or Cglib dynamic proxy according to whether the original Bean has an interface implementation, this is a typical policy pattern embodiment.

In DefaultAopProxyFactory, there is a strategy mode:

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    if(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class<? > targetClass = config.getTargetClass();if (targetClass == null) {
            throw new AopConfigException("TargetSource cannot determine target class: " +
                    "Either an interface or a target is required for proxy creation.");
        }
        // Policy judgment
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        return newJdkDynamicAopProxy(config); }}Copy the code

The middle one determines whether the target object currently being propped is of type an interface or whether the target object is a proxy class. If it is one of the two, you can use the JDK’s dynamic proxy directly; otherwise, the Cglib proxy is used.

Space limit, the rest of the 6 design patterns will be reflected in the next introduction ~ friends remember to focus on the like ah, have the source code learning needs can see my booklet ~ ollie to 】