文章目录
手写spring ioc(一)
首记
spring源码断断续续学了近半年,算是小成,不过也是只学了冰山一角,好在ioc基本理清了,于是想出一个手写ioc的系列博客,不知道要写多久,反正尽量写, 这一版本会完成注册bd,DI注入,单例池等简单功能,下图是版本一的图示
手写ioc
上图说了,一切始于bd,首先我们先定义一个BeanDefinition
public interface BeanDefinition {
String SCOPE_SINGLETON = "singleton";//单例
String SCOPE_PROTOTYPE = "prototype";//多例
Class<?> getBeanClass();
void setBeanClass(Class<?> beanClass);
//单多例域
String getScope();
void setScope(String scope);
boolean isSingleton();//是否单例
boolean isPrototype();//是否多例
//懒加载
void setLazyInit(boolean lazyInit);
boolean isLazyInit();
//postConstructor方法
String getInitMethodName();
void setInitMethodName(String initMethodName);
//DI属性
List<PropertyValue> getPropertyValues();
void setPropertyValues(List<PropertyValue> propertyValues);
}
PropertyValue是描述DI属性的,PropertyValue类要写什么呢, 由于在最终spring容器中需要通过属性名字拿到field,然后field.set(v,realValue), 所以PropertyValue需要存储属性名字,属性值
public class PropertyValue {
private String name;//属性名字
private Object value;//属性值
public PropertyValue(String name, Object value){
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
已经定义好了bd,我们写一个实现类
public class GenericBeanDefinition implements BeanDefinition {
private Class<?> beanClass;
private String scope = BeanDefinition.SCOPE_SINGLETON;
private boolean lazyInit = false;
private String initMethodName;
private List<PropertyValue> propertyValues;
@Override
public Class<?> getBeanClass() {
return beanClass;
}
@Override
public void setBeanClass(Class<?> beanClass) {
this.beanClass = beanClass;
}
@Override
public String getScope() {
return scope;
}
@Override
public void setScope(String scope) {
//单例不用改,scope是乱写的也不用改,只有用户传多例才改
if(BeanDefinition.SCOPE_PROTOTYPE.equals(scope)){
this.scope = scope;
}
}
@Override
public boolean isSingleton() {
return BeanDefinition.SCOPE_SINGLETON.equals(scope);
}
@Override
public boolean isPrototype() {
return BeanDefinition.SCOPE_PROTOTYPE.equals(scope);
}
@Override
public void setLazyInit(boolean lazyInit) {
this.lazyInit = lazyInit;
}
@Override
public boolean isLazyInit() {
return lazyInit;
}
@Override
public String getInitMethodName() {
return initMethodName;
}
@Override
public void setInitMethodName(String initMethodName) {
this.initMethodName = initMethodName;
}
@Override
public List<PropertyValue> getPropertyValues() {
return propertyValues;
}
@Override
public void setPropertyValues(List<PropertyValue> propertyValues) {
this.propertyValues = propertyValues;
}
}
现在bd部分就已经完成了,我们要定义bd注册中心和bean工厂
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
Object getBean(String beanName) throws Exception;
boolean containsBean(String beanName);
}
public interface BeanDefinitionRegistry {
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);
void removeBeanDefinition(String beanName);
BeanDefinition getBeanDefinition(String beanName);
boolean containsBeanDefinition(String beanName);
String[] getBeanDefinitionNames();
int getBeanDefinitionCount();
boolean isBeanNameInUse(String beanName);
}
spring基于工厂设计模式实现ioc,我们需要提供一个bean工厂的实现类,来实现上面两个接口
public class DefaultListableBeanFactory implements BeanFactory,BeanDefinitionRegistry {
//bd集合
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
//单例池
private final Map<String,Object> singletonObjects = new ConcurrentHashMap<>(256);
//早期单例池
private final Map<String,Object> earlySingletonObjects = new HashMap<>(16);
//正在创建的单例spring bean的 beanName的集合
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
//bd name 集合
private final List<String> beanDefinitionNames = new ArrayList<>();
/**
* 单例池取
* 取不出,用bd new一个
* 决策构造器,newInstance()
* 空对象==暂无属性
* 加入早期单例池
* 属性注入
* 循环依赖问题发现(定义一个正在创建的bean集合)
* 循环依赖问题(二级缓存解决)
* 调用init方法
* 加入单例池,移除早期单例池
* ioc完毕
* @param beanName
* @return
*/
@Override
public Object getBean(String beanName) throws Exception {
Objects.requireNonNull(beanName,"spring bean name must not null at getBean");
return this.doGetBean(beanName);
}
protected Object doGetBean(String beanName) throws Exception {
BeanDefinition bd = beanDefinitionMap.get(beanName);
if(bd==null||bd.getBeanClass()==null){
return null;
}
Object instance = null;
//单例直接从单例池拿
if(bd.isSingleton()){
instance = singletonObjects.get(beanName);
if(instance!=null){
return instance;
}
}
//决策构造器
instance = createBeanInstance(bd);
//DI
populateBean(bd,instance);
//AOP代理 生命周期回调
instance = initializeBean(bd,instance);
//加入单例池
if(bd.isSingleton()) {
singletonObjects.put(beanName, instance);
}
return instance;
}
private Object initializeBean(BeanDefinition bd, Object instance) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
if(StringUtils.isNotBlank(bd.getInitMethodName())){
Method method = instance.getClass().getMethod(bd.getInitMethodName(),null);
method.invoke(instance,null);
}
//aop省略
return instance;
}
private void populateBean(BeanDefinition bd, Object instance) throws Exception {
if(null==instance||null==bd.getPropertyValues()||bd.getPropertyValues().isEmpty())return;
for(PropertyValue pv:bd.getPropertyValues()){
if(StringUtils.isBlank(pv.getName())){
continue;
}
Class clazz = instance.getClass();
Field field = clazz.getDeclaredField(pv.getName());
field.setAccessible(true);
Object realVal = getBaseValue(field.getType(),getRealValue(pv.getValue()));
field.set(instance,realVal);
}
}
private Object getBaseValue(Class<?>type,Object realVal){
if(type==int.class||type==Integer.class){
realVal = Integer.parseInt(realVal.toString());
}else if(type==double.class||type==Double.class){
realVal = Double.parseDouble(realVal.toString());
}else if(type==float.class||type==Float.class){
realVal = Float.parseFloat(realVal.toString());
}else if(type==boolean.class||type==Boolean.class){
realVal = Boolean.parseBoolean(realVal.toString());
}else if(type==long.class||type==Long.class){
realVal = Long.parseLong(realVal.toString());
}else if(type==byte.class||type==Byte.class){
realVal = Byte.parseByte(realVal.toString());
}else if(type==short.class||type==Short.class){
realVal = Short.parseShort(realVal.toString());
}else if(type==char.class||type==Character.class){
realVal = 'A';
}
return realVal;
}
//集合处理太复杂,省略
public Object getRealValue(Object value) throws Exception {
Object realValue = null;
if(value==null){
realValue = null;
}else if (value instanceof BeanReference) {
realValue = this.doGetBean(((BeanReference) value).getBeanName());
} else if (value instanceof Object[]) {
Object[] objs = (Object[]) value;
for (int i=0;i<objs.length;i++) {
objs[i] = getRealValue(objs[i]);
}
realValue = objs;
} else if (value instanceof Collection) {
Collection collection = (Collection) value;
Object[] objs = collection.toArray();
for (int i=0;i<objs.length;i++) {
objs[i] = getRealValue(objs[i]);
}
//todo
if(value instanceof List){
List list= Arrays.asList(objs);
if(value instanceof ArrayList){
realValue = Arrays.asList(objs);
}else if(value instanceof LinkedList){
LinkedList ll = new LinkedList();
ll.addAll(list);
realValue = ll;
}else if(value instanceof Vector){
Vector v = new Vector();
v.addAll(list);
realValue = v;
}
}else if(value instanceof Set){
if(value instanceof HashSet){
}else if(value instanceof LinkedHashSet){
}else if(value instanceof SortedSet){
}else if(value instanceof TreeSet){
}
}
} else if (value instanceof Properties) {
// TODO 处理properties中的bean引用
} else if (value instanceof Map) {
// TODO 处理Map中的bean引用
} else{
realValue = value;
}
return realValue;
}
private Object createBeanInstance(BeanDefinition bd) throws Exception {
return bd.getBeanClass().newInstance();
}
@Override
public boolean containsBean(String beanName) {
return false;
}
@Override
public void registerBeanDefinition(String beanName, BeanDefinition bd) {
Objects.requireNonNull(beanName,"spring bean name must not null at registerBeanDefinition");
Objects.requireNonNull(bd,"spring bd must not null at registerBeanDefinition");
Objects.requireNonNull(bd.getBeanClass(),"spring bd class must not null at registerBeanDefinition");
if(beanDefinitionMap.containsKey(beanName)){
throw new RuntimeException("beanDefinitions must not contain same beanName:"+beanName);
}
beanDefinitionMap.put(beanName,bd);
beanDefinitionNames.add(beanName);
}
@Override
public void removeBeanDefinition(String beanName) {
beanDefinitionMap.remove(beanName);
beanDefinitionNames.remove(beanName);
}
@Override
public BeanDefinition getBeanDefinition(String beanName) {
return beanDefinitionMap.get(beanName);
}
@Override
public boolean containsBeanDefinition(String beanName) {
return beanDefinitionMap.containsKey(beanName);
}
@Override
public String[] getBeanDefinitionNames() {
return (String[]) beanDefinitionNames.toArray();
}
@Override
public int getBeanDefinitionCount() {
return beanDefinitionNames.size();
}
@Override
public boolean isBeanNameInUse(String beanName) {
return singletonsCurrentlyInCreation.contains(beanName);
}
//预加载所有的单例非懒加载的bean
public void preInstantiateSingletons() throws Exception{
synchronized (beanDefinitionNames){
for (String name : beanDefinitionNames){
BeanDefinition beanDefinition = this.getBeanDefinition(name);
if (beanDefinition.isSingleton()&&!beanDefinition.isLazyInit()){
this.doGetBean(name);
}
}
}
}
}
到此为止实现了ioc简单功能,我们可以写一个测试类测试一下
public class ABean {
private String name;
private int age;
public void doSth(){
System.out.println("do");
}
public void init(){
System.out.println("init");
}
}
public class Test {
DefaultListableBeanFactory fac = new DefaultListableBeanFactory();
@org.junit.jupiter.api.Test
public void testIoc1() throws Exception {
BeanDefinition bd = new GenericBeanDefinition();
bd.setBeanClass(ABean.class);
bd.setInitMethodName("init");
List<PropertyValue> pvs = new ArrayList<>();
pvs.add(new PropertyValue("name","lry"));
pvs.add(new PropertyValue("age",20));
bd.setPropertyValues(pvs);
fac.registerBeanDefinition("aBean",bd);
fac.preInstantiateSingletons();
//上面都是系统做的,以后会支持注解扫描出类注册bd,提前实例化bean
ABean aBean = (ABean) fac.getBean("aBean");
aBean.doSth();
}
}
输出
init
do
证明一切顺利,但实则不然,还有一个小问题,如果ABean中有个属性要注入spring容器中的CBean怎么办,
这个new PropertyValue(“name”,“lry”)要怎么写
写法一:new PropertyValue(“cBean”,“cBean”) 肯定不行,他会把字符串cBean赋值给cBean对象 ,肯定报错
写法一:new PropertyValue(“cBean”,newCBean()) 肯定不行,我们需要的是spring管理的bean,而不是手动new出来的,
所以我想要是spring看到cBean这个字符串就知道是个spring bean就好了,那么他就可以调用getBean(cBean)获取这个bean了,所以我们可以定义一个BeanReference只储存beanName即可
public class BeanReference {
private String beanName;
public BeanReference(String beanName){
this.beanName = beanName;
}
public String getBeanName(){
return beanName;
}
}
DefaultListableBeanFactory 的getRealValue 加入以下代码
else if (value instanceof BeanReference) {
realValue = this.doGetBean(((BeanReference) value).getBeanName());
}
再次修改测试用例
public class CBean {
}
public class Test {
DefaultListableBeanFactory fac = new DefaultListableBeanFactory();
@org.junit.jupiter.api.Test
public void testIoc1() throws Exception {
BeanDefinition bd1 = new GenericBeanDefinition();
bd1.setBeanClass(CBean.class);
fac.registerBeanDefinition("cBean",bd1);
BeanDefinition bd = new GenericBeanDefinition();
bd.setBeanClass(ABean.class);
bd.setInitMethodName("init");
List<PropertyValue> pvs = new ArrayList<>();
pvs.add(new PropertyValue("name","lry"));
pvs.add(new PropertyValue("age",20));
pvs.add(new PropertyValue("cBean",new BeanReference("cBean")));
bd.setPropertyValues(pvs);
fac.registerBeanDefinition("aBean",bd);
fac.preInstantiateSingletons();
//上面都是系统做的,以后会支持注解扫描出类注册bd,提前实例化bean
ABean aBean = (ABean) fac.getBean("aBean");
aBean.doSth();
}
}
总结
第一版的ioc就算完成了,不过还有很多不完善的地方,例如循环引用,推断构造器,提前暴露对象(有aop的情况是提前暴露对象工厂),其中最让我头疼的就是推断构造器,推断构造器实在是太难了,我又回头看了推断构造器的源码和ImportBeanDefinitionRegistrar这个接口,突然对mybatis的MapperScan注解有了更深层次的理解,spring推断构造器的通过构造器装配真的是把扩展发挥到极致了,它可以让mybatis不加Component注解却依然可以注入一个class对象,我很想给自己的spring ioc也提供这个功能,看看以后写不写得出来