当前位置: 移动技术网 > IT编程>开发语言>Java > Spring源码试读--BeanFactory模拟实现

Spring源码试读--BeanFactory模拟实现

2019年02月09日  | 移动技术网IT编程  | 我要评论

热爱剧情介绍,一触即发吻戏,申思近况

动机

现在springboot越来越便捷,如果简单的spring应用,已无需再配置xml文件,基本可以实现全注解,即使是springcloud的那套东西,也都可以通过yaml配置完成。最近一年一直在用springboot+jpa或者springboot+mybatis,基本上不用spring和springmvc了,心血来潮想着趁假期试着一点点实现一下spring的基本功能(当然是会对照源码的,毕竟很多细节想不到,变量命名也会按照源码来),基本思路就是先按照spring的类图试着自己写,争取实现相同的功能,然后再看源码的实现方式,再重构。

第一篇先实现spring的基本组件--bean容器

雏形

定义两个接口beanfactory和beandefinition

public interface beanfactory {

    beandefinition getbeandefinition(string beanid)
    object getbean(string beanid);
}
public interface beandefinition {

    public string getbeanclassname();
}

两个实现类defaultbeanfactory和genericbeandefinition分别实现这两个接口:

public class defaultbeanfactory implements beanfactory {
    public static final string id_attribute="id";
    public static final string class_attribute="class";
    private map<string,beandefinition> beandefinitionmap=new concurrenthashmap<string, beandefinition>();
    public defaultbeanfactory(string configfile) {
        loadbeandefinition(configfile);

    }
    
    private void loadbeandefinition(string configfile) {
        inputstream is= null;
        classloader classloader = this.getclass().getclassloader();
        is=classloader.getresourceasstream(configfile);
        //需要dom4j
        saxreader saxreader = new saxreader();
        try {
            document doc = saxreader.read(is);
            element root = doc.getrootelement();
            iterator iterator = root.elementiterator();
            while (iterator.hasnext()){
                element element = (element)iterator.next();
                string id=element.attributevalue(id_attribute);
                string classname=element.attributevalue(class_attribute);
                beandefinition beandefinition = new genericbeandefinition(id, classname);
                beandefinitionmap.put(id,beandefinition);
            }
        } catch (documentexception e) {
           throw new beandefinitionstoreexception("load and parsing xml failed",new throwable());
        }finally {
            if(is!=null){
                try {
                    is.close();
                } catch (ioexception e) {
                    e.printstacktrace();
                }
            }
        }



    }

    public beandefinition getbeandefinition(string beanid) {
        if(beandefinitionmap.containskey(beanid))
            return beandefinitionmap.get(beanid);
        return null;
    }
    //职责2:创建bean实例
    public object getbean(string beanid) {
        beandefinition beandefinition = this.getbeandefinition(beanid);
        if(beandefinition==null){
            throw new beancreationexception("bean definition does not exist");
        }
        classloader classloader = this.getclass().getclassloader();
        try {
            class<?> clz = classloader.loadclass(beandefinition.getbeanclassname());
            return clz.newinstance();
            //捕获所有异常,然后抛出自定义异常
        } catch (exception e) {
            throw new beancreationexception("create bean for "+beandefinition.getbeanclassname()+" failed.");
        }

    }
}
public class genericbeandefinition implements beandefinition {
    private string id;
    private string beanclassname;
    public genericbeandefinition(string id, string beanclassname) {
        this.id = id;
        this.beanclassname = beanclassname;
    }
    public string getbeanclassname() {

        return this.beanclassname;
    }

}

主要逻辑在defaultbeanfactory中,通过解析xml来生成一个bean实例并保存到map中。

单一指责原则

  • 核心思想:一个类应该有且只有一个变化的原因。

  • 为什么引入单一职责:

    在srp中,把职责定义为变化的原因。当需求变化时,将通过更改职责相关的类来体现。如果一个类拥有多于一个的职责,则多个职责耦合在一起,会有多于一个原因来导致这个类发生变化。一个职责的变化可能会影响到其他的职责,另外,把多个职责耦合在一起,影响复用性。如:defaultbeanfactory类目前有两个指责:1.加载和读取xml文件;2.创建bean实例

    我们把读取xml的职责拆分出来给一个新类xmlbeandefinitionreader,同时,beanfactory是供给client使用的,而beandefinition是一个内部的概念,应该对client是透明的,所以不应该对外暴露,所以把getbeandefinition和注册(即之前的添加到map)职责分出来给一个新接口beandefinitionregistry。defaultbeanfactory实现beandefinitionregistry,下一节会用一个applicationcontext包装defaultbeanfactory,进而对用户屏蔽getbeandefinition()和registerbeandefinition()。

修改后的defaultbeanfactory

public class defaultbeanfactory implements beanfactory,beandefinitionregistry {
    private map<string,beandefinition> beandefinitionmap=new concurrenthashmap<string, beandefinition>();
    public defaultbeanfactory(){

    }

    public beandefinition getbeandefinition(string beanid) {
        if(beandefinitionmap.containskey(beanid))
            return beandefinitionmap.get(beanid);
        return null;
    }

    public void registerbeandefinition(string beanid, beandefinition beandefinition) {
        this.beandefinitionmap.put(beanid,beandefinition);
    }


    public object getbean(string beanid) {
        beandefinition beandefinition = this.getbeandefinition(beanid);
        if(beandefinition==null){
            throw new beancreationexception("bean definition does not exist");
        }
        classloader classloader = this.getclass().getclassloader();
        try {
            class<?> clz = classloader.loadclass(beandefinition.getbeanclassname());
            return clz.newinstance();
            //捕获所有异常,然后抛出自定义异常
        } catch (exception e) {
            throw new beancreationexception("create bean for "+beandefinition.getbeanclassname()+" failed.");
        }

    }
}

beandefinitionregistry接口:

public interface beandefinitionregistry {
    beandefinition getbeandefinition(string beanid);
    void registerbeandefinition(string beanid,beandefinition beandefinition);
}

xmlbeandefinitionreader类:用来读取xml并调用beandefinitionregistry的registerbeandefinition方法注册beandefinition。

public class xmlbeandefinitionreader {

    public static final string id_attribute = "id";

    public static final string class_attribute = "class";

    public static final string scope_attribute = "scope";

    beandefinitionregistry registry;

    public xmlbeandefinitionreader(beandefinitionregistry registry) {
        this.registry = registry;
    }

    public void loadbeandefinition(string configfile) {
        inputstream is = null;
        classloader classloader = this.getclass().getclassloader();
        is = classloader.getresourceasstream(configfile);
        saxreader saxreader = new saxreader();
        try {
            document doc = saxreader.read(is);
            element root = doc.getrootelement();
            iterator iterator = root.elementiterator();
            while (iterator.hasnext()) {
                element element = (element) iterator.next();
                string id = element.attributevalue(id_attribute);
                string classname = element.attributevalue(class_attribute);
                beandefinition beandefinition = new genericbeandefinition(id, classname);
                registry.registerbeandefinition(id, beandefinition);
            }
        } catch (documentexception e) {
            throw new beandefinitionstoreexception("load and parsing xml failed", new throwable());
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (ioexception e) {
                    e.printstacktrace();
                }
            }
        }
    }
}

applicationcontext

spring中通常不会直接访问beanfactory,而是通过applicationcontext来得到bean,即通过applicationcontext调用beanfactory方法。

定义一个接口applicationcontext继承beanfactory:

public interface applicationcontext extends beanfactory {
}

创建一个实现类classpathxmlapplicationcontext,从classpath下读取xml,内部持有一个defaultbeanfactory实例,对外只暴露getbean()方法,屏蔽了getbeandefinition()和registerbeandefinition():

public class classpathxmlapplicationcontext implements applicationcontext {
    private defaultbeanfactory factory=null;
    public classpathxmlapplicationcontext(string configfile) {
        factory=new defaultbeanfactory();
        xmlbeandefinitionreader reader=new xmlbeandefinitionreader(factory);
        reader.loadbeandefinition(configfile);
    }

    public object getbean(string beanid) {
        return factory.getbean(beanid);
    }
}

resource

使用resource来抽象资源

除了从classpath读取xml,还可以从filesystem读取,最终都是要转换成为一个inputstream,所以抽象出一个resource接口,并创建两个实现类来分别处理从两种途径读取xml。

public interface resource {
    inputstream getinputstream() throws ioexception;
    string getdescription();

}
public class classpathresource implements resource {

    private string path;
    private classloader classloader;

    public classpathresource(string path) {
        this(path, (classloader) null);
    }
    public classpathresource(string path, classloader classloader) {
        this.path = path;
        this.classloader = (classloader != null ? classloader : classutils.getdefaultclassloader());
    }

    public inputstream getinputstream() throws ioexception {
        inputstream is = this.classloader.getresourceasstream(this.path);

        if (is == null) {
            throw new filenotfoundexception(path + " cannot be opened");
        }
        return is;

    }
    public string getdescription(){
        return this.path;
    }

}
public class filesystemresource implements resource {

    private final string path;
    private final file file;


    public filesystemresource(string path) {
        //这里的assert不是junit的assert,是自定义的一个工具类,就是判空处理并提示指定信息,逻辑简单不贴代码了
        assert.notnull(path, "path must not be null");
        this.file = new file(path);
        this.path = path;
    }

    public inputstream getinputstream() throws ioexception {
        return new fileinputstream(this.file);
    }

    public string getdescription() {
        return "file [" + this.file.getabsolutepath() + "]";
    }

}

现在defaultbeanfactory中的loadbeandefinition可以接收一个resource对象,从中获取inputstream,而不用管是从classpath还是从filesystem读取的。同时可以创建一个与classpathxmlapplicationcontext相对应的filesystemxmlapplicationcontext类来完成从filesystem读取xml并获取bean:

public class filesystemxmlapplicationcontext implements applicationcontext {
    defaultbeanfactory factory=null;
    public filesystemxmlapplicationcontext(string path) {
        factory=new defaultbeanfactory();
        xmlbeandefinitionreader reader = new xmlbeandefinitionreader(factory);
        //这是与classpathxmlapplicationcontext唯一的区别
        resource resource=new filesystemresource(path);
        reader.loadbeandefinition(resource);
    }
    public object getbean(string beanid){
        return factory.getbean(beanid);

    }
}

可以发现这个类和classpathxmlapplicationcontext唯一的区别就是resource不同,为了避免重复代码,用模板方法重构,新建一个抽象类abstractapplicationcontext,然后两个applicationcontext类继承并实现getresourcebypath。

public abstract class abstractapplicationcontext implements applicationcontext {

    private defaultbeanfactory factory = null;
    private classloader beanclassloader=null;

    public abstractapplicationcontext(string configfile){
        factory = new defaultbeanfactory();
        xmlbeandefinitionreader reader = new xmlbeandefinitionreader(factory);
        resource resource = this.getresourcebypath(configfile);
        reader.loadbeandefinition(resource);
    }

    public object getbean(string beanid) {

        return factory.getbean(beanid);
    }

    protected abstract resource getresourcebypath(string path);
}

scope

spring中的bean有一个scope属性用来指定bean是否是单例。而spring是如何管理单例对象的呢?肯定不是把类设计成单例模式,而是spring统一管理bean,然后根据scope属性来提供bean实例。

先定义一个接口singletonbeanregistry:

public interface singletonbeanregistry {

    void registersingleton(string beanname, object singletonobject);

    object getsingleton(string beanname);
}

它的实现类defaultsingletonbeanregistry,通过一个map管理单例对象:

public class defaultsingletonbeanregistry implements singletonbeanregistry {

    private final map<string, object> singletonobjects = new concurrenthashmap<string, object>(64);

    public void registersingleton(string beanname, object singletonobject) {

        assert.notnull(beanname, "'beanname' must not be null");

        object oldobject = this.singletonobjects.get(beanname);
        if (oldobject != null) {
            throw new illegalstateexception("could not register object [" + singletonobject +
                    "] under bean name '" + beanname + "': there is already object [" + oldobject + "] bound");
        }
        this.singletonobjects.put(beanname, singletonobject);

    }

    public object getsingleton(string beanname) {

        return this.singletonobjects.get(beanname);
    }

}

咱们的defaultbeanfactory要继承defaultsingletonbeanregistry(也可以内部持有一个defaultsingletonbeanregistry对象,采用组合模式),修改getbean()方法:

public object getbean(string beanid) {
    beandefinition beandefinition = this.getbeandefinition(beanid);
    if(beandefinition==null){
        throw new beancreationexception("bean definition does not exist");
    }
    if(beandefinition.issingleton()){
        object bean = this.getsingleton(beanid);
        if(bean == null){
            bean = createbean(beandefinition);
            this.registersingleton(beanid, bean);
        }
        return bean;
    }
    return createbean(beandefinition);



}

同时我们的beandefinition和genericbeandefinition也要修改,增加singleton相关的属性:

public interface beandefinition {
    public static final string scope_singleton = "singleton";
    public static final string scope_prototype = "prototype";
    public static final string scope_default = "";

    public boolean issingleton();
    public boolean isprototype();
    string getscope();
    void setscope(string scope);

    public string getbeanclassname();
}
public class genericbeandefinition implements beandefinition {
    private string id;
    private string beanclassname;
    private boolean singleton = true;
    private boolean prototype = false;
    private string scope = scope_default;
    public genericbeandefinition(string id, string beanclassname) {

        this.id = id;
        this.beanclassname = beanclassname;
    }
    public string getbeanclassname() {

        return this.beanclassname;
    }

    public boolean issingleton() {
        return this.singleton;
    }
    public boolean isprototype() {
        return this.prototype;
    }
    public string getscope() {
        return this.scope;
    }
    public void setscope(string scope) {
        this.scope = scope;
        this.singleton = scope_singleton.equals(scope) || scope_default.equals(scope);
        this.prototype = scope_prototype.equals(scope);

    }
}

xmlbeandefinitionreader类中的loadbeandefinition()也要修改,使其能读取xml文件中的scope属性。

至此,基本的beanfactory就实现了。我们可以通过xml文件装载bean了。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网