上一篇我们完成了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