分享

自制IOC容器(2)

 怡红公子0526 2021-05-04

本系列文章介绍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));

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多