底层代码可见性控制
Java提供了private,public,protected和package private(无修饰符)这四种访问控制级别,不过这仅仅提供了底层的OO数据封装特性。包这个概念确实是起到了分割代码的作用,但是如果包中的代码需要对包外可见,那么必须设置为public(或者protected,如果是使用了继承的话)。 这样的话就可能出现一个问题:
首先大家看看下面的例子,其中有三个java文件:
org.serc.helloworld.Hello.java:定义了一个接口
package org.serc.helloworld; public interface Hello {
void sayHello();
}
org.serc.helloworld.impl.HelloImpl.java:实现了Hello接口
package org.serc.helloworld.impl; import org.serc.helloworld.Hello; public class HelloImpl implements Hello{
final String helloString; public HelloImpl(String helloString){
this.helloString=helloString;
} public void sayHello(){
System.out.println(this.helloString);
}
} org.serc.helloworld.main.Main.java
package org.serc.helloworld.main; import org.serc.helloworld.Hello;
import org.serc.helloworld.HelloImpl; public class Main{
final StringhelloString; public static void main(String[] args){
Hello hello = new HelloImpl(“Hello,SERC!”);
hello.sayHello();
}
}
这三个文件分别在不同的包中。按理说,HelloImpl这个实现细节是不应该暴露给其他包的,但是从Main.java的main方法中我们可以明显的看出,为了创建Hello 的实例,我们不得不引入HelloImpl类,但是HelloImpl作为接口的实现细节,是不应该暴露给使用者的,这违反了封装的原则,显然不太好。
但是,如果我们不想让HelloImpl暴露出来的话,就需要做额外的工作来保证“既隐藏了实现细节,又能简单的创建一个实现了Hello接口的实例”。达到这一目的的方法不止一种(比如工厂模式),有些至今也是很常用的,但是这增加了与应用本身功能无关的多余工作,想想如果你每次想开发一个应用都要为了达到上述目的而做出多余工作,不得不说是有点繁琐的,所以这可以说是Java的一大局限。
2.2.2 classpath的局限
我们在classpath中加入jar包的时候,只是简单的给出文件路径,而这个jar包的版本和一致性,它所依赖的jar包是什么,我们都无法在classpath中明确的设置或是从classpath中看出这些属性。
并且classpath中的jar包是按序加载的,例如:
classpath=c:\servlet2.2\servlet.jar;c:\servlet2.3\servlet.jar,
那么在实际应用的过程中,Java让你使用的是servlet2.2,而不是servlet2.3。这种情况下我们还能看出来使用的是哪个版本,如果在大型系统中大家分开开发的时候各用各的servlet包,并且版本号不一样,那么在最后将开发结果合并的时候,到时候用的是哪个版本的servlet包就很难搞清楚了,也就说不可控性是比较强的。
即使classpath能注意到版本的问题,也没法精确指出依赖。试着回想你在设置classpath的过程中出现过情况:在你以为classpath已经设置完毕以后,你尝试启动程序,结果虚拟机抛出异常告诉你缺包,然后你再加上你觉得缺少的那些包,然后再启动程序,如此反复直到虚拟机不运行到缺包异常为止。
OSGi对这些局限性的改善
对于上一小节提到的Java的局限,在OSGi中都得到了很好的解决。
· 包的可见性:OSGi通过引入包的可见性机制,能够完全控制一个包中的代码对哪些模块可见,而不仅仅局限于无差别的可见性,从而完善了Java的代码访问控制机制。
· 包的版本: OSGi通过为包增加版本信息,可以精确控制代码的依赖,保证代码的版本一致性,弥补了classpath的缺点。