精华内容
下载资源
问答
  • 这是一个嵌入了Felix OSGI框架的示例Spring Boot项目。 其他项目是API(接口模型类)及其实现。 Spring Boot应用程序将这些程序包作为OSGI框架的额外程序包公开(以便能够使用公开的服务)。 有关请参见...
  • osgi和spring区别 在此姿势中,我们将采用使用OSGi进行的第一个实现,并使用Spring Dynamic Modules改进应用程序。 Spring动态模块(Spring Dm)使基于OSGi的应用程序的开发更加容易。 这样,服务的部署就容易得多...

    osgi和spring区别

    在此姿势中,我们将采用使用OSGi进行的第一个实现,并使用Spring Dynamic Modules改进应用程序。

    Spring动态模块(Spring Dm)使基于OSGi的应用程序的开发更加容易。 这样,服务的部署就容易得多。 您可以像其他任何Spring Bean一样注入服务。

    因此,让我们从Spring dm开始。

    首先,您需要下载Spring Dm Distribution 。 在本文中,我使用了具有依赖关系的发行版,而我将仅使用以下库:

    com.springsource.net.sf.cglib-2.1.3.jar
    com.springsource.org.aopalliance-1.0.0.jar
    log4j.osgi-1.2.15-SNAPSHOT.jar
    com.springsource.slf4j.api-1.5.0.jar
    com.springsource.slf4j.log4j-1.5.0.jar
    com.springsource.slf4j.org.apache.commons.logging-1.5.0.jar
    org.springframework.aop-2.5.6.SEC01.jar
    org.springframework.beans-2.5.6.SEC01.jar
    org.springframework.context-2.5.6.SEC01.jar
    org.springframework.core-2.5.6.SEC01.jar
    spring-osgi-core-1.2.1.jar
    spring-osgi-extender-1.2.1.jar
    spring-osgi-io-1.2.1.jar

    当然,您可以将Spring 2.5.6库替换为Spring 3.0库。 但是对于本文而言,Spring 2.5.6就足够了。

    因此,从服务捆绑开始。 回想一下,该捆绑软件导出了一项服务:

    package com.bw.osgi.provider.able;
     
    public interface HelloWorldService {
        void hello();
    }
    package com.bw.osgi.provider.impl;
     
    import com.bw.osgi.provider.able.HelloWorldService;
     
    public class HelloWorldServiceImpl implements HelloWorldService {
        @Override
        public void hello(){
            System.out.println("Hello World !");
        }
    }

    这里没有要做的任何更改。 现在,我们可以看到激活器:

    package com.bw.osgi.provider;
     
    import org.osgi.framework.BundleActivator;
    import org.osgi.framework.BundleContext;
    import org.osgi.framework.ServiceRegistration;
     
    import com.bw.osgi.provider.able.HelloWorldService;
    import com.bw.osgi.provider.impl.HelloWorldServiceImpl;
     
    public class ProviderActivator implements BundleActivator {
        private ServiceRegistration registration;
     
        @Override
        public void start(BundleContext bundleContext) throws Exception {
            registration = bundleContext.registerService(
                    HelloWorldService.class.getName(),
                    new HelloWorldServiceImpl(),
                    null);
        }
     
        @Override
        public void stop(BundleContext bundleContext) throws Exception {
            registration.unregister();
        }
    }

    因此,在这里,我们将简单化。 让我们删除这个类,它对于Spring Dm不再有用。

    我们将让Spring Dm为我们导出捆绑包。 我们将为此捆绑包创建一个Spring上下文。 我们只需要在文件夹META-INF / spring中创建一个文件provider-context.xml即可。 这是XML文件中的简单上下文,但是我们使用新的名称空间注册服务“ http://www.springframework.org/schema/osgi ”。 因此,让我们开始:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:osgi="http://www.springframework.org/schema/osgi"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="
                http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                http://www.springframework.org/schema/osgi
                http://www.springframework.org/schema/osgi/spring-osgi.xsd">
     
        <bean id="helloWorldService" class="com.bw.osgi.provider.impl.HelloWorldServiceImpl"/>
     
        <osgi:service ref="helloWorldService" interface="com.bw.osgi.provider.able.HelloWorldService"/>
    </beans>

    OSGi特有的唯一内容是osgi:service声明。 此行表明我们使用接口HelloWorldService作为服务名称将helloWorldService注册为OSGi服务。

    如果将上下文文件放在META-INF / spring文件夹中 ,Spring Extender将自动检测到它,并创建一个应用程序上下文。

    现在,我们可以转到消费者捆绑包。 在第一阶段,我们创建了该消费者:

    package com.bw.osgi.consumer;
     
    import javax.swing.Timer;
     
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
     
    import com.bw.osgi.provider.able.HelloWorldService;
     
    public class HelloWorldConsumer implements ActionListener {
        private final HelloWorldService service;
        private final Timer timer;
     
        public HelloWorldConsumer(HelloWorldService service) {
            super();
     
            this.service = service;
     
            timer = new Timer(1000, this);
        }
     
        public void startTimer(){
            timer.start();
        }
     
        public void stopTimer() {
            timer.stop();
        }
     
        @Override
        public void actionPerformed(ActionEvent e) {
            service.hello();
        }
    }

    目前,这里没有任何更改。 可以使用@Resource注释代替构造函数的注入,但这在Spring 2.5.6和Spring Dm中不起作用(但在Spring 3.0中很好用)。

    现在激活器:

    package com.bw.osgi.consumer;
     
    import org.osgi.framework.BundleActivator;
    import org.osgi.framework.BundleContext;
    import org.osgi.framework.ServiceReference;
     
    import com.bw.osgi.provider.able.HelloWorldService;
     
    public class HelloWorldActivator implements BundleActivator {
        private HelloWorldConsumer consumer;
     
        @Override
        public void start(BundleContext bundleContext) throws Exception {
            ServiceReference reference = bundleContext.getServiceReference(HelloWorldService.class.getName());
     
            consumer = new HelloWorldConsumer((HelloWorldService) bundleContext.getService(reference));
            consumer.startTimer();
        }
     
        @Override
        public void stop(BundleContext bundleContext) throws Exception {
            consumer.stopTimer();
        }
    }

    不再需要注射。 我们可以在此处保持计时器的启动,但是再一次,我们可以使用框架的功能来启动和停止计时器。 因此,让我们删除激活器并创建一个应用程序上下文以创建使用者并自动启动它,并将其放入META-INF / spring文件夹中

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:osgi="http://www.springframework.org/schema/osgi"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="
                http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                http://www.springframework.org/schema/osgi
                http://www.springframework.org/schema/osgi/spring-osgi.xsd">
     
        <bean id="consumer" class="com.bw.osgi.consumer.HelloWorldConsumer" init-method="startTimer" destroy-method="stopTimer"
              lazy-init="false" >
            <constructor-arg ref="eventService"/>
        </bean>
     
        <osgi:reference id="eventService" interface="com.bw.osgi.provider.able.HelloWorldService"/>
    </beans>

    我们使用了init-method和destroy-method属性来开始和停止框架的时间,并使用构造函数arg注入对服务的引用。 使用osgi:reference字段并使用接口作为服务的键来获取对该服务的引用。

    这就是我们与该捆绑包有关的全部。 比第一个版本简单得多吗? 除了简化之外,您还可以看到源不依赖于OSGi或Spring Framework,这是纯Java语言,这是一个很大的优势。

    Maven POM与第一阶段的相同,只是我们可以减少对osgi的依赖。

    提供者:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>OSGiDmHelloWorldProvider</groupId>
        <artifactId>OSGiDmHelloWorldProvider</artifactId>
        <version>1.0</version>
        <packaging>bundle</packaging>
     
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>2.0.2</version>
                    <configuration>
                        <source>1.6</source>
                        <target>1.6</target>
                    </configuration>
                </plugin>
     
                <plugin>
                    <groupId>org.apache.felix</groupId>
                    <artifactId>maven-bundle-plugin</artifactId>
                    <extensions>true</extensions>
                    <configuration>
                        <instructions>
                            <Bundle-SymbolicName>OSGiDmHelloWorldProvider</Bundle-SymbolicName>
                            <Export-Package>com.bw.osgi.provider.able</Export-Package>
                            <Bundle-Vendor>Baptiste Wicht</Bundle-Vendor>
                        </instructions>
                    </configuration>
                </plugin>
            </plugins>
        </build> 
    </project>

    消费者:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>OSGiDmHelloWorldConsumer</groupId>
        <artifactId>OSGiDmHelloWorldConsumer</artifactId>
        <version>1.0</version>
        <packaging>bundle</packaging>
     
        <dependencies>
            <dependency>
                <groupId>OSGiDmHelloWorldProvider</groupId>
                <artifactId>OSGiDmHelloWorldProvider</artifactId>
                <version>1.0</version>
            </dependency>
        </dependencies>
     
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>2.0.2</version>
                    <configuration>
                        <source>1.6</source>
                        <target>1.6</target>
                    </configuration>
                </plugin>
     
                <plugin>
                    <groupId>org.apache.felix</groupId>
                    <artifactId>maven-bundle-plugin</artifactId>
                    <extensions>true</extensions>
                    <configuration>
                        <instructions>
                            <Bundle-SymbolicName>OSGiDmHelloWorldConsumer</Bundle-SymbolicName>
                            <Bundle-Vendor>Baptiste Wicht</Bundle-Vendor>
                        </instructions>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>

    我们可以使用maven install构建两个捆绑包。 因此,让我们在Felix中测试我们的东西:

    wichtounet@Linux-Desktop:~/Desktop/osgi/felix$ java -jar bin/felix.jar
    _______________
    Welcome to Apache Felix Gogo
    
    g! install file:../com.springsource.slf4j.org.apache.commons.logging-1.5.0.jar
    Bundle ID: 5
    g! install file:../com.springsource.slf4j.log4j-1.5.0.jar
    Bundle ID: 6
    g! install file:../com.springsource.slf4j.api-1.5.0.jar
    Bundle ID: 7
    g! install file:../log4j.osgi-1.2.15-SNAPSHOT.jar
    Bundle ID: 8
    g! install file:../com.springsource.net.sf.cglib-2.1.3.jar
    Bundle ID: 9
    g! install file:../com.springsource.org.aopalliance-1.0.0.jar
    Bundle ID: 10
    g! install file:../org.springframework.core-2.5.6.SEC01.jar
    Bundle ID: 11
    g! install file:../org.springframework.context-2.5.6.SEC01.jar
    Bundle ID: 12
    g! install file:../org.springframework.beans-2.5.6.SEC01.jar
    Bundle ID: 13
    g! install file:../org.springframework.aop-2.5.6.SEC01.jar
    Bundle ID: 14
    g! install file:../spring-osgi-extender-1.2.1.jar
    Bundle ID: 15
    g! install file:../spring-osgi-core-1.2.1.jar
    Bundle ID: 16
    g! install file:../spring-osgi-io-1.2.1.jar
    Bundle ID: 17
    g! start 5 7 8 9 10 11 12 13 14 15 16 17
    log4j:WARN No appenders could be found for logger (org.springframework.osgi.extender.internal.activator.ContextLoaderListener).
    log4j:WARN Please initialize the log4j system properly.
    g! install file:../OSGiDmHelloWorldProvider-1.0.jar
    Bundle ID: 18
    g! install file:../OSGiDmHelloWorldConsumer-1.0.jar
    Bundle ID: 19
    g! start 18
    g! start 19
    g! Hello World !
    Hello World !
    Hello World !
    Hello World !
    Hello World !
    Hello World !
    Hello World !
    Hello World !
    stop 19
    g!

    如您所见,它运行完美!

    总之,Spring Dm确实使使用OSGi的开发更加容易。 使用Spring Dm,您还可以启动捆绑包。 它还使您能够制作Web捆绑包并轻松使用OSGi纲要的服务。

    以下是这两个项目的来源:

    这是两个内置的Jars:

    这是完整的文件夹,包括Felix和Spring Dm: osgi-hello-world.tar.gz

    参考: OSGI和Spring动态模块–我们的JCG合作伙伴 Baptiste Wicht的 @ @Blog(“ Baptiste Wicht”)提供的 简单Hello World

    相关文章 :

    翻译自: https://www.javacodegeeks.com/2011/11/osgi-and-spring-dynamic-modules-simple.html

    osgi和spring区别

    展开全文
  • 辛苦研究了一个多月,终于搭建成了一个可用于实际应用开发的OSGI-WEB框架,采用OSGi+SpringMVC+Spring+Hibernate+Virgo技术,鉴于此类技术架构甚少,特提供出来供大家参考。 使用步骤: 一、下载STS搭建Osgi开发...
  • 四大OSGI框架Felix, Eqinox, Spring-DM, Knopflerfish最新技术对比分析
  • 后来经过10年的发展壮大,OSGi已经不只是在嵌入式设备中应用,而是被推广到各种其他的应用领域,比如其中最成功的Eclipse IDE。目前在企业级应用开发中也开始大量使用OSGi技术,尤其是在应用服务器领域,各大主

    一. OSGi基础

    1. 什么是OSGi

    OSGi——Open Service Gateway Initiative,最初的目的是为各种嵌入式设备提供通用的软件运行平台。后来经过10年的发展和壮大,OSGi已经不只是在嵌入式设备中应用,而是被推广到各种其他的应用领域,比如其中最成功的Eclipse IDE。目前在企业级应用开发中也开始大量使用OSGi技术,尤其是在应用服务器领域,各大主要厂商相继宣布推出支持OSGi规范的中间件产品,例如Websphere、Glassfish、JBoss等。而从应用开发人员的角度来说,OSGi应该被理解为:OSGi Service Platform,它是一个开放的并且提供统一接口标准的体系框架,基于这个体系框架,服务提供商,应用开发人员,软件提供商,服务网管运营商,设备提供商之间能够联合起来相互协作,形成一个健康的生态系统,并向终端用户提供各种各样的服务(例如:SaaS,SOA等)。目前OSGi Service规范的最新版本是4.2,可以从http://www.osgi.org官网上获取更详细的信息。

    2. OSGi能带来什么好处,能解决什么样的问题?

    做为一名实用主义者,新技术或新概念除了能够带给我们暂时的新鲜感外,还应该了解它是否能给我们的工作带来实际的好处,它能帮助我们解决怎么样的问题。OSGi与所谓的“云计算”相比并不算什么新技术,它已经存在了10年,所以它是成熟的、可靠的、经过验证的。只是相对于企业级开发领域来说,它还未被充分的实践,所以对企业级开发来说OSGi的确能算得上是新技术。以下是我眼中的OSGi给我带来的好处以及能够帮助我解决的问题,但不仅限于此,更有待您的发掘。


    首先,OSGi改变了我们设计系统的传统思维模式(评价一名优秀的架构师,思考问题的方式是至关重要的评判指标之一)。模块化的系统设计理念并非新事物,早在面向对象的设计思想诞生之前就已经出现。在面向过程的程序开发中,模块化是非常重要的工具,它可以提高程序的内聚性,降低程序的耦合度,并且在大规模协同开发中发挥着重要的作用。当然OSGi对模块化又做了更进一步的发展,提出了服务平台(Service Platform)的概念——在网络环境下的服务平台,从此我们从面向对象的时代步入面向服务的时代。我们不再把关注重点放在如何高度抽象出优美的对象模型上,而是更加注重根据实际的业务需求划分各种服务接口的边界以及各服务接口之间的依赖关系。当然,组成服务内部的构件仍然基于面向对象的思想,只不过不再成为我们唯一的关注重点。在当今SaaS(软件即服务)的大趋势下,能提供何种质量的服务将被作为评判企业级管理软件优劣的重要标准,因为服务是用户唯一可见的交互渠道,系统内部的对象模型对用户来说只是一个黑盒子(这里的用户并非单指终端用户,也包括使用服务的开发人员)。而OSGi正好能用来解决如何更好的抽象服务,拆分复杂业务逻辑等诸如此类的问题。


    其次,OSGi使我们可以更好的控制每个模块之间的约束和依赖。先来回顾一个曾经让我倍感痛苦的回忆:经过3个月的努力,系统终于开发完成,并通过了所有的测试,可以正式上线了。当部署完成并启动应用服务器时却出现了一大堆的异常。每个开发人员都很疑惑,也很沮丧。不是通过了测试了吗,这么会出现异常呢?经过大家通宵的努力,终于找到了问题的所在,原来系统依赖的一个第三方jar包与应用服务器自带的一个jar包发生了版本冲突。为了开发的便捷,测试环境用的容器是Tomcat,而Tomcat并没有自带这个冲突的jar包,但是线上环境是WebSphere,它正好自带了这个jar包,并且当我们的系统调用这个jar包时,由于Classloader的加载顺序问题导致先找到了应用服务器自带的那个jar包,所以发生了之前所描述的异常现象。虽然问题终于解决了,但是这样的问题仍然在后续的项目中不断重演……。OSGi成为了解决此问题的最佳方案,它可以让我们明确定义各模块的运行时环境(JDK版本)、导入包、导出包、依赖包,而所有的这些依赖关系的合法性验证均由OSGi运行时容器自行完成,当出现jar包版本冲突,依赖丢失等错误时,OSGi可以明确的给出提示信息,帮助我们尽快的定位问题、解决问题。在某些场景中,我们并不希望暴露某些模块的package,这时我们只需要在定义文件中不导出这些package就可以了,实现了包级别的封装控制机制。


    再次,OSGi可以解决企业级应用系统启动速度慢的问题。随着企业业务的快速增长,员工对企业级应用系统的依赖程度也就越来越高,各种新的功能不断的被加入到原来的系统中,使得系统越来越臃肿。而系统中的有些功能并非被经常使用,却需要经历非常复杂的初始化过程,为了启动这个不被经常使用的功能,整个企业级应用系统的启动时间被整整拖延了数分钟(OK!您可能觉得我说的言过其实了,但是这的确是真的,我发誓!)。启动如此缓慢的应用,对于开发和测试来说简直就是噩梦,因为每次的代码修改都需要漫长而无谓的等待才能看到真正的结果。通过OSGi,我们可以将臃肿的业务逻辑拆分成边界清晰的功能模块,并对模块启动顺序进行规划,即那些模块需要第一时间启动,那些模块可以延迟加载,最后通过Bundle元数据文件(MANIFEST.MF)进行明确的启动策略定义。只有当正真需要这个模块时,OSGi运行时容器才会去加载它。这样就可以使启动缓慢的企业级应用系统如同Apache Server那样快速启动。


    最后,OSGi可以实现我梦想中“即插即用,即拔即无”的奇妙系统。热部署可能是所有了解OSGi的人不假思索就能说出的特性。目前所有的web容器均实现了热部署功能,我们可以在不停止服务的前提下动态的部署各种应用程序。如果能将此功能带入到我们自己开发的系统中那将是多么的cool啊!OSGi将实现我们的梦想,我们可以在OSGi环境下掌控所有模块的生命周期方法,比如安装、启动、停止、卸载等,而这些操作均可在不停止当前应用的情况下进行,大大提高了无故障运行时间。并且我们还可以并发运行相同模块的不同版本,当某个新发布的版本出现问题时,我们可以暂时停止该模块,并重新启动前一个稳定版本,以隔离出问题的模块,而所有的这一切对用户来说都好像从来没有发生过一样。


    最近我一直很关注网上的各类论坛和Blog,目的就是要寻找一些对于OSGi的不同声音,我需要不同的声音,因为每一件事物都不会是十全十美,只有知道它的缺陷,才能更好更全面的了解它。在我上述的四项问题中,最被大家所诟病的莫过于热部署和太过于细粒度的模块控制机制。热部署被描述为恶魔,当Bundle的数量成几何级别增长时,这个恶魔会变的越来越强大,以至于最终恶魔被释放到人间贻害无穷(其实问题的焦点聚焦于有状态的Bundle上,当热部署一个全新的Bundle时,这不存在问题,但是热部署一个更新的Bundle时,则有状态的Bunble将丢失所有已存在的状态,当然解决方案是存在的,状态的持久化是关键,但是这又增加了bundle的复杂度)。而太过于细粒度的依赖控制机制将导致bundle之间的依赖关系错综复杂,难以维护。诚然,我承认这些曾经被过度赞赏的OSGi特性在经过理性的认知和实践之后都被发现了问题,但是我个人认为,OSGi最大的好处并不是那些在各类网络文章中反复描述的“语法糖”,而是它改变了我设计系统架构的思维模式,它帮助我形成了一套面向服务的模块化架构设计思路,让我成为一名可以站在更高层次来考虑系统设计的优秀架构师(架构师如果在设计前期就陷入细节设计的话,那将是一个非常糟糕的开始)。

    3. 我们真的需要OSGi吗?

    虽然OSGi存在这样那样的问题,但是在企业级开发中它的确是存在价值的,我们评判一个事物是否有需要往往会考虑它的价值,是否利大于弊。所以做为架构师的你是否有一种冲动,是否会大声的对着项目经理说——下一个项目我们就用OSGi来构建。慢来!先让我们冷静一下,问一下自己,我们的项目真的需要OSGi吗?OSGi会不会像达摩克利斯之剑那样,在美妙的背后却隐藏着巨大的风险呢?对于我们技术人员来讲,没有最好的技术,只有最适合的技术。OSGi不是“银弹”,不可能帮助我们解决所有的问题。在为一个项目确定是否需要OSGi时,可以将OSGi的各项特性罗列出来,然后针对自己的系统逐一核对,评估每一项特性是否是系统真正需要的。比如你的系统只是一个部门内部使用的小工具,而且其他部门对此系统并不敢兴趣,那这样的系统就没有必要使用基于OSGi的架构,如果使用了反而会增加系统的复杂度,从而提高开发成本。而那些为企业级应用系统提供基础服务的服务端系统来说则更加的适合OSGi架构,因为通过模块化,它可以提供更加稳定的服务(7*24运行时间)。这里我需要特别的指出,OSGi需要生存在一个生态系统环境中,才能展现它的价值所在,所以更加适合解决方案级别的系统架构。如果在您的生态系统环境中只存在1,2个OSGi的系统,那就好比走在南极冰盖上的大象,等待它的只有死亡。


    接下去的内容我比较纠结,因为到了最枯燥乏味的概念介绍了,本打算删除这些内容,因为网络上有太多的此类介绍,但是为了保持文章的完整性,我还是决定写下去,我尽量保持言简意赅,只把精髓描述出来。同学们,让我们继续吧。

    二. OSGi原理

    1. OSGi运行机制初探

    1) 概述

    OSGi中最重要的组成单元就是Bundle,说简单点Bundle就是一个将Java类文件、配置文件、MANIFEST.MF文件以及其他一些资源文件打包起来的Jar文件,它与普通Jar的区别就是Bundle中的MANIFEST.MF文件中包含了特殊的OSGi元数据定义,这样OSGi容器才能识别出是否是合法的Bundle。在运行时环境中,每个 Bundle 都有一个独立的 Classloader,Bundle 之间通过不同的类加载器和在 MANIFEST.MF 文件中定义的元数据来实现包级别的依赖关系控制。 隔离的Classloader 是实现这种运行方式的关键机制,每个 Bundle 的 Class Loader 必须在此 Bundle的所有依赖关系得到正确解析之后,再由 OSGi 框架创建。

    2) Bundle的解析过程

    Bundle 的解析是通过分析定义在 MANIFEST.MF 文件中的OSGi元数据 ( 主要定义了包约束条件 ),查找与包约束条件相匹配的 Bundle 并建立依赖关系的过程。 MANIFEST.MF 文件中的包约束条件主要是通过 Import-Package、DynamicImport-Package、Export-Package 和 Require-Bundle 这四种表达方式来实现。下面简单介绍一下它们:

        a) Import-Package:定义需要导入的包。默认是所有需导入的包必须都能够找到相应的导出Bundle (Exporter),否则解析失败。

        b) Export-Package:定义导出的包。在一个Bundle 中,一个包的导出意味着该包下的所有Class会在Bundle 自身的类路径里面可以被查找和加载。

        c) Require-Bundle:定义必须依赖的 Bundle 。

        d) DynamicImport-Package:定义需要动态导入的包。这部分定义没有在 Bundle 解析过程中使用,而是在运行时动态解析并加载共享包。


    当Bundle 得到正确解析后,OSGi 容器将会生成所有Bundle之间 的依赖关系表。在实际运行过程中,容器就可以通过此依赖关系表找到 Bundle 依赖的外部 Class Loader,从而实现外部类资源的加载和运行。 某个Bundle 的依赖关系图可以通过在 OSGi 的控制台中输入内部命令“bundle {id}”来查看,其中{id}为某个Bundle的ID号,可通过“ss”命令查看所有运行在OSGi容器中Bundle的ID号。

    3) Bundle的运行加载

    Bundle 的运行主要依靠OSGi 容器为其创建的类加载器(Class Loader),加载器负责查找和加载 Bundle 自身的或依赖的类资源。 ClassLoader 之间的依赖关系构成了一个有向图,如下图所示:


    ( 图-1)

    Bundle 的 Class Loader 能加载的所有类的集合称为 Bundle 的类空间。Bundle的类空间由以下5部分组成:

        a) 父Class Loader 可加载的类集合;

        b) 由Import-Package定义的依赖的包;

        c) Require-Bundle 定义的依赖的 Bundle 的类集合;

        d) Bundle 自身的类集合,通常在 Bundle-Classpath 中定义;

        e) 隶属于Bundle 的 Fragment 类集合;


    网上的大部分文章很少对Fragment Bundle做深入的解释,所以我这里重点的介绍一下神奇的Fragment Bundle。Fragment Bundle可以把它看作为某一Bundle的片段,虽然它本身是一个Bundle,但是它的生命周期确实依附于宿主Bundle,我更愿意将它理解为“寄生虫”,当宿主消亡时它也就没有存在的意义了。不过这条“寄生虫”却不会给宿主Bundle带来麻烦,反而它可以改变宿主Bundle的一些行为,通过一个实例,可能会让你更加的了解Fragment Bundle的作用。在我们即将介绍的OSGi企业级开发框架中会用到Jetty容器来发布Hessian远程服务,Jetty容器需要一个jetty.xml配置文件才能正确的启动Servlet容器。但是在一些场景中,我们需要自定义jetty.xml中的一些参数,比如监听端口等参数。默认的配置文件包含在Jetty容器的启动Bundle中,我们无法对JAR包中的配置文件做任何的修改,所以此时Fragment Bundle就有了它用武之地。在这个Bundle的MANIFEST.MF文件中定义Fragment-Host配置项指定被扩展的Jetty容器启动Bundle的Bundle-SymbolicName属性值。这样当Jetty容器启动Bundle被加载时就会将Fragment Bundle中定义的配置文件覆盖自身的默认配置文件从而实现配置的自定义功能。下图展示了宿主Bundle和Fragment Bundle之间的关系:

    ( 图-2)

    其中Master=2表示该Bundle是一个Fragment Bundle,它附属于ID为2的宿主Bundle,而Fragments=1表示该Bundle存在ID为1的附属Bundle。需要注意的是,Fragment Bundle是不能用start命令启动的,否则会报错。因此该Bundle的状态为Resolved。

    最后让我们来关注一下Bundle 的 Class Loader 是根据什么规则来搜索类资源的:

        a) 如类资源属于 java.* 包,则将加载请求委托给父加载器,(图-1)中的Parent/System classloader;

        b) 如类资源定义在 OSGi容器中启动委托列表(org.osgi.framework.bootdelegation)中,则将加载请求委托给父加载器,(图-1)中的System Bundle class loader;

        c) 如类资源属于在 Import-Package 中定义的包,则将加载请求委托给导出此包的Bundle的Class Loader;

        d) 如类资源属于在 Require-Bundle 中定义的 Bundle,则将加载请求委托给该Bundle的Class Loader;

        e) Bundle 搜索自己的类资源 ( 包括Bundle-Classpath 里面定义的类路径和属于 Bundle 的 Fragment 的类资源);

        f ) 若类在DynamicImport-Package 中定义,则开始尝试在运行环境中寻找符合条件的 Bundle 。


    如果在上述的每个步骤中没有正确的加载到指定的类资源,则OSGi容器会抛出NoClassDefFoundException或ClassNotFoundException异常。这2个异常是OSGi环境下开发最容易遇到的问题,因此一旦遇到此问题就应该按照上述步骤检查每个Bundle是否正确导出了被其他Bundle引入的包或是正确引入了其他Bundel导出的包。

    4) Java Classloader vs. OSGi Classloader

    通过对上述Bundle类加载器运行机制的描述我们已经了解了OSGi环境下类加载器的运行原理,但是它和我们平时在非OSGi环境下开发的Java程序的类加载过程到底有什么不同呢?

    (图-3)

    通过(图-3)我们可以清楚的看到OSGi环境下的classloader和普通Java环境下classloader的显著区别。在普通Java环境下classloader是以继承关系相互连接的,为了加载一个class,classloader首先会去询问它的父classloader是否可以加载这个class,如果不能加载,则它就尝试自己完成这个class的加载。这个机制是运用在每一个层次上的classloader的。从上图中,我们就可以看到这种机制的运行以及所有classloader之间的关系。正如我们所预见的,在Java中一个应用程序的所有class被同一个classloader装载,因此要实现不同class之间的隔离是非常困难的。这里我们有必要讲一下JSR294——Java模块化系统,JSR294的目的是为了提供一个Java自身内部的静态模块化系统。它采用与OSGi相类似的基于Require-Bundle头部的方式但是它更加强调语言级别上的支持以及它自身JDK中的模块化运行时。JSR294不会拥有如OSGi那样在同一个进程中支持相同类的不同版本共存的功能。


    OSGi则提供一种不同的方式来使用classloader。它并没有采用基于继承的方式而是使用了一种叫Classloader Chaining的概念,它允许细粒度的控制每个类之间的可见性。在一个上下文中,每个组件(Bundle)与一个专有的Classloader所关联。根据组件(Bundle)的配置(manifest.mf),Classloader可以被连接到当前组件(Bundle)之外的其他组件(Bundle)的Classloader。但是在默认情况下,所有class都不能看到除自身之外的其他组件的class。因此我们必须通过在manifest文件中配置相应的package来显式的导入或导出它们。


    如下图所示,这个处理流程被叫做“Classloader Chaining”。为了简单起见,我们只展示了2个彼此关联的组件(Bundle),但是在实际的实现中依据不同的配置则依赖关系也有所不同。

    (图-4)

    从(图-4)中我们可以发现,2个Bundle是完全隔离的,因为他们有各自属于自己的Classloader。与之前的非OSGi的Classloader有着明显的区别。

    5) 关键的MANIFEST.MF文件

    Bundle与普通Jar文件的唯一区别就在于MANIFEST.MF文件的OSGi元数据,以下介绍一些常用的Bundle元数据定义:

        a) Bundle-Activator:定义Activator的实现全限定类名称,此类必须实现BundleActivator接口,并实现start和stop方法。当Bundle被OSGi容器启动或停止时就会去调用start和stop方法。Bundle-Activator并非是必须的,只有在需要初始化或是销毁资源时才有用,并且不推荐在start方法中进行复杂的处理,以免加重OSGi容器启动的负担。在Spring DM中,ApplicationContext的创建和销毁就是在extender中通过实现BundleActivator接口实现的。

        b) Bundle-Classpath:有些jar文件是某个Bundle专属的,此时就应该把这些jar包设置到Bundle的classpath中。比如JDBC驱动等Jar包。

        c) Bundle-ManifestVersion:设置Bundle所遵循的OSGi规范版本,目前情况下应该将该值设置为2,表示OSGi R4版本。

        d) Bundle-Name:必须的,Bundle的名称,与Bundle-SymbolicName对应,Bundle-Name类似于name,而Bundle-SymbolicName类似于id。

        e) Bundle-SymbolicName:必须的,Bundle的唯一标识名,一般推荐采用类包的机制,保证其唯一性。

         f) Bundle-Version:必须的,Bundle的版本号,可用于在import-package中进行过滤。

        g) Export-Package:Bundle对外公开的可被其他Bundle导入的包。

        h) Import-Package:Bundle需要导入的包。Bundle自身使用到的所有依赖类必须被Import进来,否则就会在OSGi容器启动时抛出NoClassDefFoundException或ClassNotFoundException异常。

        i) Require-Bundle:Bundle引用到的其他Bundle,应该将该属性值设置为被引用Bundle的Bundle-SymbolicName属性。

        j) Fragment-Host:指定被附属的Bundle的Bundle-SymbolicName,在前面部分已经进行了详细的讨论。

    6) 让人纠结的Import-Package和Require-Bundle

    OSGi 框架提供了两种导入包依赖的方式,即 Import-Package 和 Require-Bundle 。从下图中我们可以看出 Require-Bundle 会对整个 Bundle 产生依赖,也就是说 Bundle 所 Export 出的包都会被 A 加入到自己的类空间,而 Import-Package 只会对指定的包产生依赖关系。

    (图-5)

    那到底应该使用Import-Package还是Require-Bundle呢?在大多数情况下,都应该使用 Import-Package 而不是 Require-Bundle 。 Import-Package 比 Require-Bundle 更利于 Bundle 的部署和版本维护,同时在查找类的时候有更高的效率。

    好了,我认为OSGi最核心的原理也就是这些了,其实OSGi并非大家所想的那么高深莫测,其核心理论就是Classloader的隔离,这是OSGi的根本。只有在实现了这种机制的基础上才有可能存在细粒度的包控制,动态加载等被大家津津乐道的特性。


    我们的重点是基于OSGi的企业级开发框架,我本人也不止一次的宣称自己是一名实用主义者,所以我不想花太多的笔墨沉迷于理论的纠缠中。毕竟我不是搞科研理论研究的,我是利益驱动的生物,我做的一切都必须给我带来利益。不过,做为一名严谨的中生代码农,我也不能完全的将理论抛在一边。实践是检验真理的唯一标准,但是没有真理的实践也是空洞和乏味的。这里我推荐2本书,一本是《OSGi in Action》,已经有中文版的发售,虽然从书名上看,它是一本更注重实战的书,但是观后我却感觉它更适合做为一本理论教学课本。另外一本是《Spring Dynamic Modules in Action》,该书虽然没有中文版本,但是也是一本不错的好书,由于Spring不再继续开发Spring DM了,所以这本书估计也不会再有人去翻译了。我也只翻译了前三章而作罢,因为Spring DM不复存在了。不过该书中的理论还是可以参考的,有助于我们理解Spring DM的工作原理。因为我们的基于OSGi的企业级开发框架就是在Spring DM基础上发展而来的。

    二. Spring DM(Eclipse Gemini Blueprint)与OSGi

    在我写这篇培训材料的时候,Spring DM还属于SpringSource。当时的最新版本是2.0.0.M1,从那时起到现在的近3年中,Spring DM却始终处于停滞的状态。直到近期,SpringSource终于宣布放弃Spring DM并将其代码捐献给了Eclipse,更名为Gemimi Blueprint。庆幸的是名字虽然变了,但是其核心代码功能却没有发生任何的变化。只不过包名由原来的org.springframework.osgi变更为org.eclipse.gemini.blueprint,如果要移植则可能需要一些小修改。也正因为如此,我当时写这些培训教材才不至于被遗弃,能够继续发挥它的作用。


    化了很大的篇幅讨论了OSGi的原理和实现,原因很简单,因为只有了解了这些基础概念,才能对Spring DM有更深入的理解。因为SpringDM并不是OSGi的标准实现,它的运行必须依赖OSGi的标准容器,比如Equinox、Felix或是Knopflerfish等。SpringDM只是帮助我们用一种我们所熟悉的编程方式来完成OSGi服务的注册、查询、使用和监听。我们也可以将这些OSGi服务称之为Bean。所以SpringDM依赖于OSGi容器,没有OSGi容器,则SpringDM就无法运行,相反的,如果没有SpringDM,则OSGi服务将变的非常的难以使用,不利于OSGi技术的推广和发展。

    1. 什么是Spring DM?

    Spring DM的前身是Spring OSGi,目的是为了提供一套在OSGi环境下简化开发的模型(基于POJO的编程模型),将Spring现有的机制如DI和AOP无缝的引入到OSGi环境下。现在,Spring OSGi更名为Spring Dynamic Modules(Spring动态模型),更加明确了Spring DM的实现目标,即Spring Dynamic Modules致力于整合Spring框架和OSGi平台:前者有强大且非侵入性的企业级编程模型,而后者具有动态性、模块化的特点。将两家之长融会贯通,扬长避短,则更加能成为企业级开发领域的“大杀器”。

    2. Spring DM能带来什么好处,为什么要使用Spring DM?

    Spring框架提供一个轻量级的容器和一种非侵入编程模型,它基于DI、AOP和可移植服务抽取。OSGi服务平台提供一个动态应用程序执行环境,在这个环境里Bean(bundles)可以被即时地安装、更新或者移除。它同样对Bean的版本控制有着优秀的支持。

    Spring Dynamic Modules使得编写一个可部署在OSGi执行环境下的Spring应用程序变得很轻松。由于Spring框架的简单易用和强大,Spring对OSGi的支持同样使得OSGi应用程序的开发变得更简单和高产。对企业级应用来说,Spring Dynamic Modules和OSGi平台的整合提供如下特性:

        1) 更好的模块间的应用逻辑隔离,这些模块具有运行时强制的模块边界;

        2) 同时部署同一个模块(或库)的不同版本的能力;

        3) 动态发现和使用系统内其他模块提供的服务的能力;

        4) 在运行着的系统中动态地安装、更新和卸载模块的能力;

        5) 使用Spring框架在模块内部和模块之间进行实例化、配置、整合组件的能力;

        6) 对于企业级开发人员来说Spring是一个简单而熟悉的编程模型,可以降低开发人员的入门门槛和学习成本;

    相信OSGi和Spring的结合将为构建企业级应用程序提供一个方便易懂的编程模型。

    3. OSGi Bundle和Spring的ApplicationContext上下文

    在Spring中最核心的概念就是应用程序上下文(Application Context),在应用程序上下文中则包含了一些Bean定义。应用程序上下文可以被配置成具有层次关系,这样一个子应用程序上下文可以使用其父应用程序上下文中定义的Bean,反之却不行,这种层次关系与classloader之间的层次关系非常的相似。Spring中的输出器Bean和工厂Bean则用于将Bean的引用输出到应用程序上下文之外的客户端中,并且将服务的引用注入到客户端中。


    在OSGi bundle和Spring应用程序上下文之间有很自然的紧密联系。使用Spring Dynamic Modules,一个处于Active状态的Bundle可以包含一个Spring 应用程序上下文,它负责在Bundle中实例化、配置、组装和装饰Bean。这些Bean即可作为OSGi服务输出并提供给其他Bundle引入,也可以注入其他OSGi服务输出的Bean引用。


    Spring DM提供了一个OSGi bundle:org.springframework.osgi.bundles.extender(在Gemini Blueprint中不再是这个包名),这个Bundle负责为应用程序Bundle实例化Spring应用程序上下文。它的功能和用于Spring Web应用程序的ContextLoaderListener一样。一旦Extender Bundle被安装和启动,它就会寻找所有基于Spring的且已经在ACTIVE状态的Bundle,并且替它们创建应用程序上下文。另外Extender Bundle还监听Bundle启动事件和为所有后启动的基于Spring 的Bundle创建应用程序上下文。Extender Bundle异步地创建应用程序上下文。这样使得OSGi 服务平台能快速地启动,而且不会导致具有内部依赖性服务的Bundle在启动时死锁。一旦Bundle的应用程序上下文创建成功,应用程序上下文对象会自动通过OSGi服务注册表输出为可用服务。上下文发布为org.springframework.context.ApplicationContext接口。被发布的服务有一个名为org.springframework.context.service.name的服务属性,该属性值为Bundle在应用程序上下文里注册的标记名称。把应用程序上下文发布为服务主要用于方便测试和管理。在运行时通过getBean或类似操作来获取上下文对象并不被鼓励使用。获取在其他应用程序上下文里定义的Bean的最好方式是将该Bean在它的定义上下文里输出为OSGi服务,然后在要用到服务的上下文里导入该服务。通过这种方式使用服务注册表确保Bean仅能获取和所需服务类型兼容的服务,而且OSGi平台的动态性也得到了尊重。

    4. 打包和部署基于SpringDM的OSGi应用程序

    一个典型的Spring应用程序使用一个单独的应用程序上下文或者使用一个具有子上下文的父上下文,父上下文包含了服务层、数据层和域对象,子上下文包含了网络层组件。应用程序上下文可以由多个配置文件的内容集成。

    部署一个应用程序到OSGi,更自然的结构是把应用程序打包成一系列的Bundles,每个Bundle使用一个单独的应用程序上下文,这些Bundle通过服务注册表进行交互。独立的子系统应该被打包成独立的Bundle或者一系列的Bundles。例如一个简洁的web应用程序应该被分成四个模块: Web Bundle,服务层Bundle,数据层Bundle,域模型Bundle。这样的一个应用程序如下图:

    (图-6)

    这是一个网上引用比较频繁的示意图,我也拿来主义一下。在本例中,数据层Bundle生成了一个包括有许多内部组件的数据层应用程序上下文。其中两个Bean在服务注册表里被发布成了服务,从而使得两者变得在应用程序上下文之外公共可用。


    服务层Bundle生成了一个包括有许多内部组件的服务层应用程序上下文。其中的一些组件依赖于数据层服务,它们从服务注册表里导入了这些服务。其中的两个Bean在服务注册表里被发布成了服务,从而使得它们在外部可用。


    Web组件Bundle生成了一个包括许多内部组件的Web应用程序上下文。其中的一些组件依赖应用程序服务,并且从服务注册表里导入了这些服务。由于域模型Bundle仅提供域模型类型,但是不需要创建任何自己的组件,所以它与应用程序上下文没有任何关系。

    5. 基于Spring的Bundle格式

    Bundle要能被Spring DM识别并为其创建关联的上下文就必须要符合Spring DM定义的Bundle格式。当满足如下两个条件之一时,Spring Extender会识别一个Bundle是“基于Spring的”(Spring-Powered),并且会为之创建一个关联的应用程序上下文:

        1) Bundle路径包含了一个文件夹 META-INF/spring,在该文件夹下有一个或多个XML文件;

        2) META-INF/MANIFEST.MF 文件里包含了一个Spring-Context元数据。

    基于“约定优于配置”的原则,通常情况下我推荐采用第一种方式,即在META-INF目录下创建一个Spring目录,并将所有的Spring Bean的XML配置文件放入其中,应用程序上下文(ApplicationContext)从这些配置文件中构造。一个推荐的做法是将上下文配置文件分成至少两个文件,习惯上分别命名为“模块名-context.xml”和“模块名-osgi-context.xml”。前者包含独立于OSGi之外的标准Bean定义。后者包含输出和导入服务的Bean定义。

    6. 必须的Springframework Bundles和Spring DM Bundles

    Spring Dynamic Modules工程提供了很多现成的Bundles,如果要正常使用Spring Extender这些Bundles必须安装到OSGi平台上。

        1) Extender Bundle本身org.springframework.osgi.extender;

        2) Spring Dynamic Modules的核心实现,org.springframework.osgi.core;

        3) Spring Dynamic Modules I/O 支持库bundle,org.springframework.osgi.io;

    如果需要Web的支持,还必须包含以下Bundles:

        1) org.springframework.osgi.web;

        2) org.springframework.osgi.web.extender;

    另外Spring Framework 提供了一些Bundles 必须安装。安装的最小集为:

        1) spring-core.jar (org.springframework.core) ;

        2) spring-context.jar (org.springframework.context) ;

        3) spring-beans.jar (org.springframework.beans) ;

        4) spring-aop.jar (org.springframework.aop) ;

    另外如下几个支持包是必需的。这些库的OSGi版本和 Spring Dynamic Modules的分发包在一起:

        1) aopalliance;

        2) backport-util (如果在JDK1.4上运行);

        3) cglib-nodep (如果服务代理有类,而不仅仅是接口,那么一般都需要此包) ;

        4) commons-logging API (推荐为SLF4J版本) ;

        5) logging 实现包,例如 log4j;

    关于Spring DM我就介绍到这里吧,我想最最核心的东西基本上都已经呈现出来了。更多的细节,您可以到http://www.eclipse.org/gemini/官方主页上查看。


    其实Spring DM的使用是非常简单的,因为对于一个熟悉Spring开发模型的人来说,很多东西都是老朋友了。即使是有点生疏的面孔也可以凭借自己的经验来快速的了解它。我们的基于OSGi的企业级开发框架之所以考虑采用Spring DM在很大的程度上就是考虑到Spring编程模型的深入人心。接下去,我们就将进入本系列文章的关键内容,基于OSGi企业级开发框架的实践,敬请关注!

    展开全文
  • OSGI和spring-dm

    2013-08-05 15:21:13
    OSGI原理与最佳实战 未来十年:OSGI、Gemini、Virgo spring-dm的api
  • 最近纠结一个问题,osgi框架是分模块开发的,然后spring cloud是分布式开发,这两个有什么区别呢?或者有联系么?
  • OSGI框架有类似于hiberate validation 的@validated 注解吗?比如: @Null 被注释的元素必须为 null @NotNull 被注释的元素必须不为 null @Email 被注释的元素必须是电子邮箱地址 @Length(min=,max=) 被注释...
  • Spring 是一个著名的 轻量级 J2EE 开发框架,其特点是面向接口编程非侵入式的依赖注入。将 OSGi Spring 结合能充分发挥二者各自的特长,更好地满足企业级应用开发的需求。Spring 开发组织在 2008 年发布了将 ...

    作为一个新的事实上的工业标准,OSGi 已经受到了广泛的关注, 其面向服务(接口)的基本思想和动态模块部署的能力, 是企业级应用长期以来一直追求的目标。Spring 是一个著名的 轻量级 J2EE 开发框架,其特点是面向接口编程和非侵入式的依赖注入。将 OSGi 和 Spring 结合能充分发挥二者各自的特长,更好地满足企业级应用开发的需求。Spring 开发组织在 2008 年发布了将 OSGi 和 Spring 结合的第一个版本:Spring-DM。本文通过一个简单实例,介绍如何利用 Spring-DM 开发基于 OSGi 和 Spring 架构的 Web 应用,同时探讨其中用到的关键技术及其基本思想。


    展开全文
  • OSGI入门整合Spring

    2008-04-14 16:35:13
    本文介绍了使用Eclipse进行OSGi开发的入门实践,并进一步介绍了如何将OSGi和Spring整合。
  • 1.3 Spring-DM 1.3.1 简介 Spring-DM 指的是 ...Spring-DM 的主要目的是能够方便地将 Spring 框架 和OSGi框架结合在一起,使得使用Spring的应用程序可以方便简单地部署在OSGi环境中,利用 OSGi 框架提供的...

    转自:http://www.osgi.com.cn/article/7289459

     

    1.3 Spring-DM

    1.3.1 简介

    Spring-DM 指的是 Spring Dynamic Modules。Spring-DM 的主要目的是能够方便地将 Spring 框架 和OSGi框架结合在一起,使得使用Spring的应用程序可以方便简单地部署在OSGi环境中,利用 OSGi 框架提供的服务,将应用变得更加模块化。具体的信息可以查看 http://static. springframework.org/osgi/docs/1.2.0-rc1/reference/html/。

    1.3.2 环境搭建

    可以从 Spring 的网站上下载新的 Spring-DM 包。目前新的版本为 1.2.0 RC1,下载地址在 http://www.springsource.com/download/community?project=Spring%20Dynamic%20Modules&version= 1.2.0.RC1 这个页面上。它提供了 with-dependencies 和没有 dependecies 两个包的下载。我们建议大家下载 with-dependencies 的这个包,这个包里面包含了 Spring-DM 依赖的其他的包。下载了这个 Spring-DM 包后,我们把压缩包中的 dist 和 lib 目录解压到硬盘上的某个目录,比如解压到 C 盘根目录下的 spring-dm 目录中。那么我们会在 C:\Spring-dm\dist 目录下看到如图 1-52 所示的内容。在C:\spring-dm\lib 中看到如图 1-53 所示的内容。

    【图 1-52 Spring-DM 中 Dist 的内容 】

    下面把 spring-dm 的 bundle 导入到 Eclipse 中。

    我们首先导入 spring-dm 的包。打开 Eclipse,选择 Import…,在 Import 的对话框中选择 Plug-ins and Fragments(见图 1-54)。

    【图 1-53 Spring-DM 中 lib 的内容 】

    【图 1-54 导入插件 】

    然后在 Import Plugin-ins and Fragments 对话框中做如图 1-55 所示的设置。

    【图 1-55 设置要导入插件的目录 】

    我们要设置 Plug-in Location,先设置为 c:\spring-dm\dist,导入 sprimg-dm 的包。点击 Next 后,出现了让我们选择导入的 Plugin 界面(见图 1-56)。

    【图 1-56 选择要导入的插件 】

    我们可以导入 core、extender、io 三个 Bundle。完成后,会在 Eclipse 的工作区看到如图 1-57 的显示。

    【图 1-57 导入后的显示 】

    我们直接在 Run Configurations 中选择这三个 Bundle,并执行,会发现三个 Bundle 都是 INSTALLED状态,如果我们启动Bundle,会报错,原因是我们没有加入这三个Bundle所依赖的Bundle。而这些Bundle,也就在c:\spring-dm\lib目录下。我们用和前面一样的方式来导入lib中所需要的Bundle。要 导 入 的 Bundle 是 com.springsource.org.aopalliance 、 org. springframework.aop 、 org.springframework.beans 、 org.springframework.context 、 org.springframework.context.support 、 org.springframework.core。

    这个时候,在 Eclipse 的工作区看到的应该是如图 1-58 的显示。

    【图 1-58 导入其他需要的插件 】

    在 Run Configurations 中选择目前导入的所有 Bundle,然后再选择 Target Platform 中的 org.apache.commons.logging 这个 Bundle,运行。可以看到,所有的 Bundle 都是 ACTIVE 状态了(见图 1-59)。

    到这里就完成了环境的搭建。下面就来做一个基于 Spring-DM 的应用。

    【图 1-59 启动后的 Bundle 状态 】

    1.3.3 HelloWorld

    先看一个简单的例子。在这个例子中将展示如何把一个Spring Bean展示成服务,以及如何把OSGi 的服务注入到 Spring 的 Bean 中。在这个例子中,有两个 Bundle,分别是 HelloWorld Bundle 和 TimeService Bundle。例子代码见源码 Spring-DM 中的 HelloWorld 工程和 TimeService 工程。

    这两个工程中都没有 BundleActivator。在 Bundle 启动和停止的时候不用去做什么事情。先来看 一下 TimeService 工程(见图 1-60)。

    【图 1-60 TimeService 工程 】

    可以看到,我们在 TimeService 工程中定义了 TimeService 接口和 TimeService 的实现(TimeServiceImpl)。在这里,这样定义和实现并不是一个很好的做法,接口的定义应该放到一个单独的 Bundle 中,这里不过是为了示意,选择了一个简单的做法。TimeService 接口和 TimeServiceImpl 类的代码很简单,我们不再介绍。这个工程大的不同是在 META-INF 目录下有一个 spring 目录。这个目录中有两个文件,分别是 timeservice.xml 和 timeservice-osgi.xml。我们分别看一下这两个文件的内容。

    timeservice.xml

    <?xml version="1.0" encoding="UTF-8"?> 
    <beans xmlns="http://www.springframework.org/schema/beans"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">   
      <bean name="timeService" 
    class="org.osgichina.demo.timeservice.impl.TimeServiceImpl"> 
      </bean> 
    </beans>

    这个 xml 文件中定义了一个 spring bean。这个和通常的 spring 的配置文件没有任何区别。

    timeservie-osgi.xml

    <?xml version="1.0" encoding="UTF-8"?> 
    <beans xmlns="http://www.springframework.org/schema/beans"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xmlns:osgi="http://www.springframework.org/schema/osgi"   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd   http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd"> <osgi:service id="osgiTimeService" ref="timeService" interface="org.osgichina.demo.timeservice.TimeService"> 
    </osgi:service> 
    </beans>

    这里,我们看到了一个新的标签“”,这个标签的含义是来定义一个 osgi 的 service。这个 service 实现的接口是“org.osgichina.demo.timeservice.TimeService”,另外这个 service 引用的对象是“timeService”,也就是刚才我们看到的上一个配置文件中定义的 Spring Bean。这样的一个配置,就可以把 spring bean 发布成一个 osgi 的 service 了。是不是很简单呢。

    下面我们看一下 HelloWorld 工程(见图 1-61)。

    【图 1-61 HelloWorld 工程 】

    可以看到,HelloWorld 文件中只有 HelloWorldImpl 这样一个 Java 文件,并且在 META-INF 下也躲了一个 spring 的目录,目录中有 helloworld-osgi.xml和 helloworld.xml 文件。我们简单看下 HelloWorld 的代码。

    public class HelloWorldImpl{ private TimeService timeService; 
     
    public TimeService getTimeService() { return timeService; 
    }  
    public void setTimeService(TimeService timeService) { this.timeService = timeService; 
    }  
    public void start(){ 
    System.out.println("started at " +timeService.getCurrentTime()); 
    }  
    public void stop(){ 
    System.out.println("stopped at " +  timeService.getCurrentTime()); 
    } 
    }

    可以看到,HelloWorld 是一个简单的 bean,在调用 start 和 stop 的时候会有日志的输出。

    接下来看 helloworld.xml 文件。

    <?xml version="1.0" encoding="UTF-8"?> 
    <beans xmlns="http://www.springframework.org/schema/beans"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
      <bean name="hello" class="org.osgichina.demo.helloworld.HelloWorldImpl" init-method="start" destroy-method="stop" > 
      <property name="timeService" ref="osgiTimeService"/> 
      </bean>   
    </beans>

    可以看到,这个配置文件和普通的 spring 配置文件是一样的,定义了一个 bean,以及 init 和 destory 的方法。并且注入了一个对象给 timeService 这个属性。下面,我们来看一下 helloworld-osgi.xml。

    <?xml version="1.0" encoding="UTF-8"?> 
    <beans xmlns="http://www.springframework.org/schema/beans"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xmlns:osgi="http://www.springframework.org/schema/osgi"   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd   http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd"> 
     
    <osgi:reference id="osgiTimeService" 
    interface="org.osgichina.demo.timeservice.TimeService"/> 
    </beans>

    注意,这里也多了一个新的标签“osgi:reference”,这个是对 OSGi 服务的一个引用。这里要定义 一下接口名称,因为这是用来表示引用的服务的。

    把一个 spring bean 发布为服务,以及如何引用一个 OSGi 服务作为 bean 的属性了吧。这里,可能大家有一个疑问是,如果 timeService 不是注入到 spring bean 里面,而是要在代码中获取这个服务,那该怎么办呢?很简单,我们只要通过 BundleContext 来获取这个服务就好了。服务的名字就是接口的名字,换句话说,对于这个例子中的 timeService,我们通过下面的代码就能获得这个服务:

     ServiceReference sf = context.getServiceReference(TimeService.class.getName()); 
    TimeService ts = (TimeService)context.getService(sf);

    到这里,例子已经完成了。在这里简单介绍一下这背后的实现。其实是 spring 的 extender 这个 bundle 帮我们完成了服务的发布和注入的工作。这个 bundle 会寻找 META-INF 下 spring 目录中的所有 xml,完成配置工作。当然,我们也可以在 MANIFEST.MF 文件中配置 spring-dm 用到的配置。而 spring 目录则是一个默认的约定。完成了这个例子,下面我们要完成一个 Spring-DM 用在 Web 场景下的例子。

    1.3.4 Web版HelloWorld

    前面一节已经看到了 Spring-DM 的 HelloWorld 的例子。这里,来看一下 Web 版的 HelloWorld。这里主要是想介绍在 Spring-DM 中的另外两个 Bundle,一个是 org.springframework.osgi.web,另外一个是 org.springframework.osgi.web.extender 。大家还记得,在上一节的例子中,我们用到了 org.springframework.osgi.extender 这个 Bundle。这里的org.springframework.osgi.web.extender 这个Bundle 是用在 Web 环境中的一个 Extender。

    其实构造 Web 版的 HelloWorld 有多种方法。一种方式是可以在前面的 HelloWorld 的例子中通过嵌入 HttpServer 来完成一个 Web 应用。而这一节不采用这样的方案,我们采用 Spring-DM 的 WebExtender 来部署一个 Web 程序。

    在 Eclipse 中有两个方案来创建我们的工程:一个是创建一个标准的 Web 工程,这样的做法有一个不方便的地方是调试的问题;另外一个办法是创建一个插件工程,而这个工程打包后是拥有 Web 应用结构的 jar 包。下面,我们就来动手完成这个例子。

    1.3.4.1 环境搭建

    我们在完成 Spring-DM 的 HelloWorld 的时候,搭建了一个环境。现在,要在那个环境的基础上再引入几个 Bundle ,分别是 org.springframework.osgi.catalina.osgi 、 org.springframework. osgi.catalina.start.osgi 、 org.springframework.osgi.servlet-api.osgi 、 org.springframework.osgi.web 、 org.springframework.osgi.web.extender。其中后面两个 Bundle 是从 dist 目录下导入的,前面三个是从 lib 目录下导入的。导入这些 Bundle 后,我们的 Eclipse 看起来应该是如图 1-62 所示的样子。

    【图 1-62 加入新插件后的显示 】

    接着,我们要修改 Run Configurations,设置 org.springframework.osgi.web.extender 的 Start Level,需要把这个 Bundle 的 Start Level 修改得比别的 Bundle 大。另外就是在 Target Platform 中要选择javax.servlet 和 org.apache.commons.logging 这两个 Bundle。如图 1-63 所示。

    然后点击“Run”,可以在 Eclipse 的 Console 中看到类似如图 1-64 所示的显示。

    【图 1-63 运行配置的 Bundles 】

    【图 1-64 运行后的显示 】

    这个时候,也可以查看到 8080 端口已经是在 LISTEN 的状态。我们的 Web 版环境准备结束,下面就来进行开发。

    1.3.4.2 Web应用开发

    Web 应用开发步骤如下。

    >第一步,创建插件工程。

    我们创建一个名为 HelloWorldWeb 的插件工程。

    **> **第二步,添加 WEB-INF 目录和 index.html 文件,并且在 WEB-INF 目录中加入 web.xml 文件。 这样,我们的工程看起来应该如图 1-65 所示。

    【图 1-65 HelloWorldWeb 的显示 】

    **>**第三步,引入 Package(见图 1-66)。

    【图 1-66 引入的 Package 】

    >第四步,创建 HelloWorldServlet 。代码请见源码中 HelloWorld-SpringDM 目录下的 HelloWorldWeb 工程。并且,我们在这里也要引入前面 HelloWorld 例子中的 TimeService。

    >第五步,编辑 index.html。

    这里,我们可以随便在 index.html 中搞点儿什么你喜欢的。

    >第六步,编辑 web.xml。

    这个 web.xml 基本就是普通的 Web 应用中的配置,在这个 web.xml 中,我们加入了 Spring 的 ApplicationContext 的配置:

    <context-param> 
    <param-name>contextClass</param-name> 
    <param-value>org.springframework.osgi.web.context.support. 
       OsgiBundleXmlWebApplicationContext</param-value> 
    </context-param>

    完整的内容可以参考源码中 HelloWorld-SpringDM 目录下 HelloWorldWeb 工程中的 web.xml。

    >第七步,添加 applicationContext.xml。我们在 WEB-INF 目录下添加 applicationContext.xml 文件,这里的配置是为了引入 TimeService,内容如下:

    <!--引入OSGi Service的定义--> 
    <osgi:reference id="osgiTimeService" 
    interface="org.osgichina.demo.timeservice.TimeService"/>

    >第八步,配置 Run Configurations。

    我们要在 Run Configurations 的 Target Platform 中加入三个 Bundle,分别是 javax.servlet.jsp、 org.apache.jasper、org.apache.commons.el。

    >第九步,启动,运行。

    我们会看到 Console 中会输入类似下面的信息:

        信息: Successfully deployed bundle [HelloWorldWeb Plug-in (HelloWorldWeb)] 
    at [/HelloWorldWeb] on server org.apache.catalina.core.StandardService/1.0

    至此,可以在浏览器上测试一下我们的应用了。

    打开浏览器,输入 http://localhost:8080/HelloWorldWeb/。

    会看到如图 1-67 所示的显示。

    【图 1-67 浏览器结果显示 】

    然后,再输入 http://localhost:8080/HelloWorldWeb/hello。

    将会看到如图 1-68 所示的内容。

    【图 1-68 浏览器结果显示 】

    好了,我们的 Web 应用已经可以正常工作了,并且也可以方便地在 Eclipse 的集成环境中调试我 们的程序。

    展开全文
  • OSGi相关框架学习资料

    2019-07-22 13:24:05
    资源名称:OSGi相关框架学习资料资源目录:【】EclipseRCP与SpringOSGi技术详解与最佳实践【】osgi.core.javadoc-5.0.0【】OSGi_R4_Spec_zh_cn_origional【】OSGi原理与最佳实践【】OSGi原理与最佳实践的源码【】...
  • 4种常见的osgi框架比较

    万次阅读 2017-09-24 19:28:55
    OSGi正在成为一种趋势,越来越多的项目采用了OSGi,越来越多的中间件都开始采取了OSGi的标准。  什么是OSGi  OSGi的名称来源于其开源组织的名称Open Services Gateway ...普通的JVM程序相比,OSGi的程
  • OSGi正在成为一种趋势,越来越多的项目采用了OSGi,越来越多的中间件都开始采取了OSGi的标准。身为一名Java开发人员,如果你还对OSGi结构一无所知,那你真的有点Out了。  什么是OSGi  OSGi的名称来源于其...
  • 在此姿势中,我们将采用使用OSGi进行的第一个实现,并使用Spring Dynamic Modules改进应用程序。 Spring动态模块(Spring Dm)使基于OSGi的应用程序的开发更加容易。 这样,服务的部署就容易得多。 您可以像其他任何...
  • 在实际项目中,我负责的模块需要对每个传入的issue进行解析,得到采集项采集对象然后进行不同的采集动作,由于没有上车统一南向(一个专门负责采集的模块),采集方法比较杂乱,每一个issue都需要写一个对应的...
  • OSGi入门和Spring整合

    千次阅读 2008-04-15 11:00:00
    写了一个OSGi入门及整合Spring教程,见我的资源:http://download.csdn.net/source/413310另:近期对Spring Dynamic Modules guid reference的翻译已经接近尾声,会在近期内释出 
  • 本文从 Java 类加载器说起,探讨了 OSGI 的类加载器原理并对 Equinox 中的 Servletbridge 原理实现进行了详细的研究,同时扩展到使用这一原理如何在 Spring boot 应用中嵌入 OSGI 开发 Spring boot 应用如何与 ...
  • 本文是OSGi和Spring入门的第一部分,首先介绍了什么是Spring动态模型。OSGi亦称做Java语言的动态模块系统,它为模块化应用的开发定义了一个基础架构。
  • 框架启动时报错 org.osgi.framework.BundleException: Unable to resolve com.chinatelecom.all.smart gateway.ZService-OSGi [2](R 2.0): missing requirement [com.chinatelecom.all.smar tgateway.ZService-...
  • 基于OSGi和Spring开发Web应用

    万次阅读 2015-08-11 11:28:43
    Spring 是一个著名的轻量级J2EE 开发框架,其特点是面向接口编程非侵入式的依赖注入。将 OSGi Spring 结合能充分发挥二者各自的特长,更好地满足企业级应用开发的需求。Spring 开发组织在 2008 年发布了将 OSGi...
  • 到目前为止,我们已经学习了如何使用Blueprint將Spring框架整合到OSGI应用中,并学习了Blueprint&Gemini Blueprint的一些使用细节。本篇文章开始,我们將Mybatis框架整合到OSGI应用中。一、获取Mybatis框架...
  • osgi框架基础原理与实例一

    万次阅读 2017-06-15 16:36:19
    OSGI(Open Services Gateway Initiative),或者通俗点说JAVA动态模块系统,定义了一套模块应用开发的框架OSGI容器实现方案如Knopflerfish, Equinox, and Apache Felix允许你把你的应用分成多个功能模块,这样通过...
  • 基于OSGi框架的Eclipse开发环境搭建

    千次阅读 2018-09-08 20:57:29
    Apache Karaf: Karaf是2001年Apache旗下的一个开源项目.Karaf同时也是一个基于OSGi的运行环境,Karaf提供了一个轻量级的OSGi容器。可以用于部署各种组件,应用程序.Karaf提供了很多特性用于帮助开发者用户更加灵活...
  • 当我们开始使用OSGi时,就有机会为小型网络网关开发世界一流的框架。 相比之下,今天的Raspberry Pi非常庞大。 16年后的今天,OSGi是市场上针对Java应用程序的主要模块化解决方案。 我们认为,模块化解决方案对于每...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,053
精华内容 2,821
关键字:

osgi框架和spring区别

spring 订阅