• 自制IOC容器(2)


    本系列文章介绍ByxContainer的实现思路。

    ByxContainer是一个简单的轻量级IOC容器,具有以下特性:

    • 使用JSON格式的配置文件
    • 支持构造函数注入、静态工厂注入、实例工厂注入、属性注入、setter注入、条件注入
    • 组件的延迟加载和单例组件
    • 根据id注册、获取容器中的组件

    ByxContainer的设计借鉴了ajoo大神的博客

    项目地址:github 码云

    本篇文章介绍ByxContainer中与对象创建有关的设计。

    对象的创建方式

    要解决上面的问题,我们需要思考:创建一个对象到底有多少种方法呢?

    在Java中,创建一个对象主要有以下三种方法:

    • 构造函数
    • 静态工厂
    • 实例工厂

    最常用的创建对象的方式,肯定是构造函数了:

    A a = new A("hello", 123);
    

    静态工厂方式,就是通过调用某个工厂类的静态方法来创建对象,常用于工厂模式:

    A a = Factory.create("hello", 123);
    

    实例工厂方式,则是通过调用某个工厂类的实例方法来创建对象:

    Factory factory = new Factory();
    A a = factory.create("hello", 123);
    

    对于最后一种实例工厂方式,有的人可能觉得很陌生,但是实际上,我们在Java中调用的大多数方法都是实例工厂。其实,凡是通过对象实例调用并且返回一个值的方法都属于实例工厂,即使这个方法与创建对象并没有语义上的关系,如Stringsubstring,或者Listget

    其实这里还漏了一种方式,那就是:我们不需要容器来帮我们创建对象,而是直接把创建好的对象交给容器,到时候让容器直接返回这个对象就行了。什么时候需要用这种方式呢?比如说,我们想在容器中放一个整数123,但我们并不希望到时候让容器调用new java.lang.Integer(123)来创建这个整数,而是希望容器直接返回一个123给我们。如果这里不理解,可以直接看下面ValueComponent的实现。

    封装通用Component实现类

    既然归纳出了上面四种创建对象的方式,那么我们是不是可以对这四种方式分别封装一个通用的Component实现类呢?这样,假如用户恰好需要使用这四种方式之一来创建对象,就可以直接使用我们写好的实现类,而不用自己编写实现类了。

    首先是最简单的ValueComponent,它封装了已经创建出来的对象:

    public class ValueComponent implements Component
    {
        private final Object value;
    
        public ValueComponent(Object value)
        {
            this.value = value;
        }
    
        @Override
        public Object create()
        {
            return value;
        }
    }
    

    为了使用更方便,可以在Component接口定义中添加一个静态方法value

    public interface Component
    {
        ...
        static Component value(Object value)
        {
            return new ValueComponent(value);
        }
    }
    

    这样,只要静态导入了Component,就可以直接写下面的代码:

    Component intValue = value(123);
    Component stringValue = value("hello");
    

    然后是ConstructorComponent

    public class ConstructorComponent implements Component
    {
        private final Class<?> type;
        private final Component[] params;
    
        public ConstructorComponent(Class<?> type, Component... params)
        {
            this.type = type;
            this.params = params;
        }
    
        @Override
        public Object create()
        {
            // 获取参数
            Object[] p = Arrays.stream(params).map(Component::create).toArray();
            
            // 调用type的构造函数,并传递参数
            ...
        }
    }
    
    public interface Component
    {
        ...
        static Component constructor(Class<?> type, Component... params)
        {
            return new ConstructorComponent(type, params);
        }
    }
    

    ConstructorComponent需要一个type用来指明调用哪个类的构造函数,params用来传递构造函数的参数。注意,params的类型是Component[],而不是Object[],为什么呢?因为构造函数的参数也可能是一个被IOC容器管理的组件,例如:

    B b = new B();
    A a = new A(b);
    

    这里ab都是IOC容器中的组件,可以这样来声明这两个组件:

    Component b = constructor(B.class);
    Component a = constructor(A.class, b);
    

    如果想向构造函数传递常数,可以用ValueComponent包装一下:

    // A a = new A("hello", 123);
    Component a = constructor(value("hello"), value(123));
    

    接着是StaticFactoryComponent

    public class StaticFactoryComponent implements Component
    {
        private final Class<?> type;
        private final String method;
        private final Component[] params;
    
        public StaticFactoryComponent(Class<?> type, String method, Component[] params)
        {
            this.type = type;
            this.method = method;
            this.params = params;
        }
    
        @Override
        public Object create()
        {
            // 获取参数
            Object[] p = Arrays.stream(params).map(Component::create).toArray();
    
            // 调用type的静态method方法,并传递参数
            ...
        }
    }
    
    public interface Component
    {
        ...
        static Component staticFactory(Class<?> type, String method, Component... params)
        {
            return new StaticFactoryComponent(type, method, params);
        }
    }
    

    type是工厂类的Classmethor是工厂方法名,params是方法参数。

    最后是InstanceFactoryComponent

    public class InstanceFactoryComponent implements Component
    {
        private final Component instance;
        private final String method;
        private final Component[] params;
    
        public InstanceFactoryComponent(Component instance, String method, Component[] params)
        {
            this.instance = instance;
            this.method = method;
            this.params = params;
        }
    
        @Override
        public Object create()
        {
            // 获取实例和参数
            Object i = instance.create();
            Object[] p = Arrays.stream(params).map(Component::create).toArray();
    
            // 调用i的实例method方法,并传递参数
            ...
        }
    }
    
    public interface Component
    {
        ...
        static Component instanceFactory(Component instance, String method, Component... params)
        {
            return new InstanceFactoryComponent(instance, method, params);
        }
    }
    

    instance是创建实例的组件,method是实例方法名,params是方法参数。

    使用ByxContainer

    到这里,与创建对象有关的操作就完成得差不多了,对于一些常规的创建对象的需求,ByxContainer都能很好地应对。下面给出一些使用示例:

    // A a = new A();
    Component a = constructor(A.class);
    
    // A a = new A("hello", 123);
    Component a = constructor(value("hello"), value(123));
    
    // A a = Factory.createDefault();
    Component a = staticFactory(Factory.class, "createDefault");
    
    // A a = Factory.create("hello", 123);
    Component a = staticFactory(Factory.class, "create", value("hello"), value(123));
    
    // B b = new B();
    // A a = b.create("hello", 123);
    Component b = construct(B.class);
    Component a = instanceFactory(b, "create", value("hello"), value(123));
    
    // A a = new B().create("hello", 123);
    Component a = instanceFactory(constructor(B.class), "create", value("hello"), value(123));
    
  • 相关阅读:
    双列集合
    单列集合
    Comparable和Comparator的区别
    用友U8 |【固定资产】固定资产生成凭证,点完计提折旧后没有凭证出来
    用友U8 |【应收管理】为什么在应收模块弃审销售发票,弃审不成功?
    用友U8 | 【总账】应收核销明细报错,提示:将截断字符串或二进制数据
    用友U8 | 【网上银行】网上银行模块网银支付后台字段说明
    用友U8 | 【实施导航】引入交易单位档案,提示:账号不能为空!
    用友U8 | 【实施导航】已经创建的人员档案,如何更新维护银行信息
    用友U8 |【合同管理】合同结算单不能失效
  • 原文地址:https://www.cnblogs.com/baiyuxuan/p/14394222.html
Copyright © 2020-2023  润新知