本系列文章介绍ByxContainer的实现思路。
ByxContainer是一个简单的轻量级IOC容器,具有以下特性:
- 使用JSON格式的配置文件
- 支持构造函数注入、静态工厂注入、实例工厂注入、属性注入、setter注入、条件注入
- 组件的延迟加载和单例组件
- 根据id注册、获取容器中的组件
ByxContainer的设计借鉴了ajoo大神的博客。
本篇文章介绍ByxContainer中的条件组件。
需求
假设现在有一个MyDao
接口,以及它的两个实现类:
public interface MyDao {...}
public class MyDaoImpl1 implements MyDao {...}
public class MyDaoImpl2 implements MyDao {...}
这里还有一个MyService
类,它需要使用一个MyDao
,所以需要通过构造函数传进来:
public class MyService
{
private final MyDao myDao;
public MyService(MyDao myDao)
{
this.myDao = myDao;
}
...
}
那么,应该如何创建一个MyService
对象呢?显然,可以使用下面两种方式之一:
MyService myService = new MyService(new MyDaoImpl1());
或
MyService myService = new MyService(new MyDaoImpl2());
假设现在需要根据某个标志位“动态地”决定应该用哪种方式来创建MyService
对象,类似下面这样:
boolean flag = ...;
MyService myService;
if (flag)
myService = new MyService(new MyDaoImpl1());
else
myService = new MyService(new MyDaoImpl2());
当然,这些逻辑最好不要直接写在代码里,因为这样会导致频繁修改代码和重新编译(当标志位条件改变时)。所以,我们考虑使用IOC容器来解决这个问题。
ConditionComponent实现类
根据上面的描述,可以知道,ByxContainer需要支持根据某个条件创建不同组件的功能,ConditionComponent
就是用来完成这项功能的:
public class ConditionComponent implements Component
{
private final Component predicate;
private final Component c1;
private final Component c2;
public ConditionComponent(Component predicate, Component c1, Component c2)
{
this.predicate = predicate;
this.c1 = c1;
this.c2 = c2;
}
@Override
public Object create()
{
Object p = predicate.create();
if (p instanceof Boolean && (boolean) p)
return c1.create();
return c2.create();
}
}
public interface Component
{
...
static Component condition(Component predicate, Component c1, Component c2)
{
return new ConditionComponent(predicate, c1, c2);
}
}
ConditionComponent
包含三个子组件:predicate
、c1
和c2
,表示的逻辑是:如果predicate
创建的对象为Boolean
类型并且值为true
,则返回c1
创建的对象,否则返回c2
创建的对象。
使用ConditionComponent
有了ConditionComponent
,上面的需求就可以这样来实现:
Component flag = value(true);
Component myService = condition(
flag,
constructor(MyService.class, constructor(MyDaoImpl1.class)),
constructor(MyService.class, constructor(MyDaoImpl2.class))
);
或者这样:
Component flag = value(true);
Component myDao = condition(
flag,
constructor(MyDaoImpl1.class),
constructor(MyDaoImpl2.class)
);
Component myService = constructor(MyService.class, myDao);
flag
也可以是一个更复杂的组件定义,例如判断某个标志字符串是否是某个特定值:
Component database = value("XXX");
Component flag = instanceFactory(database, "equals", value("mysql"));
Component myDao = condition(
flag,
constructor(MyDaoImpl1.class),
constructor(MyDaoImpl2.class)
);
Component myService = constructor(MyService.class, myDao);
等价于以下逻辑:
String database = "XXX";
boolean flag = database.equals("mysql");
MyDao myDao = flag ? new MyDaoImpl1() : new MyDaoImpl2();
MyService myService = new MyService(myDao);
这些配置都可以写在配置文件中,然后让ByxContainer读取配置文件来初始化容器,这样就可以在不修改代码的情况下动态改变MyService
的创建过程。