您的位置:首页 > 编程语言 > Java开发

OSGI Java模块化技术学习与总结

2015-12-29 10:35 465 查看
最近接触了OSGI Java模块化技术,对其进行了初步研究和学习,具体OSGI技术详细介绍和讲解 可以直接到Osgi官网进行了解。

下面是对Osgi 框架的初步认识,了解一下OSGI 目前主流的一些框架。

Apache Felix 最全面的框架
Apache Felix是 Apache旗下的一个OSGi框架,项目本身非常成熟,已经被用到了很多其他的项目中,例如Apache Servicemix。它本身提供的服务也是最全的,几乎涵盖了全部的OSGi 4.2的标准。除此之外还提供了一些非标准的功能,例如iPOJO。框架本身非常紧凑,你只需要3个包加一个shell就可以运行了,无论是开发还是
Debug都非常简便。除了Felix,还有两个项目是和OSGi相关的。一个是Apache Felix Karaf,它本身是Felix的一个子项目,但他其实是封装了Felix提供更高一层的Runtime,例如提供了JAAS。另一个是Apache
Aries,目前还处于起步阶段,它作为Felix的补充,提供OSGi企业级规范,包括JPA、JDBC、JTA、JNDI等等。
总的来说,Apache Felix是我个人推荐的最佳OSGi框架,它简单的结构也更适合出学OSGi的开发人员。
Equinox 与Eclipse完美结合
Equinox是 Eclipse旗下的OSGi框架,本身也被Eclipse采用,是Eclipse注明的PDE开发环境的底层。Equinox本身也是相当的全面的框 架,提供的功能不比Felix少多少。但是它功能的分类就稍显混乱,文档和Sample也组织的不是很好。事实上相当Equinox还是被当做开发
Eclipse Plugin的应用较多,如果你要开发一个Web程序,你就会感到它本身的功能和文档不够全面。Equinox最大的优势在于它和Eclipse结合紧 密,只要你安装了PDE,你就已经有了Equinox,可以方便的在Eclipse里设置你开发的Bundle,启动、部署等操作也异常简单,而且有专门 的Debug界面,你还能要求什么呢?
如果你想基于Eclipse开发,Equinox无疑是好选择。但对于新手而言,有时候会搞混Eclipse Plugin与OSGi的关系。
Spring DM 畸形的需求产物
Spring DM是 Spring旗下的OSGi框架,Spring我想大家都知道了,Spring DM的最大特点就是结合了Spring框架。我之所以说特点还不是优势,是因为我认为这个需求本身就是错误的。Spring和核心就是一个IoC,当然后
来它的外延扩大了,提供了越来越多乱七八糟的功能。OSGi规范本身就制定了一系列IoC的功能标准,尤其是其中的BluePrint其实相当多的借鉴了 Spring,因此完全没有必要再引入Spring充当新的IoC了。Spring本身无论是ClassLoader还是配置文件上都与OSGi格格不 入,之所以有这种需求是因为现在有大量基于Spring的项目想要过渡到OSGi上。Spring还发布了一个App Server叫Spring DM Server,是一个基于Spring DM的App Server,你会发现你需要加载80+的包来完成一个hello
world操作,这种恐怖的依赖性正是Spring所带来的。
意识到这个问题的显然不只是我一个人,传闻Spring DM和Spring DM Server都将会移交给Eclipse。就目前来说除非你有基于Spring项目的移植需求,我不推荐其他任何情况下使用Spring DM。
项目中采用的是Felix框架和Aries结合,下面拿与数据库交互为例,个人理解成的是关系数据库服务,数据库使用Mysql关系型数据库。

1、首先数据源模块配置: datasource-mysql.xml,

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.0.0"
           default-activation="eager">
    <reference id="transactionManager" timeout="20000"
               interface="org.apache.geronimo.transaction.manager.RecoverableTransactionManager" />
    <cm:property-placeholder persistent-id="carnet.jpa.datasource">
        <cm:default-properties>
               <cm:property name="ds.mysql.url" value="jdbc:mysql://localhost:3306/osgi?autoReconnect=true&characterEncoding=utf8"/> 
            <cm:property name="ds.mysql.user" value="root"/> 
            <cm:property name="ds.mysql.password" value="123"/> 
        </cm:default-properties>
    </cm:property-placeholder>
    <!--   thu data source -->
    <bean id="mysqlDataSource" class="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource">
        <property name="url" value="${ds.mysql.url}"/>
        <property name="user" value="${ds.mysql.user}"/>
        <property name="password" value="${ds.mysql.password}"/>
    </bean>

    <bean id="tcnDataSource" class="org.apache.commons.dbcp.managed.BasicManagedDataSource" destroy-method="close">
        <property name="xaDataSourceInstance" ref="mysqlDataSource" />
        <property name="transactionManager" ref="transactionManager" />
        <property name="initialSize" value="20"/>
        <property name="maxIdle" value="30" />
        <property name="minIdle" value="5" />
        <property name="maxActive" value="256" />

        <!--removeAbandoned: 是否自动回收超时连接-->
        <property name="removeAbandoned" value="true"/>
        <!--removeAbandonedTimeout: 超时时间(以秒数为单位)-->
        <property name="removeAbandonedTimeout" value="180"/>
        <!--maxWait: 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒-->
        <property name="maxWait" value="3000"/>
        <property name="validationQuery">
            <value>SELECT 1</value>
        </property>
        <property name="validationQueryTimeout" value="1" />
        <property name="timeBetweenEvictionRunsMillis" value="1800000" />
        <property name="testOnBorrow" value="true" />
        <property name= "testOnReturn" value="false" />
        <property name="testWhileIdle" value="true" />
        <property name="numTestsPerEvictionRun" value="30" />
    </bean>

    <service interface="javax.sql.DataSource" ref="tcnDataSource">
        <service-properties>
            <entry key="osgi.jndi.service.name" value="jdbc/tcn"/>
        </service-properties>
    </service>

</blueprint>



每一个OSGI模块都会有一个MANIFEST.MF文件信息,从这个文件信息里可以找到OSGI配置,目的是用于OSGI框架容器加载,也算是一个OSGI技术的标准文件吧。

2、持久层依赖于数据源层: 一般应用都会有数据持久层,例如Java技术领域的JPA。下面是配置persistence.xml。<class></class>节点配置就是持久层里的实体对象。

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             version="1.0">
    <persistence-unit name="tcnDB" transaction-type="JTA">
        <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
        <jta-data-source>osgi:service/javax.sql.DataSource/(osgi.jndi.service.name=jdbc/tcn)</jta-data-source>
        <class>com.entity.jpa.User</class>
        <exclude-unlisted-classes>true</exclude-unlisted-classes>
        <properties>
            <!-- These properties are creating the database on the fly. We are using them to avoid users having
            to create a database to run the sample. This is not something that should be used in production.
            See also the create=true line in the ariestrader-derby-ds blueprint meta data -->
            <property name="openjpa.jdbc.DBDictionary" value="mysql" />
            <property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)"/>
            <!--<property name="openjpa.Log" value="DefaultLevel=TRACE" />-->
            <property name="openjpa.RuntimeUnenhancedClasses" value="supported" />
            <property name="openjpa.TransactionMode" value="managed"/>
            <!--<property name="openjpa.Log" value="DefaultLevel=WARN, Runtime=INFO, Tool=INFO, SQL=TRACE"/>-->
            <!--<!– 启用缓存,并且设置缓存的容量为5000,并且软引用的容量为100 –>-->
            <!--<property name="openjpa.DataCache" value="true(CacheSize=5000, SoftReferenceSize=100)" />-->
            <!--<!– 启用查询结果缓存,缓存的容量为1000,并且软引用的容量为100 –>-->
            <!--<property name="openjpa.QueryCache" value="true(CacheSize=5000, SoftReferenceSize=100)" />-->
            <!--<!– 启用org.apache.openjpa.util.CacheMap,CacheMap缓存固定容量和一些软引用,保证缓存的容量在一定的范围内 –>-->
            <!--<property name="openjpa.QueryCompilationCache" value="true"/>-->
            <property name="openjpa.jdbc.MappingDefaults" value="ForeignKeyDeleteAction=restrict,JoinForeignKeyDeleteAction=restrict"/>
            <!--<property name="openjpa.ManagedRuntime" value="jndi(TransactionManagerName=osgi:service/org.apache.geronimo.transaction.manager.RecoverableTransactionManager)"/>-->
        </properties>
    </persistence-unit>

</persistence>


3、服务层依赖于持久层:这个服务如果是对数据库的操作那么需要依赖于上面配置好的持久层,例如对数据库的CRUD操作。

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:tx="http://aries.apache.org/xmlns/transactions/v1.0.0"
           xmlns:jpa="http://aries.apache.org/xmlns/jpa/v1.1.0"
           default-activation="lazy">
    <service ref="IBasePersistence" interface="com.services.jpa.persistence.IBasePersistence"/>
    <bean id="IBasePersistence"  class="com.services.jpa.persistence.impl.BasePersistenceImpl">
        <tx:transaction method="*" value="Required"/>
        <jpa:context property="entityManager" unitname="tcnDB"/>
    </bean>
    <service ref="ISMSSender" interface="com.service.common.business.ISMSSender"/>
    <bean id="ISMSSender"
          class="com.service.common.business.impl.SMSSenderHttpImpl">
    </bean>
</blueprint>


4、业务层依赖于服务层:一个完整的业务一般是需要很多服务,通过服务之间的协作,使得服务单元与其他服务单元降低耦合,构成一个套完整的服务体系。其实也跟现实生活中公司的部分商业模式一样,对于服务体系中的服务,就是接口,服务提供商换掉了没有关系,再找其他服务提供商,不会影响到我本身系统的运作。个人理解OSGI在大型复杂的业务系统能发挥出他的专长,而且说是支持热部署,这块还没有去研究过,如果是热部署或者是热加载,那么还是很不错的。

配置好上述的依赖关系之后 如何在业务应用中拿到上面第数据库访问服务和短信服务呢

public class JNDIHelper {
    public static final <T> T getServiceClient(Class<T> type) {
        T client = null;
        try {
            InitialContext ic = new InitialContext();
            client = (T) ic.lookup("osgi:service/" + type.getName());
        } catch (NamingException e) {
            e.printStackTrace();
            IOException ioe = new IOException(
                    type.getSimpleName() + " service resolution failed");
            ioe.initCause(e);
            //throw ioe;
        }
        return client;
    }
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: