控制反转缺少的部分是应用程序层不直接调用构造函数。它使用工厂(IoC 容器)来填充构造函数参数。
无论您使用什么工具,guice / spring / picocontainer / singleton-factories,您的应用程序代码应该类似于:
@Controller
class MyController {
@Resource // Some container knows about this annotation and wires you in
MyBusinessLogic myBusinessLogic;
@RequestMethod("/foo/bar.*")
public MyWebResponse doService(Response resp, long id, String val) {
boolean worked = myBusinessLogic.manipulatevalue(id, val);
return new MyWebResponse(worked);
}
}
请注意,myBusinessLogic 可以通过多种方式注册 - java 的 @Resource、MyBusinessLogicFactory.getMyBusinessLogic()、guice.get(MyBusinessLogic.class) 等。
一个穷人的解决方案是:
package foo;
class MyBusinessLogicFactory {
static volatile MyBusinessLogic instance; // package-scoped so unit tests can override
public static MyBusinessLogic getInstance() {
if (instance == null) {
synchronized(MyBusinessLogicFactory.class) {
instance = new MyBusinessLogic(MyDatabaseLayerFactory.getInstance());
}
}
return instance;
}
}
// repeat with MyDatabaseLayerFactory
请注意,强烈建议不要使用上述单例模型,因为它没有作用域。你可以把上面的内容包装在一个上下文中——比如
class Context {
Map<Class,Object> class2Instance = new ConcurrentHashMap<>();
public <T> T getInstance(Class<T> clazz) {
Object o = class2Instance.get(clazz);
if (o == null) {
synchronized(this) {
o = class2Instance.get(clazz);
if (o != null) return (T)o;
o = transitivelyLoadInstance(clazz); // details not shown
for (Class c : loadClassTree(clazz)) { // details not shown
class2Instance.put(c, o);
}
}
}
return (T)o;
}
...
}
但是到那时,picocontainer、guice 和 spring 可以更好地解决上述 SOOO 的复杂性。
此外,像 spring 这样尊重 java 6 注释的东西意味着你可以做除了构造函数注入之外的事情,如果你有多个相同基本数据类型的配置项(例如字符串),这非常有用。