精华内容
下载资源
问答
  • 基于OSGi的Virgo环境搭建二-集成Snapshttps://blog.csdn.net/likeaboy_fire/article/details/26138611Virgo Tomcat Server简析http://www.ha97.com/5152.html

    基于OSGi的Virgo环境搭建二-集成Snaps

    https://blog.csdn.net/likeaboy_fire/article/details/26138611

    Virgo Tomcat Server简析

    http://www.ha97.com/5152.html


    展开全文
  • Java动态模块化运行原理与实践 OSGI

    千次阅读 2011-09-28 00:28:15
    在Java模块化编程中,我们可以使用服务进行Bundle间的通信,通过服务可以让模块系统动态化,这样就能应对在运行时服务的变化问题。 我们之前曾了解过面向Java EE 6平台的上下文和依赖性注入和OSGi依赖性...
     
    
    • 在Java模块化编程中,我们可以使用服务进行Bundle间的通信,通过服务可以让模块系统动态化,这样就能应对在运行时服务的变化问题。

    我们之前曾了解过面向Java EE 6平台的上下文和依赖性注入OSGi依赖性管理,比如Bundle的访问域等内容。其实,标准Java代码和模块化Java代码的区别之一就是依赖在运行时是如何绑定的。在本篇文章中,我们将详细讨论模块化Java中的动态模块化,包括对Bundle ClassPath、类的垃圾回收以及查找绑定等。

    Bundle ClassPath

    对于一个普通Java程序,只有一个classpath——启动应用程序所使用的那个。该路径通常是在命令行中用-classpath选项指定的,或者通 过CLASSPATH 环境变量来设定。Java类装载器在运行时解析类的时候会扫描此路径,无论这一过程是静态地(已编译进代码)还是动态地(使用反射及 class.forName())。然而,在运行时也可以使用多个类加载器;像Jetty和Tomcat这样的Web应用引擎都是使用多个类加载器,以便支持应用热部署。

    在OSGi中,每个bundle都有其自己的类加载器。需要被其他bundle访问的类则被委派(delegated)给这些其他bundle的类装载器。因此,尽管在传统应用中,来自logging类库、client和server JAR中的类都是由同一个类加载器加载的,但在OSGi模块系统中,他们都是由自己的类加载器加载的。

    结果是,一个VM中有可能有多个类加载器,其中可能存在名字相同的不同Class的对象。也就是说,在同一个VM中,一个叫做 com.infoq.example.App的类,其不同版本可以由com.infoq.example bundle的第1版和第2版同时输出。Client bundle版本1使用该类的第1版,而client版本2使用该类的第2版。这在模块化系统中相当普遍;在同一个VM中,有些代码可能需要装载一个类库 的老版本,同时更新点的代码(在另一个bundle中)却需要该类库的新版本。好在OSGi为你管理起这种依赖传递,确保不再出现不兼容类引发的问题。

    类的垃圾回收

    每个类都有一个对其类装载器的引用。因此如果想要从不同的bundle访问这些类,不但要有对该类实例的引用,而且还要有对该类的类装载器的引用。当一个bundle持有另一个bundle的类时,它也会将该bundle固定在内存中。在前篇文章的例子中,client被固定到该server上。

    在静态世界里,无论你是否把自己的类固定到其他类(或类库)都无所谓;因为不会有什么变化。可是,在动态世界里,在运行时将类库或工具替换成新版本就有可 能了。这听起来可能有点复杂,但是在可热部署应用的Web应用引擎早期就出现了(如Tomcat,最早发布于1999年)。每个Web应用程序都绑定到 Servlet API的某个版本上,当其停止时,装载该Web应用的类加载器也就废弃掉了。当Web应用重新被部署时,又创建了一个新的类加载器,新版类就由其装载。只要servlet引擎没有保持对老版应用的引用,这些类就像其他Java对象一样被垃圾回收器回收了。

    并不是所有的类库都能意识到Java代码中可能存在类泄漏的问题,就像是内存泄漏。一个典型的例子就是Log4J的addAppender()调用,一旦其执行了,将会把你的类绑定在Log4J bundle的生命周期上。即使你的bundle停止了,Log4J仍将维对appender的引用,并继续发送日志事件(除非该bundle在停止时恰当地调用了removeAppender()方法)。

    查找和绑定

    为了成为动态,我们需要有一个能查找服务的机制,而不是持久持有他们(以免bundle停止)。这是通过使用简单Java接口和POJO来实现的,也就是大家所熟知的services(注意他们与WS-DeathStar或其他任何XML底层架构都没有关系;他们就是普通Java对象——Plain Old Java Objects)。

    典型工厂实现方式是使用从properties文件中获取的某种形式的类名,然后用Class.forName()来实例化相应的类,OSGi则不同,它 维护了一个‘服务注册器’,其实这是一个包含了类名和服务的映射列表。这样,OSGi系统就可以使用 context.getService(getServiceReference("java.sql.Driver")),而不是 class.forName("com.example.JDBCDriver")来获取一个JDBC驱动器。这就把client代码解放出来了,它不需 知道任何特定客户端实现;相反,它可以在运行时绑定任何可用驱动程序。移植到不同的数据库服务器也就非常简单了,只需停止一个模块并启动一个新模 块;client甚至不需要重新启动,也不需要改变任何配置。

    这样做是因为client只需知道其所需的服务的API(基本上都是接口,尽管OSGi规范允许使用其他类)。在上述情况中,接口名是 java.sql.Driver;返回的接口实例是具体的数据库实现(不必了解是哪些类,编码在那里)。此外,如果服务不可用(数据库不存在,或数据库临 时停掉了),那么这个方法会返回null以说明该服务不可用。

    为了完全动态,返回结果不应被缓存。换句话说,每当需要服务的时候,需要重新调用getService。框架会在底层执行缓存操作,因此不存在太大的性能 问题。但重要的是,它允许数据库服务在线被替换成新的服务,如果没有缓存代码,那么下次调用时,client将透明地绑定到新服务上。

    付诸实施

    为了证明这一点,我们将创建一个用于缩写URL的OSGi服务。其思路是服务接收一个长URL,如http://www.example.com/articles/modular-java-what-is-it,将其转换为短点的URL,如http://tr.im/EyH1。该服务也可以被广泛应用在Twitter这样的站点上,还可以用它来把长URL转成短的这样便签背后也写得下。甚至像《新科学家》和《Macworld》这样的杂志也是用这些短URL来印刷媒体链接的。

    为了实现该服务,我们需要:

    ◆一个缩写服务的接口
    ◆一个注册为缩写实现的bundle
    ◆一个验证用client

    尽管并没有禁止把这些东西都放在同一个bundle中,但是我们还是把他们分别放在不同的bundle里。(即便他们在一个bundle中,最好也让bundle通过服务来通讯,就好像他们处于不同的bundle一样;这样他们就可以方便地与其他服务提供者进行集成。

    把缩写服务接口与其实现(或client)分开放在单独bundle中是很重要的。该接口代表了client和server之间的‘共享代码’,这样,该 接口在每个bundle中都会加载。正因如此,每个bundle实际上都被固定到了该接口特定版本上,所有服务都有共同的生命周期,将接口放在单独 bundle中(在整个OSGi VM生命周期中都在运行),我们的client就可以自由变化。如果我们把该接口放在某个服务实现的bundle中,那么该服务发生变化后我们就不能重新 连接到client上了。

    shorten接口的manifest和实现如下:

    1. Bundle-ManifestVersion: 2 
    2. Bundle-Name: Shorten  
    3. Bundle-SymbolicName: com.infoq.shorten  
    4. Bundle-Version: 1.0.0 
    5. Export-Package: com.infoq.shorten  
    6. ---   
    7. package com.infoq.shorten;  
    8.  
    9. public interface IShorten {  
    10.     public String shorten(String url) throws IOException;  

    上面的例子建立了一个拥有单一接口(com.infoq.shorten.IShorten)的bundle(com.infoq.shorten),并将其输出给client。参数是一个URL,返回一个唯一的压缩版URL。

    和接口定义相比,实现就相对有趣一些了。尽管最近缩写名称的应用开始多起来了,但是所有这些应用的祖师爷都是 TinyURL.com。(具有讽刺意味的是,http://tinyurl.com实际上可以被压缩的更短http://ow.ly/AvnC)。如今比较流行有:ow.ly、bit.ly、tr.im等等。这里并不是对这些服务全面介绍,也不是为其背书,我们的实现也可以使用其他同类服务。本文之所以使用TinyURL和Tr.im,是由于他们都可以匿名基于GET提交,易于实现,除此之外没有其他原因。

    每种实现实际上都非常小;都以URL为参数(要缩写的东西)并返回新的压缩过的文本:

    1. package com.infoq.shorten.tinyurl;  
    2. import java.io.BufferedReader;  
    3. import java.io.InputStreamReader;  
    4. import java.net.URL;  
    5. import com.infoq.shorten.IShorten;  
    6.  
    7. public class TinyURL implements IShorten {  
    8.     private static final String lookup =   
    9.         "http://tinyurl.com/api-create.php?url=";  
    10.     public String shorten(String url) throws IOException {  
    11.         String line = new BufferedReader(  
    12.             new InputStreamReader(  
    13.                 new URL(lookup + url).openStream())).readLine();  
    14.         if(line == null)  
    15.             throw new IllegalArgumentException(   
    16.                 "Could not shorten " + url);  
    17.         return line;  
    18.     }  

    Tr.im的实现类似,只需用http://api.tr.im/v1/trim_simple?url=替代lookup的值即可。这两种实现的源代码分别在com.infoq.shorten.tinyurl和com.infoq.shorten.trim bundle里。

    那么,完成缩写服务的实现后,我们如何让其他程序访问它呢?为此,我们需要把实现注册为OSGi框架的服务。BundleContext类的registerService(class,instance,properties)方法可以让我们定义一个服务以供后用,该方法通常在bundle的start()调用期间被调用。如上篇文章所讲,我们必须定义一个BundleActivator。实现该类后,我们还要把Bundle-Activator放在MANIFEST.MF里以便找到该实现。代码如下:

    1. Manifest-Version: 1.0 
    2. Bundle-ManifestVersion: 2 
    3. Bundle-Name: TinyURL  
    4. Bundle-SymbolicName: com.infoq.shorten.tinyurl  
    5. Bundle-Version: 1.0.0 
    6. Import-Package: com.infoq.shorten,org.osgi.framework  
    7. Bundle-Activator: com.infoq.shorten.tinyurl.Activator  
    8. ---  
    9. package com.infoq.shorten.tinyurl;  
    10. import org.osgi.framework.BundleActivator;  
    11. import org.osgi.framework.BundleContext;  
    12. import com.infoq.shorten.IShorten;  
    13.  
    14. public class Activator implements BundleActivator {  
    15.     public void start(BundleContext context) {  
    16.         context.registerService(IShorten.class.getName(),  
    17.             new TinyURL(),null);  
    18.     }  
    19.     public void stop(BundleContext context) {  
    20.     }  

    尽管registerService()方法接收一个字符串作为其第一个参数,而且用"com.infoq.shorten.IShorten"也是可以的,但是最好还是用class.class.getName()这种形式,这样如果你重构了包或改变了类名,在编译时就可发现问题。如果用字符串,进行了错误的重构,那么只有在运行时你才能知道问题所在。

    registerService()的第二个参数是实例本身。之所以将其与第一个参数分开,是因为你可以将同一个服务实例输出给多个服务接口(如果需要带有版本的API,这就有用了,你可以进化接口了)。另外,一个bundle输出同一类型的多个服务也是有可能的。

    最后一个参数是服务属性(service properties)。允许你给服务加上额外元数据注解,比如标注优先级以表明该服务相对于其他服务的重要性,或者调用者关心的其他信息(比如功能描述和厂商)。

    只要该bundle一启动,缩写服务就可用了。当bundle停止,框架将自动取消服务注册。如果我们想要自己取消注册(比方说,对错误代码和网络接口不可用所作出的响应)也很容易(用context.unregisterService())。

    使用服务

    一旦服务起来并运行之后,我们就可以用client访问它了。如果运行的是Equinox,你可以用services命令罗列所有已安装的服务,以及它们是由谁注册的:

    1. {com.infoq.shorten.IShorten}={service.id=27}  
    2.   Registered by bundle: com.infoq.shorten.trim-1.0.0 [1]  
    3.   No bundles using service.  
    4. {com.infoq.shorten.IShorten}={service.id=28}  
    5.   Registered by bundle: com.infoq.shorten.tinyurl-1.0.0 [2]  
    6.   No bundles using service. 

    在调用服务处理URL之前,client需要解析服务。我们需要获得一个服务引用,它可以让我们查看服务自身内部的属性,然后利用其来获得我们感兴趣的服务。可是,我们需要能够重复处理相同及不同的URL,以便我们可以把它集成到Equinox或Felix的shell里。实现如下:

    1. package com.infoq.shorten.command;  
    2. import org.osgi.framework.BundleContext;  
    3. import org.osgi.framework.ServiceReference;  
    4. import com.infoq.shorten.IShorten;  
    5.  
    6. public class ShortenCommand {  
    7.     protected BundleContext context;  
    8.     public ShortenCommand(BundleContext context) {  
    9.         this.context = context;  
    10.     }  
    11.     protected String shorten(String url) throws IllegalArgumentException, IOException {  
    12.         ServiceReference ref =  
    13.             context.getServiceReference(IShorten.class.getName());  
    14.         if(ref == null)  
    15.             return null;  
    16.         IShorten shorten = (IShorten) context.getService(ref);  
    17.         if(shorten == null)  
    18.             return null;  
    19.         return shorten.shorten(url);  
    20.     }  

    当shorten方法被调用时,上面这段程序将查找服务引用并获得服务对象。然后我们可以把服务对象赋值给一个IShorten对象,并使用它与前面讲到 的已注册服务进行交互。注意这些都是在同一个VM中发生的;没有远程调用,没有强制异常,没有参数被序列化;只是一个POJO与另一个POJO对话。实际 上,这里与最开始class.forName()例子的唯一区别是:我们如何获得shorten POJO。

    为了在Equinox和Felix里面使用这一服务,我们需要放一些样板代码进去。必须提一下,当我们定义manifest时,我们可以在Felix和 Equinox命令行上声明可选依赖,这样,当我们两者中任何一个安装之后,我们就可以运行了。(一个更好的解决方案是将其部署为单独的bundles, 这样我们可以去掉选项;但是如果bundle不存在,activator将会失败,因此无法启动)。Equinox和Felix特定命令的源代码在com.infoq.shorten.command bundle中。

    如果我们安装了命令client bundle,我们将得到一个新命令,shorten,通过OSGi shell可以调用它。要运行该命令,需要先执行java -jar equinox.jar -console -noExit或java -jar bin/felix.jar,然后安装bundle,之后你就可以使用该命令了:

    1. java -jar org.eclipse.osgi_* -console -noExit  
    2. osgi> install file:///tmp/com.infoq.shorten-1.0.0.jar  
    3. Bundle id is 1 
    4. osgi> install file:///tmp/com.infoq.shorten.command-1.0.0.jar  
    5. Bundle id is 2 
    6. osgi> install file:///tmp/com.infoq.shorten.tinyurl-1.0.0.jar  
    7. Bundle id is 3 
    8. osgi> install file:///tmp/com.infoq.shorten.trim-1.0.0.jar  
    9. Bundle id is 4 
    10. osgi> start 1 2 3 4 
    11. osgi> shorten http://www.infoq.com  
    12. http://tinyurl.com/yr2jrn  
    13. osgi> stop 3 
    14. osgi> shorten http://www.infoq.com  
    15. http://tr.im/Eza8 

    注意,在运行时TinyURL和Tr.im服务都是可用的,但是一次只能使用一种服务。可以设置一个服务级别(service ranking), 这是一个整数,取值范围在Integer.MIN_VALUE和Integer.MAX_VALUE之间,当服务最初注册时给 Constants.SERVICE_RANKING赋予相应值。值越大表示级别越高,当需要服务时,会返回最高级别的服务。如果没有服务级别(默认值为 0),或者多个服务的服务级别相同,那么就使用自动分配的Constants.SERVICE_PID,可能是任意顺序的一个服务。

    另一个需注意的问题是:当我们停止一个服务时,client会自动失败转移到列表中的下一个服务。每当该命令执行时,它会获取(当前)服务来处理URL压 缩需求。如果在运行期间服务提供程序发生了变化,不会影响命令的使用,只要有此需求时有服务在就成。(如果你停止了所有服务提供程序,服务查找将返回 null,这将会打印出相应的错误信息——好的代码应该确保程序能够预防返回服务引用为null的情况发生。)

    服务跟踪

    除过每次查询服务外,还可以用ServiceTracker来代替做这一工作。这就跳过了中间获得ServiceReference的几步,但是要求你在构造之后调用open,以便开始跟踪服务。

    对于ServiceReference,可以调用getService()获得服务实例。而waitForService()则在服务不可用时阻塞一段时间(根据指定的timeout。如果timeout为0,则永远阻塞)。我们可以如下重新实现shorten命令:

    1. package com.infoq.shorten.command;  
    2.  
    3. import java.io.IOException;  
    4. import org.osgi.framework.BundleContext;  
    5. import org.osgi.util.tracker.ServiceTracker;  
    6. import com.infoq.shorten.IShorten;  
    7.  
    8. public class ShortenCommand {  
    9.     protected ServiceTracker tracker;  
    10.     public ShortenCommand(BundleContext context) {  
    11.         this.tracker = new ServiceTracker(context,  
    12.             IShorten.class.getName(),null);  
    13.         this.tracker.open();  
    14.     }  
    15.     protected String shorten(String url) throws IllegalArgumentException,  
    16.             IOException {  
    17.         try {  
    18.             IShorten shorten = (IShorten)  
    19.                 tracker.waitForService(1000);  
    20.             if (shorten == null)  
    21.                 return null;  
    22.             return shorten.shorten(url);  
    23.         } catch (InterruptedException e) {  
    24.             return null;  
    25.         }  
    26.     }  

    使用Service Tracker的常见问题是在构造后忘记了调用open()。除此之外,还必须在MANIFEST.MF内部引入org.osgi.util.tracker包。

    使用ServiceTracker来管理服务依赖通常被认为是管理关系的好方法。在没有使用服务的情况下,查找已输出的服务稍微有点复杂:比 如,ServiceReference在其被解析为一个服务之前突然变得不可用了。存在一个ServiceReference的原因是,相同实例能够在多 个bundle间共享,而且它可以被用来基于某些标准(手工)过滤服务。而且,它还可以使用过滤器来限制可用服务的集合。

    服务属性和过滤器

    当一个服务注册时,可以将服务属性一起注册。大多情况下属性可以为null,但是也可以提供OSGi特定或关于URL的通用属性。例如,我们想给服务分级 以便区分优先级。我们可以注册Constants.SERVICE_RANKING(代表优先级的数值),作为最初注册过程的一部分。我们可能还想放一些 client想知道的元数据,比如服务的主页在哪儿,该站点的条款链接。为达此目的,我们需要修改activator:

    1. public class Activator implements BundleActivator {  
    2.     public void start(BundleContext context) {  
    3.         Hashtable properties = new Hashtable();  
    4.         properties.put(Constants.SERVICE_RANKING, 10);  
    5.         properties.put(Constants.SERVICE_VENDOR, "http://tr.im");  
    6.         properties.put("home.page""http://tr.im");  
    7.         properties.put("FAQ""http://tr.im/website/faqs");  
    8.         context.registerService(IShorten.class.getName(),  
    9.             new Trim(), properties);  
    10.     }  
    11. ...  

    服务级别自动由ServiceTracker及其他对象来管理,但也可以用特定条件来过滤。Filter是由LDAP风格的过滤器改编而来的,其使用了一种前缀表示法(prefix notation)来 执行多个过滤。虽然多数情况下你想提供类的名字(Constants.OBJECTCLASS),但你也可以对值进行检验(包括限制连续变量的取值范 围)。Filter是通过BundleContext创建的;如果你想跟踪实现了IShorten接口的服务,并且定义一个FAQ,我们可以这样做:

    1. ...  
    2. public class ShortenCommand  
    3.     public ShortenCommand(BundleContext context) {  
    4.         Filter filter = context.createFilter("(&" +  
    5.             "(objectClass=com.infoq.shorten.IShorten)" +  
    6.             "(FAQ=*))");  
    7.         this.tracker = new ServiceTracker(context,filter,null);  
    8.         this.tracker.open();  
    9.     }  
    10.     ...  

    在定义服务时可以被过滤或可以设置的标准属性包括:

    ◆service.ranking (Constants.SERVICE_RANKING) - 整数,可以区分服务优先级
    ◆service.id (Constants.SERVICE_ID) - 整数,在服务被注册时由框架自动设置
    ◆service.vendor (Constants.SERVICE_VENDOR) - 字符串,表明服务出自谁手
    ◆service.pid (Constants.SERVICE_PID) - 字符串,代表服务的PID(persistent identifier)
    ◆service.description (Constants.SERVICE_DESCRIPTION) - 服务的描述
    ◆objectClass (Constants.OBJECTCLASS) - 接口列表,服务被注册在哪些接口下

    过滤器语法在OSGi核心规范的 3.2.7节 “Filter syntax”中有定义。最基本的,它允许如等于(=)、约等于(~=)、大于等于、小于等于以及子字符串比较等操作符。括号将过流器分组,并且可以结合 使用“&”、“|” 或“!”分别代表and、or和not。属性名不是大小写敏感的,值可能是(如果不用~=作比的话)。“*”是通配符,可用来支持子字符串匹配,比如 com.infoq.*.*。

    总结

    本文中,我们介绍了如何使用服务进行bundle间通信,以替代直接类引用的方法。服务可以让模块系统动态化,这样就能应对在运行时服务的变化问题。我们 还接触到了服务级别、属性及过滤器,并使用标准服务跟踪器来更容易的访问服务并跟踪变化的服务。

    展开全文
  • Java 9开始支持模块化编程,在打包应用时不需要带上整个JRE了。利用Java自带的jlink工具可以自定义仅包含应用所需模块和指定平台所需模块的JRE。jlink位于%JAVA_HOME%\bin中,语法如下: jlink <选项> --...

    Java 9开始支持模块化编程,在打包应用时不需要带上整个JRE了。利用Java自带的jlink工具可以自定义仅包含应用所需模块和指定平台所需模块的JRE。jlink位于%JAVA_HOME%\bin中,语法如下:

    jlink <选项> --module-path <模块路径> --add-modules <模块>[,<模块>...]
    

    可能的选项包括:

    选项 描述
    –add-modules <模块>[,<模块>…] 要解析的根模块
    –bind-services 链接服务提供方模块及其被依赖对象
    -c, --compress=<0|1|2> Enable compression of resources:
    Level 0: No compression
    Level 1: Constant string sharing
    Level 2: ZIP
    –disable-plugin <pluginname> Disable the plugin mentioned
    –endian <little|big> 所生成 jimage的字节顺序 (默认值: native)
    -h, --help, -? 输出此帮助消息
    –ignore-signing-information 在映像中链接已签名模块化JAR 的情况下隐藏致命错误。
    已签名模块化 JAR 的签名相关文件将不会复制到运行时映像。
    –launcher <名称>=<模块>[/<主类>] 为模块和主类添加给定名称的启动程序命令(如果指定)
    –limit-modules <模块>[,<模块>…] 限制可观察模块的领域
    –list-plugins List available plugins
    -p, --module-path <路径> 模块路径
    –no-header-files Exclude include header files
    –no-man-pages Exclude man pages
    –output <路径> 输出路径的位置
    –post-process-path <imagefile> Post process an existing image
    –resources-last-sorter <name> The last plugin allowed to sort resources
    –save-opts <文件名> 将 jlink 选项保存在指定文件中
    -G, --strip-debug Strip debug information
    –suggest-providers [<名称>,…] 建议可从模块路径中实现给定服务类型的提供方
    -v, --verbose 启用详细跟踪
    –version 版本信息
    @<文件名> 从文件中读取选项

    例如,将MyModule模块打包并生成启动程序同时输出到Output文件夹的命令如下:

    jlink --module-path <PATH TO MyModule>;<%JAVA_HOME%\jmods> --add-modules MyModule --launcher RunExample=MyModule/MainClassWithPackages --output <PATH TO Output>
    

    若是Windows平台,则会在Output\bin中生成RunExample.bat启动文件,但启动后会有控制台窗口。可用该方法隐藏:在Output文件夹中新建RunExaNoCon.vbs并写入:

    set ws=WScript.CreateObject("WScript.Shell")
    ws.Run ".\bin\RunExample.bat",0
    
    展开全文
  • 1,完全实现了DLL模块化开化的需要,各模块可以完全独立。 2,实现有DLL窗体和主框架MDI窗体的兼容问题。 3,实现了DLL和主框架消息的上传下达。,后续复杂的消息功能,可以自行实现。 4,为了兼容DELPHI的各...
  • DR:本文介绍了锡兰运行时和分布中的模块化更改,以使其在运行时更轻。 如果只需要结果,请跳至“ 最终运行时依赖项”部分。 锡兰从一开始就采用模块化架构 。 不仅适用于编写模块的Ceylon用户,而且适用于Ceylon...

    vue模块化和非模块化

    TL; DR:本文介绍了锡兰运行时和分布中的模块化更改,以使其在运行时更轻。 如果只需要结果,请跳至“ 最终运行时依赖项”部分。

    最初我们期望大多数Ceylon用户使用ceylon run命令运行代码,所以我们认为,由于他们安装了Ceylon发行版,因此,他们依赖该发行版中的更多模块是否超出严格的要求并不重要。 这些模块无论如何都必须存在,因此不会节省任何带宽来减少那些依赖。

    自然,我们错了,在Ceylon Eclipse或IntelliJ IDE之间,在OpenShift,WildFly,Vert.x或Android上运行Ceylon的人们开始只安装标准java运行程序而无需安装发行版即可运行Ceylon。 很快变得很明显,我们不得不解开这些依赖关系以减轻运行时要求。

    从历史上看,我们有以下模块:

    • 其他模块使用的通用模块
    • typechecker(共享的编译器前端)
    • Java编译器后端
    • JavaScript编译器后端
    • 模块仓库系统
    • JBoss模块运行时

    模型模块

    当实现改进的泛型时 ,我们必须在运行时添加子类型,以便能够确定is T bar为真。 当时最简单的事情是“仅”依赖于处理语言模型和子类型的类型检查器(编译器前端)以及Java编译器后端,后者具有从JVM信息中加载语言模型的基础架构,例如作为类文件,或者在这种情况下为反射。

    这实际上使运行时依赖于编译器的前端和后端,我们意识到这并不理想,因此在Ceylon 1.2开发过程中,我们提取了所有模型描述,并将其加载和子化为新的Ceylon ceylon-model模块,但是我们没有足够的时间做更多的事情,因此由于其他原因,这些依赖关系仍然存在。

    支持Java 9

    支持Ceylon中的Java 9 / Jigsaw模块的过程中 ,很明显,将我们的javalang工具的“ fork”(我们用于javac )保留在其原始包名称下将不再起作用,我们对其包进行了重命名并使用了有机会修剪掉我们未使用的部分Java工具。 我们还将类文件阅读器部分提取到其自己的模块中,因此我们可以在编译器外部使用它来删除对jandex (类文件扫描器)的依赖。

    最后,当我们创建ceylon jigsaw工具(使用Ceylon模块所需的jar文件填充一个文件夹,以便在Java 9 VM上运行它)时,很明显,运行时仍然不仅依赖于编译器前端,而且Java后端,甚至在JavaScript后端,坦率地说,在大多数JVM执行中,这没有什么意义。

    这些依赖关系是由于Ceylon工具提供程序API方便时(当时)已ceylon.language模块。 由于这使您能够以编程方式为Java和JavaScript后端编译和运行Ceylon,因此它必须依赖于工具。

    我们决定将Ceylon Tool Provider分成自己的模块,摆脱了从语言模块到编译器和typechecker的最终依赖关系,但是没有更多的时间为Ceylon及时摆脱JBoss Modules和Aether之类的依赖关系1.2.2。

    支持Android

    在Android上运行Ceylon的最初工作表明,对于普通的JVM执行甚至Java EE部署具有很小的依赖性,对于所有方法都至关重要的Android来说,这并不是一个选择。

    在这一点上,我们不得不硬着头皮,使所有不需要的传递依赖项消失。

    我们注意到,旧的common模块已经成长为包括使ceylon命令及其子命令和插件正常工作的命令行工具API 这又取决于ceylon doc使用的Markdown渲染器。 将其提取到其自己的模块非常简单,因为在锡兰用户程序中从未使用过它。

    接下来是我们的Shrinkwrap Resolver依赖项,我们的模块存储库系统使用该依赖项与Maven存储库进行互操作 这是一个胖子,它包含所有依赖项,包括一些Apache Commons模块和Eclipse Aether的过时版本。 这个胖子在我们的Maven模块中已经存在问题,该模块已经具有Aether版本,因此摆脱胖子是一个好主意。 我们还意识到,它的某些Apache Commons依赖项已经包含在我们的发行版本存储库中的胖子之外,因此也有重复的问题可以解决。

    因此,我们要做的就是删除Shrinkwrap Resolver依赖项,并通过将其所有子组件合并到我们的发行版中直接使用Aether。 事实证明,由于最新版本的Aether需要使用Google Guava,因此我们的发行版的大小有所增加而不是缩小(那罐子很大)。 但是为了弥补这一点,我们将Aether依赖项设置为可选,并确保只要事先有一些编译步骤提供了可以在互操作中使用的所有Maven依赖项,就可以在没有它的情况下运行Ceylon。 ceylon fat-jarceylon jigsaw将为您做到这一点。

    我们的模块存储库系统还提供了对WebDAV或Herd存储库写入的支持,这需要对Apache Http Client或Sardine的某些依赖,并且我们也使这些依赖关系成为可选,因为在运行时,您的Ceylon程序不太可能写入HTTP存储库。 只有编译器和其他工具才能执行此操作。

    我们还使用抽象从语言模块中删除了对JBoss模块的依赖,因为该平台是可选的,并且从未在Android或其他平面类路径运行时使用。

    最后,由于其中存在Main API ,语言模块在(更苗条的)模块存储库系统上只剩下一个依赖项,因此我们将该类移到了自己的模块中。

    最终运行时依赖项

    在完成所有这些修剪之后,JVM上的语言模块又恢复为要求以下传递依赖项集:

    • 通用(很小,没有工具和依赖项)
    • 模型(仅取决于类文件读取器)
    • 类文件阅读器

    因此,您的Ceylon模块将仅依赖于四个jar(这三个和language模块),它们的总大小为2.4 Mb,比最初的要小得多,并且方法的数量要少得多,大约为17148个方法。 这仍然太多了,但是可以通过诸如ProGuard之类的工具删除未使用的类来降低它。 记住这包括一个完整的语言运行时,所以它不是那么大,所有的事情考虑。

    SDK变更

    为了能够在Android上使用Ceylon的HTTP客户端,我们还将ceylon.net模块从Ceylon SDK拆分为客户端和服务器模块。 否则,HTTP服务器及其依赖关系对于Android的方法数量来说是太多了。

    翻译自: https://www.javacodegeeks.com/2016/07/modularity-changes.html

    vue模块化和非模块化

    展开全文
  • 模块化

    2020-12-27 13:22:03
    模块化一、模块化简介1、模块化产生的背景2、什么是模块化开发二、CommonJS模块规范1、创建“module”文件夹2、导出模块3、导入模块4、运行程序三、ES6模块化规范1、导出模块2、导入模块3、运行程序三、ES6模块化的...
  • 转自: ... ...下面主要来讲一下单一模块的独立编译运行和插拔式的集成。...单一模块的独立编译...  模块化的好处之一就是单一模块可以独立的开发编译运行安装到用户的手机上,这样就方便了对某一模块的单独开发调试
  • 模块化开发中如何使各个模块(module)单独运行 在组件化还发中,为了提高开发效率,避免不必要的编译事件浪费,就不得不使单个业务模块独立运行。那如何实现呢? Android中有源集的概念,源集很好的解决了这类...
  • ES6模块化输出和运行

    2018-10-11 02:27:08
    安装需要的相关模块,成功之后在工程文件的根目录下创建一个.babelrc的文件,并写入: { "presets": ["es2015","latest"], "plugins": [] } 之后全局安装babel-cli:npm install -g babel-cli 可以通过...
  • java 模块化 我想每个人都会同意,编写模块化应用程序和模块化通常是一件好事。 但是,从Java和Scala语言以及各种Java / Scala框架来看,对模块化的支持看起来如何? 有很多不同的方法! 让我们看看其中的一些。 ...
  • MForcal对Forcal源程序进行模块化编译,能够编译运行具有固定格式的源程序(字符串表达式),源程序中可以使用C++风格的注释。 MForcal使得Forcal能更好地用在没有指针的编程语言中,例如 VB 。
  • 模块化时,运行出多个一模一样的APP,如下图: 解决方法:删除AndroidManifest.xml中application属性(除了app中的AndroidManifest)
  • 前面已经写了两篇Android模块的... 模块化的好处之一就是单一模块可以独立的开发编译运行安装到用户的手机上,这样就方便了对某一模块的单独开发调试,单一模块生成的apk体积也小,编译时间也快,开发效率会高很多。
  • 在分析模块化多电平换流器(MMC)稳定运行应满足多种约束条件的基础上,推导了MMC的运行边界;以换流器向交流系统注入的有功功率、无功功率为指标,建立了用于描述换流器运行特性的P-Q功率域;分析了影响换流器运行...
  • jdk 模块化 Java SE 9终于来了 。 它提供了150多种新功能,包括新的模块系统和相当多的改进,这些改进有望带来更高的安全性,更多的可伸缩性和更好的性能管理。 该发行版的明星当然是Java平台模块系统,也称为...
  • 什么是JavaScript的模块化?...模块化就是程序在运行编码过程中,就是按照每个模块一个一个逐一编码,最后形成一整个项目。当然了,这些许许多多的模块不能胡乱的编写和使用,那么就需要一定的规范了,下面给...
  • Android模块化

    2018-04-23 21:02:03
    阿里ARouter路由实现模块化开发,Android平台中对页面、服务提供路由功能的中间件。模块化的要解决的问题:模块间页面跳转(路由);模块间事件通信;模块间服务调用;模块的独立运行;模块间页面跳转路由拦截。
  • 本文阅读时间大约7分钟。今天我们谈谈SOFA模块化,首先看一段SOFA的介绍: SOFABoot是蚂蚁金服开源的基于Spring Boot的研发框架,它在Spring B...
  • Sawtooth Lake用于构建,部署和运行分布式分类帐的高度模块化平台
  • Android模块化 实例代码和通信框架地址 模块化/组件化 ...模块化的好处很多,同时也存在一些需要改进的地方:例如编译速度的瓶颈越来越大、模块间怎么进行高效通信、模块怎么独立运行调试、模块的可插拨以...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 20,364
精华内容 8,145
关键字:

模块化运行