纯手写实现Spring源码框架(三)

上一篇我们完成了Bean定义的加载对象,这次完成scope获取value值写入元数据和设置BeanName

scope体系
纯手写实现Spring源码框架(三)
定义作用域源数据类ScopeMetadata

/**
 * 定义作用域源数据类
 */
public class ScopeMetadata {
    //定义作用域的默认值
    private String scopeName = BeanDefinition.SCOPE_SINGLETON;

    public String getScopeName() {
        return scopeName;
    }

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

定义ScopeMetadata元数据注入接口ScopeMetadataResolver

/**
 *ScopeMetadata元数据注入接口
 */
public interface ScopeMetadataResolver {
    ScopeMetadata resolverScopeMetadata(BeanDefinition beanDefinition);
}

定义ScopeMetadata元数据注入具体类AnnotationScopeMetadataResolver

/**
 * 实现scope元数据定义具体类
 */
public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver {
    private Class<? extends Annotation> scopeType = Scope.class;
    @Override
    public ScopeMetadata resolverScopeMetadata(BeanDefinition beanDefinition) {
        ScopeMetadata scopeMetadata = new ScopeMetadata();
        // 获取当前beanDefinition处理的Class对象
        Class<?> beanClass = beanDefinition.getbeanClass();
        // 判断类对象上面是否存在@ZXScopez注解
        if (beanClass.isAnnotationPresent(scopeType)) {
            // 获取ZXScope注解对象
            Scope annotation = ((Scope) beanClass.getAnnotation(scopeType));
            // 获取主键设置value值
            String value = annotation.value();
            if (!"".equals(value)) {
                scopeMetadata.setScopeName(value);
            }
        }
        return scopeMetadata;
    }
}

因为在最前的doscan是生成BeanDefinition对象,这里用BeanDefinition去判断

测试成功
纯手写实现Spring源码框架(三)
接下来进行BeanName的写入
梳理结构,其实这里源码当中还用一个AnnotationAttributes类,继承自linkhasmap<String,Object>来实现的,这里我们用反射因此就不这样操作啦。
纯手写实现Spring源码框架(三)
创建BeanName别名称接口

/**
 * 生成BeanName别名称接口
 */
public interface BeanNameGenerator {
    String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry);
}

AnnotationBeanNameGenerator 具体beanname实现类

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;
            //拿到Bean返回值
            String beanName = determineBeanNameFromAnnotation(annotatedBeanDefinition);
            if (beanName.equals("")) {
                beanName = getDefultBeanName(definition);
            }
            System.out.println("Compont返回值:"+beanName);
        }
;        return  beanName;
    }

  //拿到注解返回值
    private String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedBeanDefinition) {
        //获取注解元数据信息
        AnnotationMetadata annotationMetadata = annotatedBeanDefinition.getAnnotationMetadata();
        //拿到注解返回值
        String componentValue = annotationMetadata.getComponentValue();

        return componentValue;
    }
    //将大写首字母转为小写
    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(){
    };
}

这里在元数据类新增了一条方法getComponentValue拿取注解Value值的方法

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

测试成功:user为自己定义,其他的则为自动生成的
纯手写实现Spring源码框架(三)

这里有问题,突然理解到了为什么源码为什么不用注解对象了,因为源码是个通用的方法,是通过截取annotation.toString()解决的,写了模板把关键代码复制在下面了

SimpleAnnotationMetadata类输出这段代码,需要价格类型判断

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

写入linkmap<String,object>对象格式为:Kev=vlaue,value=beanname

  // 解析当前的注解对象
        // @org.example.context.stereotype.Component(value=user)
        // 截取字符串获取的value=dao内容
        String annotationString = annotation.toString();
        int startIndex = annotationString.lastIndexOf("(") + 1;
        int endIndex = annotationString.lastIndexOf(")");
        annotationString = annotationString.substring(startIndex, endIndex);
        String[] kv = annotationString.split("=");
        // 判断是否设置value属性
        if (kv.length > 1) {
            this.putIfAbsent(kv[0], kv[1]);
        } else {
            this.putIfAbsent(kv[0],EMPTY_STRING);
        }

调用:

// 获取value作为key的值,也就是Bean的别名称beanName
String beanName = (String) AnnotationAttributes.get("value");
//输出返回值

下面的Lazy获取我们就用这种方式

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

    }
    //注入注解
    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;
    }

    //解析annotation.tostring();
    private void attributeForMap(Annotation annotation) {
        // 解析当前的注解对象
        // @org.example.context.stereotype.Component(value=user)
        // 截取字符串获取的value=dao内容
        
        //这里Value没设置好,因为可能存在Lazy对象不存在的情况所有设置了一个默认值
        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("=");
        // 判断是否设置value属性
        if (kv.length > 1) {
            this.putIfAbsent(kv[0], kv[1]);
        } else {
            this.putIfAbsent(kv[0],EMPTY_STRING);
        }
    }
}

测试成功
纯手写实现Spring源码框架(三)

接下来是校验工厂里是否有存在Bean,之前解析过这里再次解析一次
纯手写实现Spring源码框架(三)
GenericApplicationContext构造函数中构造了工厂和返回工厂对象方法,在接口中提供校验方法,在抽象类进行实现,调用返回工厂对象方法,调用校验方法
编码

AbstractApplicationContext


    /**
     * 抽象类实现接口,具体类继承抽象
     */
    public abstract class AbstractApplicationContext implements FuChaShengConfigurationApplicationContext {
        @Override
        public void refresh() {

        }
        //校验方法
        @Override
        public boolean containsBeanDefinition(String beanName) {
            return getBeanFactory().containsBeanDefinition(beanName);
        }
        //调用返回工厂方法
        public abstract ConfigurableListableBeanFactory getBeanFactory();
    }

GenericApplicationContext

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {


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

    //返回工厂方法
    @Override
    public ConfigurableListableBeanFactory getBeanFactory() {
        return this.beanFactory;
    }

    /**
     * 完成Bean定义的核心方法
     * @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定义注册接口

/**
 * Bean定义注册接口
 */
public interface BeanDefinitionRegistry {
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);
    boolean containsBeanDefinition(String beanName);
}

测试 it’s OK
纯手写实现Spring源码框架(三)
看源码又绕了个圈子转为了BeanDefinitionHolderBeanDefinitionRegistry接口对象注入,这里还不懂为什么,就按源码建个吧
BeanDefinitionReaderUtils 帮助注入类

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

调用和判断方法

     if (!checkCandidate(beanName)){
                    BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(beanName, beanDefinition);
                    list.add(beanDefinitionHolder);
                    // 注册到IOC容器
                    registryBeanDefinition(beanDefinitionHolder, this.registry);
                    //注入工厂
                    this.registry.registerBeanDefinition(beanName,beanDefinition);
                }
  // 注册到IOC容器
    private void registryBeanDefinition(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry) {
        BeanDefinitionReaderUtils.registryBeanDefinition(beanDefinitionHolder, registry);
    }

    /**
     * 检查是否已经在IOC中注册过
     * @param beanName
     * @return
     */
    private boolean checkCandidate(String beanName) {
        return this.registry.containsBeanDefinition(beanName);
    }

总结
这篇主要是模拟了scope体系,和Lazy的注解获取过程,健全了体系并成功注入了容器。中间结构层次不算复制,但是用到后面接口的妙用越来越精彩,写出来就觉得平平无奇,比如判断map中有Bean那个接口就很妙,写出来就没了味道,而且关于Scope哪里我直接用的反射获得的,因为开始的时候没想明白为什么要解析?后后到了第二部Lazy我才发现妙用可以重用,因此在第二部Lazy哪里模拟了一个补上,反正spring源码当中有很多小惊喜需要一遍一遍的学习和研读。

百度云:https://pan.baidu.com/s/1NBWus1v-CEJZu2NHsya2oA
密码:mdm6

上一篇:来来来!尚硅谷java全套视频


下一篇:Spring学习笔记--源码解析aop代理对象创建流程