上一篇我们完成了Bean定义的加载对象,这次完成scope获取value值写入元数据和设置BeanName
scope体系
定义作用域源数据类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
去判断
测试成功
接下来进行BeanName的写入
梳理结构,其实这里源码当中还用一个AnnotationAttributes
类,继承自linkhasmap<String,Object>
来实现的,这里我们用反射因此就不这样操作啦。
创建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为自己定义,其他的则为自动生成的
这里有问题,突然理解到了为什么源码为什么不用注解对象了,因为源码是个通用的方法,是通过截取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); } } }
测试成功
接下来是校验工厂里是否有存在Bean,之前解析过这里再次解析一次
在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; } }
BeanDefinitionRegistry
Bean定义注册接口
/** * Bean定义注册接口 */ public interface BeanDefinitionRegistry { void registerBeanDefinition(String beanName, BeanDefinition beanDefinition); boolean containsBeanDefinition(String beanName); }
测试 it’s OK
看源码又绕了个圈子转为了BeanDefinitionHolder
和BeanDefinitionRegistry
接口对象注入,这里还不懂为什么,就按源码建个吧
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