This article is excerpted from Spring 5 Core Principles

1 Annotation (custom configuration) module

The code implementation of the Annotation we’re going to stick with the Mini version, keep it the same, just copy it over.

1.1 @ GPService

The @gpService code is as follows:


package com.tom.spring.formework.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/** * business logic, injection interface */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPService {

   String value(a) default "";
	 
}

Copy the code

1.2 @ GPAutowired

The @gpAutowired code is as follows:


package com.tom.spring.formework.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/** * automatic injection */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPAutowired {

   String value(a) default "";
	 
}

Copy the code

1.3 @ GPController

The @gpController code is as follows:


package com.tom.spring.formework.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/** * page interaction */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPController {

   String value(a) default "";
	 
}

Copy the code

1.4 @ GPRequestMapping

The @gprequestMapping code is as follows:


package com.tom.spring.formework.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/** * request URL */
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPRequestMapping {

   String value(a) default "";
	 
}

Copy the code

1.5 @ GPRequestParam

The @gprequestParam code is as follows:


package com.tom.spring.formework.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/** * Request parameter mapping */
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPRequestParam {

   String value(a) default "";
	 
}

Copy the code

2 Core (top-level interface) module

2.1 GPFactoryBean

In terms of top-level interface design, we have learned the basic functions of FactoryBean, so we won’t go into too much explanation here.


package com.tom.spring.formework.core;

public interface GPFactoryBean {}Copy the code

2.2 GPBeanFactory

As the top-level design for all IoC containers, the role of the BeanFactory has also been described in detail.


package com.tom.spring.formework.core;

/** * Top level design of singleton factory */
public interface GPBeanFactory {

    /** * Get an instance Bean * from the IoC container according to beanName@param beanName
     * @return* /
    Object getBean(String beanName) throws Exception;

    public Object getBean(Class
        beanClass) throws Exception;

}

Copy the code

3 Beans (Configuration encapsulation) module

3.1 GPBeanDefinition

BeanDefinition is mainly used to hold bean-related configuration information.


package com.tom.spring.formework.beans.config;

// It is used to store information in the configuration file
// This corresponds to the configuration saved in memory
public class GPBeanDefinition {

    private String beanClassName;  // The full class name of the native Bean
    private boolean lazyInit = false; // flag whether the load is delayed
    private String factoryBeanName;  // Save the beanName, the key stored in the IoC container
		
    public String getBeanClassName(a) {
        return beanClassName;
    }
		
    public void setBeanClassName(String beanClassName) {
        this.beanClassName = beanClassName;
    }
		
    public boolean isLazyInit(a) {
        return lazyInit;
    }
		
    public void setLazyInit(boolean lazyInit) {
        this.lazyInit = lazyInit;
    }
		
    public String getFactoryBeanName(a) {
        return factoryBeanName;
    }
		
    public void setFactoryBeanName(String factoryBeanName) {
        this.factoryBeanName = factoryBeanName; }}Copy the code

3.2 GPBeanWrapper

BeanWrapper is used to encapsulate created Object instances. Proxy objects or Original objects are stored in BeanWrapper.


package com.tom.spring.formework.beans;

public class GPBeanWrapper {

    private Object wrappedInstance;
    privateClass<? > wrappedClass;public GPBeanWrapper(Object wrappedInstance){
        this.wrappedInstance = wrappedInstance;
    }

    public Object getWrappedInstance(a){
        return this.wrappedInstance;
    }

    // return the Class after the proxy
    // It could be $Proxy0
    publicClass<? > getWrappedClass(){return this.wrappedInstance.getClass(); }}Copy the code

4 Context (IoC container) module

4.1 GPAbstractApplicationContext

The top-level abstract class of the IoC container implementation class implements the IoC container-related common logic. To keep things as simple as possible, only a refresh() method has been designed for the time being in this Mini version.


package com.tom.spring.formework.context.support;

/** * Top-level design of IoC container implementation */
public abstract class GPAbstractApplicationContext {

    // Protected only for subclass overrides
    public void refresh(a) throws Exception {}}Copy the code

4.2 GPDefaultListableBeanFactory

DefaultListableBeanFactory is typical representative of the IoC container subclasses. In the Mini version, I only made a simple design, which is to define the top-level IoC cache, i.e. a Map. The attribute name is also the same as the original Spring attribute name, which is defined as beanDefinitionMap, for easy comparison and understanding.


package com.tom.spring.formework.beans.support;

import com.tom.spring.formework.beans.config.GPBeanDefinition;
import com.tom.spring.formework.context.support.GPAbstractApplicationContext;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class GPDefaultListableBeanFactory extends GPAbstractApplicationContext{

    // The BeanDefinition that stores the registration information
    protected final Map<String, GPBeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, GPBeanDefinition>();
		
}

Copy the code

4.3 GPApplicationContext

ApplicationContext is direct contact with the user’s entry, mainly realizes the DefaultListableBeanFactory refresh () method and the BeanFactory getBean () method, complete the IoC, DI and AOP’s cohesion.


package com.tom.spring.formework.context;

import com.tom.spring.formework.annotation.GPAutowired;
import com.tom.spring.formework.annotation.GPController;
import com.tom.spring.formework.annotation.GPService;
import com.tom.spring.formework.beans.GPBeanWrapper;
import com.tom.spring.formework.beans.config.GPBeanPostProcessor;
import com.tom.spring.formework.core.GPBeanFactory;
import com.tom.spring.formework.beans.config.GPBeanDefinition;
import com.tom.spring.formework.beans.support.GPBeanDefinitionReader;
import com.tom.spring.formework.beans.support.GPDefaultListableBeanFactory;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

IoC, DI, MVC, AOP */
public class GPApplicationContext extends GPDefaultListableBeanFactory implements GPBeanFactory {

    private String [] configLoactions;
    private GPBeanDefinitionReader reader;

    // Singleton IoC container cache
    private Map<String,Object> factoryBeanObjectCache = new ConcurrentHashMap<String, Object>();
		
    // A generic IoC container
    private Map<String,GPBeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<String, GPBeanWrapper>();

    public GPApplicationContext(String... configLoactions){
		
        this.configLoactions = configLoactions;
				
        try {
            refresh();
        } catch(Exception e) { e.printStackTrace(); }}@Override
    public void refresh(a) throws Exception{
		
        //1. Locate the configuration file
        reader = new GPBeanDefinitionReader(this.configLoactions);

        //2. Load the configuration file, scan the relevant classes, and wrap them as BeanDefinitions
        List<GPBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();

        //3. Register and put the configuration information in the container (pseudo-IOC container)
        doRegisterBeanDefinition(beanDefinitions);

        //4. Preinitialize classes that are not lazy-loaded
        doAutowrited();
				
    }

    // Handle only the case of non-delayed loading
    private void doAutowrited(a) {
		
        for (Map.Entry<String, GPBeanDefinition> beanDefinitionEntry : super.beanDefinitionMap.entrySet()) {
				
           String beanName = beanDefinitionEntry.getKey();
					 
           if(! beanDefinitionEntry.getValue().isLazyInit()) {try {
							 
                   getBean(beanName);
									 
               } catch(Exception e) { e.printStackTrace(); }}}}private void doRegisterBeanDefinition(List<GPBeanDefinition> beanDefinitions) throws Exception {

        for (GPBeanDefinition beanDefinition: beanDefinitions) {
				
            if(super.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())){
                throw new Exception("The “" + beanDefinition.getFactoryBeanName() + "" is the exists!!");
            }
            super.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(),beanDefinition);
						
        }
				
        // At this point, the container is initialized
    }
		
    public Object getBean(Class
        beanClass) throws Exception {
		
        return getBean(beanClass.getName());
				
    }

    // Dependency injection, from here, reads the information in BeanDefinition
    // Then create an instance via reflection and return it
    // Instead of putting out the original object, Spring uses a BeanWrapper
    // Decorator mode:
    //1. Keep the original OOP relationship
    //2. It needs to be extended and enhanced (to lay the foundation for future AOP)
    public Object getBean(String beanName) throws Exception {

        return null;
    }

    public String[] getBeanDefinitionNames() {
		
        return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap. size()]);
				
    }

    public int getBeanDefinitionCount(a){
		
        return this.beanDefinitionMap.size();
				
    }

    public Properties getConfig(a){
		
        return this.reader.getConfig(); }}Copy the code

4.4 GPBeanDefinitionReader

By convention, BeanDefinitionReader mainly interprets the Application.properties configuration file, and the implementation logic is very simple. The constructor gets the locations configuration file path passed from the ApplicationContext, and then parses, scans, and saves all related classes and provides a unified access point.


package com.tom.spring.formework.beans.support;

import com.tom.spring.formework.beans.config.GPBeanDefinition;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

// Find, read, and parse the configuration file
public class GPBeanDefinitionReader {

    private List<String> registyBeanClasses = new ArrayList<String>();

    private Properties config = new Properties();

    // Fix the key in the configuration file, relative to the XML specification
    private final String SCAN_PACKAGE = "scanPackage";

    public GPBeanDefinitionReader(String... locations){
		
        // Find the corresponding file through the URL location, and then convert to the file stream
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(locations[0]. replace("classpath:".""));
        try {
            config.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(null! = is){try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        doScanner(config.getProperty(SCAN_PACKAGE));
				
    }

    private void doScanner(String scanPackage) {
		
        // Convert to file path. Replace with /
        URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll ("\ \."."/"));
        File classPath = new File(url.getFile());
        for (File file : classPath.listFiles()) {
            if(file.isDirectory()){
                doScanner(scanPackage + "." + file.getName());
            }else{
                if(! file.getName().endsWith(".class")) {continue; } String className = (scanPackage +"." + file.getName().replace(".class"."")); registyBeanClasses.add(className); }}}public Properties getConfig(a){
		
        return this.config;
				
    }

    // Convert all configuration information scanned in the configuration file to GPBeanDefinition objects for subsequent IoC operations
    public List<GPBeanDefinition> loadBeanDefinitions(a){
		
        List<GPBeanDefinition> result = new ArrayList<GPBeanDefinition>();
        try {
				
            for(String className : registyBeanClasses) { Class<? > beanClass = Class.forName(className);if(beanClass.isInterface()) { continue; } result.add(doCreateBeanDefinition(toLowerFirstCase(beanClass.getSimpleName()), beanClass.getName())); Class<? > [] interfaces = beanClass.getInterfaces();for(Class<? > i : interfaces) { result.add(doCreateBeanDefinition(i.getName(),beanClass.getName())); }}}catch (Exception e){
            e.printStackTrace();
        }
				
        return result;
				
    }


    // Parse each configuration information into a BeanDefinition
    private GPBeanDefinition doCreateBeanDefinition(String factoryBeanName,String beanClassName){
		
        GPBeanDefinition beanDefinition = new GPBeanDefinition();
        beanDefinition.setBeanClassName(beanClassName);
        beanDefinition.setFactoryBeanName(factoryBeanName);
        return beanDefinition;
				
    }

    // Change the first letter of the class name to lowercase
    // For the sake of simplifying the program logic, I will not make any other judgments
    private String toLowerFirstCase(String simpleName) {
		
        char [] chars = simpleName.toCharArray();
        // Because the ASCII characters are 32 different
        And the ASCII characters for uppercase letters are smaller than the ASCII characters for lowercase letters
        // In Java, doing arithmetic on char is actually doing arithmetic on ASCII
        chars[0] + =32;
        returnString.valueOf(chars); }}Copy the code

4.5 GPApplicationContextAware

The ApplicationContextAware interface (ApplicationContextAware) is used to retrieve the IoC container’s context by implementing a listening mechanism to retrieve a callback method. In this Mini version just did a top-level design, tell you such a phenomenon, and did not do specific implementation. This is not the point of this book, so you can try it out on your own.


package com.tom.spring.formework.context;

/** * The top layer of the IoC container is decoupled * and then all classes are scanned through a listener. Once this interface is implemented, the setApplicationContext() method is automatically called to inject the IoC container into the target class */
public interface GPApplicationContextAware {

    void setApplicationContext(GPApplicationContext applicationContext);
		
}

Copy the code

Pay attention to “Tom play architecture” reply to “Spring” can obtain the complete source code.

This article is “Tom play structure” original, reproduced please indicate the source. Technology is to share, I share my happiness! If you have any suggestions can also leave a comment or private letter, your support is my motivation to adhere to the creation. Pay attention to “Tom bomb architecture” for more technical dry goods!

Original is not easy, adhere to very cool, see here, small partners remember to like, collect, look, one key three even add attention! If you feel the content is too dry, you can share and forward to a friend moist moist!