问题描述
我正在开发一个具有这种结构的机器人程序(我将包括这些层只是为了笑) :
A层 -用于处理按钮,操纵杆等的GUI简单界面。将这些值转换为命令以发送到控制界面。
B层 - Control评估来自数据库的设备读/写条目和来自gui的命令请求,以便为数据库计算新的设备写入条目
层C -设备的数据库逻辑表示,创建从设备写入和读取的值的历史记录
D层 - 硬件与物理硬件对话。 将设备写入条目转换为命令并将它们发送到设备。 使用设备中的值更新数据库设备读取条目。
我想创建一个java应用程序,其中没有任何层能够调用上面或下面多层的函数。
是否有可能使用包隐私创建项目结构,或像工厂这样的模式使代码无法进入,比如说A层,从层D或C导入任何东西?
1楼
TL; DR没有一个灵丹妙药解决方案,但许多不同的工具可以利用
有许多不同的技术可以隔离软件应用程序的不同部分,但我认为没有任何一种解决方案可以解决所有问题。
一些构建系统可以限制目标之间的依赖关系(例如, 在构建目标上具有visibility
属性,可以防止一个目标依赖于另一个目标,即使它们通过Java的类可见性彼此可见),这些属性可以与Java一起使用内置的可见性。
例如:
// Foo.java
package com.yourcompany.foo;
public class Foo {}
// Build rule for Foo.java
java_library(
name = "Foo",
srcs = ["Foo.java"],
# Restricts visibility to this directory, even though
# the class visibility was "public"
visibility = ["//visibility:private"],
)
// Bar.java
package com.yourcompany.bar;
import com.yourcompany.foo.Bar; // prevented by build visibility system
public class Bar {
Foo foo = new Foo();
}
还可以使用接口来调解逻辑组件之间的所有交互并隐藏这些接口的实现(例如,仅通过服务注册表接口或通过接口依赖性注入来暴露实现)。 例如,使用 ,您可以为每个图层创建一个单独的 ,这样您就可以编写如下代码:
final class ControllerImpl implements Controller {
// Since "ControllerImpl" is instantiated / wired into the
// controller layer, the database dependency is available /
// exposed for injection within this layer. The access control is
// strictly performed by the way the dependencies are wired.
@Inject
public ControllerImpl(Database database) {
// ...
}
}
除上述内容外,您还可以使用依赖关系分析/依赖关系分析测试或提交挂钩来自动检测依赖关系规则违规(并根据它们触发错误/拒绝提交)。 例如,穷人的解决方案是简单地扫描每个文件的包声明及其import语句,然后使用一些启发式方法来检测错误的依赖关系。
另一种方法是将不同的组件捆绑在单独的JAR中,并使用自定义的加载它们,这样可以防止使用反射进行非法访问(否则会绕过任何程序结构)。
除了自动化方法,手动方法也有其价值。 手动方法包括在代码审查和审计期间执行的常规代码审查和策略。
简而言之,没有一个正确的答案。 根据这种分离的重要程度,有必要结合使用几种不同的方法。
2楼
这不是单独使用访问修饰符可以实现的。
您也无法通过(某种方式)控制import
...因为Java语言不会对导入施加任何(额外)限制。
( import
指令实际上只是语法糖,所以你不需要在任何地方使用完全限定的名称。)
那你还能做什么呢?
您可以尝试实现运行时限制,以防止错误的层访问工厂对象。 但这些限制很容易被故意或意外地破坏。
您可以使用某种内部“功能”或“凭据”机制,但很难看出如何防止凭据泄漏。 (如果凭据由安全管理器(见下文)管理,可能有效,但这会使问题变得更复杂。)
我认为你能做到的唯一方法是实现一个自定义的SecurityManager
,并在每次有可能的层交叉调用时实施安全检查。
例如,安全管理器检查调用堆栈以查找调用它的方法/类/包是可能的(尽管很昂贵)。
您还需要关闭某些可用于(通常)破坏安全管理器的反射操作。
基本上,除了内环之外的所有内容都需要被视为“不可信”的代码。
坦率地说,使用具有“防黑客”安全性的JVM来实现这种事情可能超出了凡人的能力。 (Sun / Oracle还没有成功......)
其他选择是:
依靠程序员纪律。
依靠对代码库的静态分析; 例如,通过记录访问规则的注释来辅助。 您需要编写自己的代码分析器才能执行此操作。
在层之间使用地址空间分离和占用空间小,安全的API。 (我们不再在这里讨论一个传统的JVM ......)