前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >ByxContainer——轻量级IOC容器

ByxContainer——轻量级IOC容器

原创
作者头像
byx
修改于 2021-02-14 07:21:32
修改于 2021-02-14 07:21:32
7860
举报
文章被收录于专栏:开发记录开发记录

ByxContainer是一个用Java编写的轻量级IOC容器,具有以下特性:

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

项目地址:https://github.com/byx2000/ByxContainer

配置文件

ByxContainer使用JSON作为配置文件的格式。你可以将配置文件命名为任何名字,放在任何你喜欢的路径下。

ByxContainer配置文件的基本框架如下:

代码语言:txt
AI代码解释
复制
{
    "typeAlias":
    {
        // 配置components中使用的类型别名
    },
    "components":
    {
        // 定义容器中的所有组件
    }
}

类型

说明

是否必须

typeAlias

对象

components中使用的类型别名

components

对象

定义容器中的所有组件

关于类型别名的使用,请参考这里

组件

ByxContainer使用组件来管理系统中的所有对象。组件代表了系统中的一个对象,并封装了该对象的创建过程。每个组件都有一个唯一的key,当我们向ByxContainer注册一个组件时,需要为组件指定一个唯一的key,同时还要定义组件的创建过程和依赖关系。

ByxContainer的所有组件都定义在components中。组件以键值对的形式写在components对象中,键就是组件的id,值就是组件的定义。

有很多种方式来定义组件,详见下文。

代码语言:txt
AI代码解释
复制
{
    "components":
    {
        "c1": ... // 组件c1的定义
        "c2": ... // 组件c2的定义
        "c3": ... // 组件c3的定义
        ...
    }
}

加载与使用ByxContainer

在应用程序的初始化代码中,按照以下方式加载配置文件,并初始化容器:

代码语言:txt
AI代码解释
复制
InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("配置文件路径");
ContainerFactory factory = new JsonContainerFactory(inputStream);
Container container = factory.create();

容器初始化完成后,需要使用注册组件时的id来获取某个特定组件:

代码语言:txt
AI代码解释
复制
// 获取id为c1的组件
SomeType c1 = container.getComponent("c1");

组件的创建方式

在ByxContainer中,支持以下四种组件创建方式:

  • 常数
  • 构造函数(constructor)
  • 静态工厂(static factory)
  • 实例工厂(instance factor)

常数

ByxContainer支持JSON中的所有基本类型常数,包括整数、浮点数、字符串、布尔、null,它们在Java中对应的类型分别是:intdoubleStringbooleannull

代码语言:txt
AI代码解释
复制
{
    "components":
    {
        "intValue": 123,
        "doubleValue": 3.14,
        "stringValue": "hello",
        "trueValue": true,
        "falseValue": false,
        "nullValue": null
    }
}

以上配置声明了几种不同类型的常数组件。

构造函数

配置构造函数创建方式时,需要指定全限定类名,如果要传递参数则指定参数数组。

类型

说明

是否必须

class

字符串

全限定类名

parameter

数组

构造函数参数

代码语言:txt
AI代码解释
复制
{
    "components":
    {
        "a1":
        {
            "class": "byx.test.A"
        },
        "a2":
        {
            "class": "byx.test.A",
            "parameters": ["hello", 123]
        }
    }
}

以上配置等价于以下Java代码:

代码语言:txt
AI代码解释
复制
A a1 = new A();
A a2 = new A("hello", 123);

注:parameters数组中的元素既可以是常数,也可以嵌套其它组件的定义:

代码语言:txt
AI代码解释
复制
{
    "components":
    {
        "a3":
        {
            "class": "byx.test.A",
            "parameters": 
            [
                "hello", 
                123, 
                {"class": "byx.test.B"}
            ]
        }
    }
}

以上配置等价于以下Java代码:

代码语言:txt
AI代码解释
复制
A a3 = new A("hello", 123, new B());

静态工厂

配置静态工厂创建方式时,需要指定工厂类的类名和工厂函数名,如果要传递参数则指定参数数组。

类型

说明

是否必须

factory

字符串

工厂类全限定类名

method

字符串

工厂类静态方法名

parameter

数组

方法参数

代码语言:txt
AI代码解释
复制
{
    "components":
    {
        "a1":
        {
            "factory": "byx.test.Factory",
            "method": "createDefault"
        },
        "a2":
        {
            "factory": "byx.test.Factory",
            "method": "create",
            "parameters": ["hello", 123]
        }
    }
}

以上配置等价于以下Java代码:

代码语言:txt
AI代码解释
复制
A a1 = Factory.createDefault();
A a2 = Factory.create("hello", 123);

实例工厂

配置实例工厂创建方式时,需要指定实例组件和工厂函数名,如果要传递参数则指定参数数组。

类型

说明

是否必须

instance

对象或基本类型

实例组件定义

method

字符串

实例工厂方法名

parameter

数组

方法参数

代码语言:txt
AI代码解释
复制
{
    "components":
    {
        "a1":
        {
            "instance": {"class": "byx.test.B"},
            "method": "createDefault"
        },
        "a2":
        {
            "instance": {"class": "byx.test.B"},
            "method": "create",
            "parameters": ["hello", 123]
        }
    }
}

以上配置等价于以下Java代码:

代码语言:txt
AI代码解释
复制
A a1 = new B().createDefault();
A a2 = new B().create("hello", 123);

组件的依赖声明

声明完组件的创建方式后,还可继续声明组件的依赖。ByxContainer支持以下两种依赖声明方式:

  • 属性设置
  • setter方法

属性设置

ByxContainer支持在组件创建完成之后对组件的属性进行设置。这里的属性等同于JavaBean的Property。

类型

说明

是否必须

properties

对象

(属性名, 属性值)键值对

代码语言:txt
AI代码解释
复制
{
    "components":
    {
        "a": 
        {
            "class": "byx.test.A",
            "properties":
            {
                "id": 1001,
                "name": "byx",
                "score": 97.5
            }
        }
    }
}

以上配置等价于以下Java代码:

代码语言:txt
AI代码解释
复制
A a = new A();
a.setId(1001);
a.setName("byx");
a.setScore(97.5);

注:properties中的属性值既可以是常数,也可以嵌套其它组件的定义:

代码语言:txt
AI代码解释
复制
{
    "components":
    {
        "a": 
        {
            "class": "byx.test.A",
            "properties":
            {
                "id": 1001,
                "b": {"class": "byx.test.B"}
            }
        }
    }
}

以上配置等价于以下Java代码:

代码语言:txt
AI代码解释
复制
A a = new A();
a.setId(1001);
a.setB(new B());

setter方法

ByxContainer支持在组件创建完成之后调用组件的setter方法,并传递参数。

类型

说明

是否必须

setters

对象

(setter方法名, 方法参数)键值对

代码语言:txt
AI代码解释
复制
{
    "components":
    {
        "a": 
        {
            "class": "byx.test.A",
            "setters":
            {
                "setId": [1001],
                "setNameAndScore": ["byx", 97.5]
            }
        }
    }
}

以上配置等价于以下Java代码:

代码语言:txt
AI代码解释
复制
A a = new A();
a.setId(1001);
a.setNameAndScore("byx", 97.5);

其它组件定义

ByxContainer还支持以下特殊组件定义:

  • 集合组件
  • 引用组件
  • 条件组件

集合组件

ByxContainer支持ListSetMap这三种集合组件。

下面是这三种集合组件的定义:

代码语言:txt
AI代码解释
复制
{
    "components":
    {
        "c1": {"list": [1, 2, 3]},
        "c2": {"set": [4, 5, 6]},
        "c3": 
        {
            "map":
            {
                "k1": 100,
                "k2": 200,
                "k3": 300
            }
        },
        "c4":
        {
            "map":
            [
                {"key": 400, "value": "v1"},
                {"key": 500, "value": "v2"},
                {"key": 600, "value": "v3"}
            ]
        }
    }
}

以上配置等价于以下Java代码:

代码语言:txt
AI代码解释
复制
List<Object> c1 = List.of(1, 2, 3);
Set<Object> c2 = Set.of(4, 5, 6);
Map<Object> c3 = Map.of("k1", 100, "k2", 200, "k3", 300);
Map<Object> c4 = Map.of(400, "v1", 500, "v2", 600, "v3");

注:

  • 集合组件的元素既可以是常数,也可以嵌套其它组件的定义
  • c3c4Map组件的两种不同定义方式。第一种只支持String类型的key,第二种支持任意数据类型的key

引用组件

ByxContainer既支持对全局组件的引用,也支持对局部组件的引用。

类型

说明

是否必须

ref

字符串

引用的组件key

代码语言:txt
AI代码解释
复制
{
    "components":
    {
        "a1": {"class": "byx.test.A"},
        "a2": {"ref": "a1"}
    }
}

以上配置等价于以下Java代码:

代码语言:txt
AI代码解释
复制
A a1 = new A();
A a2 = a1;

注:关于局部组件,请参考这里

条件组件

ByxContainer支持根据不同条件产生不同的组件定义。

类型

说明

是否必须

if

对象或基本类型

作为条件的组件定义

then

对象或基本类型

条件为真时产生的组件

else

对象或基本类型

条件为假时产生的组件

代码语言:txt
AI代码解释
复制
{
    "components":
    {
        "flag": true,
        "c":
        {
            "if": {"ref": "flag"},
            "then": {"class": "com.byx.A"},
            "else": {"class": "com.byx.B"}
        }
    }
}

以上配置等价于以下Java代码:

代码语言:txt
AI代码解释
复制
boolean flag = true;
Object c;
if (flag)
    c = new A();
else
    c = new B();

注:当if设置成一个组件定义时,容器将把该组件创建的对象解释成boolean值,如果该组件创建的对象不是boolean类型,则当成false处理。

高级特性

ByxContainer还支持以下高级特性:

  • 局部组件
  • 单例组件
  • 自定义组件
  • 自定义转换器
  • 类型别名

局部组件

有时候,某些组件的创建过程中需要用到其它的组件,如下面这个例子:

代码语言:txt
AI代码解释
复制
A a = new A("hello", "123", new B());

我们可以按照如下方式来配置:

代码语言:txt
AI代码解释
复制
{
    "components":
    {
        "msg": "hello",
        "val": 123,
        "b": {"class": "byx.test.B"},
        "a":
        {
            "class": "byx.test.A",
            "parameters": [{"ref": "msg"}, {"ref": "val"}, {"ref": "b"}]
        }
    }
}

A的构造函数中传递的三个参数分别属于三个不同的组件,但是这三个组件仅仅用于构建A对象,我们不希望这些临时组件占用容器中的一个key。当然,可以使用一种“就地初始化”的写法:

代码语言:txt
AI代码解释
复制
{
    "components":
    {
        "a":
        {
            "class": "byx.test.A",
            "parameters": 
            [
                "hello", 
                123, 
                {"class": "byx.test.B"}
            ]
        }
    }
}

这种写法可以隐藏临时组件,但是当组件创建过程比较复杂的时候,这种写法会形成深层次的嵌套结构,导致可读性变差。而且,这种写法也不能很好地应对某个临时组件被多次使用的情况,例如:

代码语言:txt
AI代码解释
复制
B b = new B();
A a = new A(b, b);

以上问题可以使用ByxContainer提供的局部组件来解决:

代码语言:txt
AI代码解释
复制
{
    "components":
    {
        "a":
        {
            "class": "byx.test.A",
            "parameters": [{"ref": "msg"}, {"ref": "val"}, {"ref": "b"}],
            "locals":
            {
                "msg": "hello",
                "val": 123,
                "b": {"class": "byx.test.B"}
            }
        }
    }
}

a组件定义的内部有一个locals块,里面包含了属于a的局部组件定义,这些局部组件不会占用容器的全局key,它只能在组件a的作用域内被引用。如果局部组件的key与全局组件的key相同,则局部组件将会覆盖全局组件。

单例组件

在默认情况下,ByxContainer中的所有组件都是单例的,这意味着组件最多只会被创建一次。当你多次获取同一个组件时,所得到的是同一个对象实例:

代码语言:txt
AI代码解释
复制
Object c1 = container.getComponent("c1");
Object c2 = container.getComponent("c2");
// c1与c2指向同一个对象

如果想要改变这个默认行为,只需要在组件定义中加入如下配置:

代码语言:txt
AI代码解释
复制
"singleton": false

自定义组件

ByxContainer支持用户定义自己的组件,并在配置文件中使用。

用户可以通过实现byx.container.component.Component接口来创建自己的组件:

代码语言:txt
AI代码解释
复制
public interface Component
{
    Object create();
}

Component接口中的create方法封装了对象创建的细节:

代码语言:txt
AI代码解释
复制
public class MyComponent implements Component
{
    private final String msg;
    private final int val;

    public MyComponent()
    {
        MyComponent("hello", 123);
    }

    public MyComponent(String msg, int val)
    {
        this.msg = msg;
        this.val = val;
    }

    @Override
    public Object create()
    {
        return new A(msg, val);
    }
}

在配置文件中使用自定义组件,需要指定自定义组件的全限定类名,如果想传递构造函数参数,则需指定参数列表。

类型

说明

是否必须

custom

字符串

自定义组件的全限定类名

parameters

数组

传递给自定义组件的构造函数参数

代码语言:txt
AI代码解释
复制
{
    "components":
    {
        "a1":
        {
            "custom": "byx.test.MyComponent"
        },
        "a2":
        {
            "custom": "byx.test.MyComponent",
            "parameters": ["hi", 456]
        }
    }
}

以上配置等价于以下Java代码:

代码语言:txt
AI代码解释
复制
A a1 = new MyComponent().create();
A a2 = new MyComponent("hi", 456).create();

自定义转换器

ByxContainer支持对组件创建出来的对象进行进一步转换。用户需要通过实现byx.container.component.Mapper接口来创建自定义转换器:

代码语言:txt
AI代码解释
复制
public interface Mapper
{
    Object map(Object obj);
}

Mapper接口中的map方法封装了对组件创建结果的处理:

代码语言:txt
AI代码解释
复制
public class MyMapper implements Mapper
{
    private final int val;

    public MyMapper()
    {
        MyMapper(1);
    }

    public MyMapper(int val)
    {
        this.val = val;
    }

    @Override
    public Object map(Object obj)
    {
        if (obj instanceof Integer)
            return ((int) obj) + val;
        return obj;
    }
}

在配置文件中使用自定义转换器,需要在组件定义中指定mapper属性:

  • mapper属性为字符串时,该字符串表示自定义mapper的全限定类名,同时用默认构造函数来创建该mapper
  • mapper属性为对象时,对象中必须包含classparameters属性,其中class表示mapper的全限定类名,parameters表示创建mapper时向构造函数传递的参数
代码语言:txt
AI代码解释
复制
{
    "components":
    {
        "c1":
        {
            "class": "java.lang.Integer",
            "parameters": [123],
            "mapper": "byx.test.MyMapper"
        },
        "c1":
        {
            "class": "java.lang.Integer",
            "parameters": [123],
            "mapper":
            {
                "class": "byx.test.MyMapper",
                "parameters": [100]
            }
        }
    }
}

以上配置等价于以下Java代码:

代码语言:txt
AI代码解释
复制
int c1 = (int) new MyMapper().map(container.getComponent("c1"));
int c2 = (int) new MyMapper(100).map(container.getComponent("c2"));

类型别名

为了避免键入大量冗长的全限定类名,ByxContainer提供了类型别名功能,只需在容器定义最外层添加typeAlias配置:

代码语言:txt
AI代码解释
复制
{
    "typeAlias":
    {
        "A": "byx.test.A",
        "B": "byx.test.B"
    },
    "components":
    {
        "a":
        {
            "class": "A",
            "parameters": ["hello", 123]
        },
        "b":
        {
            "class": "B"
        }
    }
}

以上配置等价于以下Java代码:

代码语言:txt
AI代码解释
复制
A a = new A("hello", 123);
B b = new B();

注:components中所有出现类名的地方都可以使用typeAlias中定义的别名,包括classfactorycustom等等。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Scalaz(26)- Lens: 函数式不可变对象数据操作方式
  scala中的case class是一种特殊的对象:由编译器(compiler)自动生成字段的getter和setter。如下面的例子: 1 case class City(name:Strin
用户1150956
2018/01/05
8200
Kotlin | 一份10万字的随查笔记,妈妈再也不担心我不会Ctrl+F了
关于Kotlin相关记录,如果有意查看我的github,其超10w字(其中8w是代码吗,哈哈)。。
Petterp
2022/03/18
2.8K0
Kotlin | 一份10万字的随查笔记,妈妈再也不担心我不会Ctrl+F了
「SF-PLF」1 Equiv
Some module (e.g.Map) not found either maunally make map.vo or proof general can solve that.
零式的天空
2022/03/02
4810
《Kotin 极简教程》第9章 轻量级线程:协程(1)
在常用的并发模型中,多进程、多线程、分布式是最普遍的,不过近些年来逐渐有一些语言以first-class或者library的形式提供对基于协程的并发模型的支持。其中比较典型的有Scheme、Lua、Python、Perl、Go等以first-class的方式提供对协程的支持。
一个会写诗的程序员
2018/08/17
1.2K0
Spring框架(二) spring IOC框架的使用流程,springIOC创建对象的3个方式
javaee的OA项目(一)项目搭建,数据库连接,用到的技术是mysql,mybatis,servlet,jsp,没有使用到具体的框架哦,是一个原生项目 总结:
一写代码就开心
2021/07/08
4990
轻量级Golang IoC容器——iocgo
习惯于Java或者C#开发的人应该对控制反转与依赖注入应该再熟悉不过了。在Java平台有鼎鼎大名的Spring框架,在C#平台有Autofac,Unity,Windsor等,我当年C#开发时用的最多的就是Windsor。使用IoC容器是面向对象开发中非常方便的解耦模块之间的依赖的方法。各个模块之间不依赖于实现,而是依赖于接口,然后在构造函数或者属性或者方法中注入特定的实现,方便了各个模块的拆分以及模块的独立单元测试。 在[长安链]的设计中,各个模块可以灵活组装,模块之间的依赖基于protocol中定义的接口,每个接口有一个或者多个官方实现,当然第三方也可以提供该接口更多的实现。为了实现更灵活的组装各个模块,管理各个模块的依赖关系,于是我写了iocgo这个轻量级的golang版Ioc容器。
深蓝studyzy
2022/06/16
8270
2021-04-09:rand指针是单链表节点结构中新增的指针,rand可能指向链表中
2021-04-09:rand指针是单链表节点结构中新增的指针,rand可能指向链表中的任意一个节点,也可能指向null。给定一个由Node节点类型组成的无环单链表的头节点 head,请实现一个函数完成这个链表的复制,并返回复制的新链表的头节点。 【要求】时间复杂度O(N),额外空间复杂度O(1) 。
福大大架构师每日一题
2021/04/09
4930
2021-04-09:rand指针是单链表节点结构中新增的指针,rand可能指向链表中
【SSM_01】Spring-IOC
一、spring 概述 1. 什么是 spring * spring 是分层的 java ee 应用,全栈(full-stact)轻量级开源框架,主要有 IOC(反转控制) 、 AOP(面向切面编程) * 反转控制:将对象的创建全交给 spring,有利于解耦,降低代码的依赖关系 2. 使用方法 ① 导包 需要导入 4 个jar包 【spring-aop 、spring-beans 、spring-core 、spring-expression】 导入 spring-context 相当
用户8250147
2021/02/04
2160
mybatis教程2(配置文件)
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置(settings)和属性(properties)信息。文档的顶层结构如下:
用户4919348
2019/04/02
6830
mybatis教程2(配置文件)
【C++初阶】想要编译器为你干活吗?来试试模板吧(模板初阶)
那只能当个CV工程师了,然后再修修改改,但是如果有很多类型呢?后期又需要改动该怎么办呢?一个一个函数的改吗?多个功能相同的函数,又会使程序可读性降低,代码冗余。
aosei
2024/01/23
1280
【C++初阶】想要编译器为你干活吗?来试试模板吧(模板初阶)
Spring-AOP @AspectJ切点函数之@within()和@target
除了@annotation和@args外,还有另外两个用于注解的切点函数,分别是@target和@within.
小小工匠
2021/08/17
2.1K0
Spring IoC容器与Bean管理
从本节开始,我将要学习作为java高级阶段最最最最重要的一个框架体系,名为Spring。Spring是整个Java生态中最重要的一环。因为我也是初学,所以我的概括也不一定全面和精炼。写这一章只是为自己以后复习。
害恶细君
2022/11/22
7140
Spring IoC容器与Bean管理
Kotlin | 2.Kotlin基础
在定义了val变量的代码块执行期间,val变量只能进行唯一一次初始化。但是,如果编译器能确保只有唯一一条初始化语句被执行,可以根据条件使用不同的值来初始化它:
Jingbin
2019/03/29
7500
Kotlin | 2.Kotlin基础
对控制反转理解不深?带你手写一个基于注解的IOC容器 加深对spring底层代码的理解
提到spring 不可避免的就是两个核心组件 IOC 和 AOP 这里我们主要实现的是 IOC
冷环渊
2022/03/30
3480
对控制反转理解不深?带你手写一个基于注解的IOC容器 加深对spring底层代码的理解
Python面向对象1:基础介绍+封装特征
目前有三种编程方式: 面向过程:根据业务逻辑从上到下写垒代码 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可 面向对象:对函数进行分类和封装,让开发“更快更好更强...” java只支持面向对象编程;python支持函数式编程、面向对象编程 1、面向对象其实就是对类和对象的使用 案例1:函数式编程和面向对象编程初步比较 #函数式编程执行方法:【执行函数】 #面向对象编程:【创建对象】【通过对象执行方法】 创建对象及使用的步骤: a创建类 class类名():    def 方法名(se
企鹅号小编
2018/01/29
5010
Java中的java.lang.Class API 详解
Class是一个位于java.lang包下面的一个类,在Java中每个类实例都有对应的Class对象。类对象是由Java虚拟机(JVM)自动构造的。
java干货
2021/02/15
1.2K0
在java中boolean类型占多少字节?
在一些资料上都有上述介绍。对于其他类型都好理解,唯独对于boolean,长度一直没有很好的说明。我们来看看jvm中是如何实现的。
冬天里的懒猫
2020/09/11
2.8K0
java中的compareto方法的详细介绍
1.返回参与比较的前后两个字符串的asc码的差值,如果两个字符串首字母不同,则该方法返回首字母的asc码的差值
全栈程序员站长
2022/09/07
1.6K0
聊一聊 Spring 6 容器 IOC
IoC 是 Inversion of Control 的简写,译为“控制反转”,它不是一门技术,而是一种设计思想,是一个重要的面向对象编程法则,能够指导我们如何设计出松耦合、更优良的程序。
小熊学Java
2023/09/06
9840
聊一聊 Spring 6 容器 IOC
「SF-LC」12 Imp
A weird convention through out all IMP is:
零式的天空
2022/03/14
1.7K0
推荐阅读
相关推荐
Scalaz(26)- Lens: 函数式不可变对象数据操作方式
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文