Pure handwriting implementation of Spring source code framework

Posted by vaavi8r on Tue, 08 Feb 2022 23:25:57 +0100

In the previous article, we completed the loading object defined by Bean. This time, we completed the scope to obtain the value value, write the metadata and set the BeanName

scope system

Define scope source data class ScopeMetadata

/**
 * Define scope source data class
 */
public class ScopeMetadata {
    //Define the default values for the scope
    private String scopeName = BeanDefinition.SCOPE_SINGLETON;

    public String getScopeName() {
        return scopeName;
    }

    public void setScopeName(String scopeName) {
        this.scopeName = scopeName;
    }
}

Define ScopeMetadata metadata injection interface ScopeMetadataResolver

/**
 *ScopeMetadata Metadata injection interface
 */
public interface ScopeMetadataResolver {
    ScopeMetadata resolverScopeMetadata(BeanDefinition beanDefinition);
}

Define ScopeMetadata metadata injection concrete class AnnotationScopeMetadataResolver

/**
 * Implement scope metadata and define specific classes
 */
public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver {
    private Class<? extends Annotation> scopeType = Scope.class;
    @Override
    public ScopeMetadata resolverScopeMetadata(BeanDefinition beanDefinition) {
        ScopeMetadata scopeMetadata = new ScopeMetadata();
        // Gets the Class object processed by the current beanDefinition
        Class<?> beanClass = beanDefinition.getbeanClass();
        // Judge whether there is @ ZXScopez annotation on the class object
        if (beanClass.isAnnotationPresent(scopeType)) {
            // Get ZXScope annotation object
            Scope annotation = ((Scope) beanClass.getAnnotation(scopeType));
            // Get the value of the primary key setting
            String value = annotation.value();
            if (!"".equals(value)) {
                scopeMetadata.setScopeName(value);
            }
        }
        return scopeMetadata;
    }
}

Because the first doscan generates BeanDefinition objects, BeanDefinition is used here to judge

Test successful

Next, write the BeanName
Sort out the structure. In fact, there is also an AnnotationAttributes class in the source code, which is inherited from linkhasmap < string, Object >. Here we use reflection, so we don't operate like this.

Create BeanName interface

/**
 * Generate BeanName interface
 */
public interface BeanNameGenerator {
    String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry);
}

AnnotationBeanNameGenerator concrete beanname implementation class

public class AnnotationBeanNameGenerator implements BeanNameGenerator {
    public static final AnnotationBeanNameGenerator INSTANCE = new AnnotationBeanNameGenerator();
    @Override
    public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {

        if (definition instanceof AnnotatedBeanDefinition){
            AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) definition;
            //Get the Bean return value
            String beanName = determineBeanNameFromAnnotation(annotatedBeanDefinition);
            if (beanName.equals("")) {
                beanName = getDefultBeanName(definition);
            }
            System.out.println("Compont Return value:"+beanName);
        }
;        return  beanName;
    }

  //Get the annotation return value
    private String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedBeanDefinition) {
        //Get annotation metadata information
        AnnotationMetadata annotationMetadata = annotatedBeanDefinition.getAnnotationMetadata();
        //Get the annotation return value
        String componentValue = annotationMetadata.getComponentValue();

        return componentValue;
    }
    //Change uppercase to lowercase
    private String getDefultBeanName(BeanDefinition definition) {
        String getbeanClassName = definition.getbeanClassName();
        char[] chars = getbeanClassName.toCharArray();
        char aChar = chars[0];
        if(aChar>='A'&&aChar<='Z'){
            chars[0]+=32;
        }
        return new String(chars);
    }
    public AnnotationBeanNameGenerator(){
    };
}

Here, a method getComponentValue is added to the metadata class to get the annotation Value

 @Override
    public String getComponentValue() {
        String value = "";
        if (resource.getClassLoader().isAnnotationPresent(Component.class)) {
            value= resource.getClassLoader().getAnnotation(Component.class).value();
        }
        return value;
    }

Test success: user defines it for himself, and others are generated automatically

There is a problem here. I suddenly understand why the source code does not need to annotate objects, because the source code is a general method, which is through intercepting annotation Tostring(), write the template and copy the key code below

When outputting this code in the SimpleAnnotationMetadata class, you need to judge the price type

//@org.example.context.stereotype.Component(value=user)
resource.getClassLoader().getAnnotations()[0].toString()

Write linkmap < string, Object > object in the format of Kev=vlaue, value=beanname

  // Parse the current annotation object
        // @org.example.context.stereotype.Component(value=user)
        // The value=dao content obtained by intercepting the string
        String annotationString = annotation.toString();
        int startIndex = annotationString.lastIndexOf("(") + 1;
        int endIndex = annotationString.lastIndexOf(")");
        annotationString = annotationString.substring(startIndex, endIndex);
        String[] kv = annotationString.split("=");
        // Determine whether to set the value attribute
        if (kv.length > 1) {
            this.putIfAbsent(kv[0], kv[1]);
        } else {
            this.putIfAbsent(kv[0],EMPTY_STRING);
        }

Call:

// Get value as the value of the key, that is, the other name of the Bean, beanName
String beanName = (String) AnnotationAttributes.get("value");
//Output return value

We use this method to get the following Lazy

public class AnalysisAnnocation extends LinkedHashMap<String,Object> {
    private Annotation annotation;
    private Class<? extends Annotation> annotationType;
    private static final String EMPTY_STRING = "";
    public AnalysisAnnocation(){

    }
    //Injection annotation
    public  AnalysisAnnocation(BeanDefinition beanDefinition,Class<? extends Annotation> clazz){
        if (beanDefinition.getbeanClass().isAnnotationPresent(clazz)) {
        this.annotation = beanDefinition.getbeanClass().getAnnotation(clazz);
        this.annotationType = annotation.annotationType();
        }
    }
    public AnalysisAnnocation attributeFor() {
         attributeForMap(annotation);
         return this;
    }

    //Parse annotation tostring();
    private void attributeForMap(Annotation annotation) {
        // Parse the current annotation object
        // @org.example.context.stereotype.Component(value=user)
        // The value=dao content obtained by intercepting the string
        
        //The Value here is not set well, because there may be cases where the Lazy object does not exist, so a default Value is set
        String string="(value='')";
        if (annotation!=null) {
            string = annotation.toString();
        }
        String annotationString =string ;
        int startIndex = annotationString.lastIndexOf("(") + 1;
        int endIndex = annotationString.lastIndexOf(")");
        annotationString = annotationString.substring(startIndex, endIndex);
        String[] kv = annotationString.split("=");
        // Determine whether to set the value attribute
        if (kv.length > 1) {
            this.putIfAbsent(kv[0], kv[1]);
        } else {
            this.putIfAbsent(kv[0],EMPTY_STRING);
        }
    }
}

Test successful

The next step is to verify whether there are beans in the factory. It has been parsed before, and it will be parsed again here

The factory and return factory object methods are constructed in the GenericApplicationContext constructor, the verification method is provided in the interface, implemented in the abstract class, the return factory object method is called, and the verification method is called
code

AbstractApplicationContext class

    /**
     * Abstract classes implement interfaces, and concrete classes inherit abstractions
     */
    public abstract class AbstractApplicationContext implements FuChaShengConfigurationApplicationContext {
        @Override
        public void refresh() {

        }
        //Verification method
        @Override
        public boolean containsBeanDefinition(String beanName) {
            return getBeanFactory().containsBeanDefinition(beanName);
        }
        //Call return factory method
        public abstract ConfigurableListableBeanFactory getBeanFactory();
    }

GenericApplicationContext class

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {


    DefaultListableBeanFactory beanFactory ;
    public GenericApplicationContext(){
        beanFactory = new DefaultListableBeanFactory();
    }

    //Return to factory method
    @Override
    public ConfigurableListableBeanFactory getBeanFactory() {
        return this.beanFactory;
    }

    /**
     * Complete the core method of Bean definition
     * @param beanName
     * @param beanDefinition
     */
    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
        this.beanFactory.registerBeanDefinition(beanName,beanDefinition);
    }

    @Override
    public String getId() {
        return null;
    }
    @Override
    public FuChaShengApplicationContext getParent() {
        return null;
    }

    @Override
    public void setId(String id) {

    }
    @Override
    public void setParent(AppletContext parent) {

    }
    @Override
    public Object getBean(String beanName) {
        return null;
    }
}

BeanDefinitionRegistryBean defines the registration interface

/**
 * Bean Define registration interface
 */
public interface BeanDefinitionRegistry {
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);
    boolean containsBeanDefinition(String beanName);
}

Test it's OK

Look at the source code and turn around to BeanDefinitionHolder and BeanDefinitionRegistry interface object injection. If you don't know why, just build one according to the source code
BeanDefinitionReaderUtils helps inject classes

public class BeanDefinitionReaderUtils {
    public static void registryBeanDefinition(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry) {
        // Get beanName and BeanDefinition
        String beanName = beanDefinitionHolder.getBeanName();
        BeanDefinition beanDefinition = beanDefinitionHolder.getBeanDefinition();
        registry.registerBeanDefinition(beanName, beanDefinition);
    }
}

Call and judge methods

     if (!checkCandidate(beanName)){
                    BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(beanName, beanDefinition);
                    list.add(beanDefinitionHolder);
                    // Register to IOC container
                    registryBeanDefinition(beanDefinitionHolder, this.registry);
                    //Injection plant
                    this.registry.registerBeanDefinition(beanName,beanDefinition);
                }
  // Register to IOC container
    private void registryBeanDefinition(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry) {
        BeanDefinitionReaderUtils.registryBeanDefinition(beanDefinitionHolder, registry);
    }

    /**
     * Check whether it has been registered in IOC
     * @param beanName
     * @return
     */
    private boolean checkCandidate(String beanName) {
        return this.registry.containsBeanDefinition(beanName);
    }

summary
This article mainly simulates the scope system and Lazy's annotation acquisition process, improves the system and successfully injects it into the container. The middle structure hierarchy is not a copy, but the wonderful use of the later interface is becoming more and more wonderful. It feels ordinary when written. For example, judging that there is a Bean in the map is very good, and there is no taste when written. In addition, I directly use the reflection to obtain the scope, because I didn't want to understand why I should parse it at the beginning? Later, in the second Lazy, I found that the wonderful functions can be reused. Therefore, I simulated a supplement in the second Lazy. Anyway, there are many small surprises in the spring source code that need to be studied and studied again and again.

Baidu cloud: https://pan.baidu.com/s/1NBWus1v-CEJZu2NHsya2oA
Password: mdm6

Topics: Java Spring reflection ioc bean