OSGI详解

最近项目用到了OSGI,第一反应就是什么是OSGI?
OSGI是一个为Java提供动态模块化的系统;
准确的说,OSGI是一个标准,一个框架,也可以理解为一个容器,具体的实现有Eclipse下的Equinox和Appach下的Felix等。OSGI是基于Bundle(模块)驱动,每个Bundle都有自己的classpath和类加载器,模块之间通过包暴露和引入进行关联,每个模块都有自己的生命周期,可以动态进行更新、加载和删除。如图所示,在IDEA中的OSGI。
OSGI详解_第1张图片
如下为导入Eclipse下的Equinox;
OSGI详解_第2张图片
OSGI中,每个Bundle有六种状态:INSTALLED、RESOLVED、STARTING、AVTIVING、STOPPING、UNINSTALLED;
INSTALLED:Bundle已安装;
RESOLVED:表示Bundle可用的类已加载完成,此时Bundle进入启动就绪或停止状态;
STARTING:表示正在执行BundleActivator中的start方法;
ACTIVING:表示Bundle已开启;
STOPPING:表示正在执行BundleActivator中的stop方法;
UNINSTALLED:Bundle已卸载;
同时,每个Bundle都有一组导入和导出的包,以及一组所需和所提供的服务,这些依赖关系在MANIFEST.MF文件中定义。当OSGI启动时,它会尝试解析和启动所有Bundle,并确保其依赖关系;
多个Bundle之间共享一个VM(virtual machine),使用这个VM,Bundle能够隐藏其它Bundle的package和class,也与其它Bundel共享package;
Bundle在依赖其它Bundle的时候,主要通过两种方式:Imported-Package和Required-Bundle;

Imported-Package:指定此插件所依赖的包,而不显示标识原始插件;

Required-Bundle:指定这个插件操作所需的插件列表;

Bunlde通过symbolic name和Bundle version进行唯一区分;

Bunlde的class loader
每个Bundle都有一个类加载器(class loader),用于加载Bundle的资源和类。每个类加载器都是一个实例化的class loader,它可以在一定范围内加载类和资源。
Bundle的类加载器遵循双亲委派模型,当一个类被加载时,它首先会委派其给父类加载器进行加载,如果父类加载器无法加载时,才会尝试由自己加载。
每个Bundle的类加载器由每个Bundle决定,不同的Bundle,类加载器是不同的,它们隔离了Bundle的类和资源,防止类之间的冲突。每个Bundle的类加载器只能访问自己Bundle内部的类和资源,不能直接访问其它Bundle的类和资源,这就保证了OSGI模块化的特性和类加载器的隔离性。
在Bundle运行时,可以通过Bundle对象获取其类加载器,通过类加载其获取类和资源。
当一个Bundle启动时,它的类加载器会自动被创建,然后开始加载Bundle的类和资源;当Bundle停止时,类加载器会被销毁,同时Bundle的类和资源也会被释放。

以上就是个人对OSGI的理解

以下是在IDEA中基于OSGI创建一个DEMO示例,目的在于进一步对OSGI了解
创建的项目
OSGI详解_第3张图片

创建的module
OSGI详解_第4张图片
填写Symbolic name和Bundle version,此处必须填写,因为Bundle靠symbolic和version唯一区分一个Bundle;
OSGI详解_第5张图片
osgi_api模块代码

package api;

public interface IHelloService {

    String sayHello(String somebody);
}

osgi_server模块代码

package server;

import api.IHelloService;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Objects;

public class HelloServerBundle implements BundleActivator {

    @Override
    public void start(BundleContext bundleContext) throws Exception {
        System.out.println("开启服务!");
        IHelloService service = new HelloServiceImpl();
        Dictionary<String, Object> properties = new Hashtable<>();
        //服务注册
        bundleContext.registerService(IHelloService.class, service, properties);
    }

    @Override
    public void stop(BundleContext bundleContext) throws Exception {

    }
}
package server;

import api.IHelloService;

public class HelloServiceImpl implements IHelloService {
    @Override
    public String sayHello(String somebody) {
        return "hello" + somebody;
    }
}

osgi_client模块

package client;

import api.IHelloService;
import client.Log.LogUtils;
import org.apache.log4j.PropertyConfigurator;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;

import java.util.Objects;

public class HelloClientBundle implements BundleActivator {
    @Override
    public void start(BundleContext bundleContext) throws Exception {
        //收到IHelloService的引用
        ServiceReference<IHelloService> reference = bundleContext.getServiceReference(IHelloService.class);
        if(Objects.nonNull(reference)){
            //发现服务
            IHelloService service = bundleContext.getService(reference);
            if(Objects.nonNull(service))
            {
                System.out.println(service.sayHello("LQM"));
                System.out.printf("Hello and welcome!\n");
                PropertyConfigurator.configure("D:\\JavaCode\\osgi_test\\osgi_client\\src\\log4j.properties");
                //Logger.getRootLogger();
                //Category logger = Category.getRoot();
                LogUtils.logDebug("Hello1");
                LogUtils.logInfo("Info");
            }
            //注销服务
            bundleContext.ungetService(reference);
        }
    }

    @Override
    public void stop(BundleContext bundleContext) throws Exception {

    }
}
package client.Log;

import org.apache.log4j.Logger;

public class LogUtils {
    private static final Logger debugLogger = Logger.getLogger("DEBUG");
    private static final Logger infoLogger = Logger.getLogger("INFO");
    private static final Logger warnLogger = Logger.getLogger("WARN");
    private static final Logger errorLogger = Logger.getLogger("ERROR");

    /**
     * 输出debug级别的日志
     *
     * @param msg Object 要输出的消息
     */
    public static void logDebug(Object msg) {
        debugLogger.debug(msg);
    }

    /**
     * 输出info级别的日志
     *
     * @param msg Object 要输出的消息
     */
    public static void logInfo(Object msg) {
        debugLogger.info(msg);
    }

    /**
     * 输出warn级别的日志
     *
     * @param msg Object 要输出的消息
     */
    public static void logWarn(Object msg) {
        debugLogger.warn(msg);
    }

    /**
     * 输出error级别的日志
     *
     * @param msg Object 要输出的消息
     */
    public static void logError(Object msg) {
        debugLogger.error(msg);
    }
}

log4j.properties

Log4J configuration for RvSnoop @version@.

I d Id Id

log4j.rootLogger=DEBUG,stdout,A

### ???????? ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS}  [ %t ]  %m%n

log4j.appender.A = org.apache.log4j.RollingFileAppender
log4j.appender.A.layout = org.apache.log4j.PatternLayout
log4j.appender.A.layout.ConversionPattern = %d{ISO8601} %-5p [%c] %m%n
# The rvsnoop.logDir property is set to point to the correct platform
# specific log location.
log4j.appender.A.File = ./log/CodeTest.log
log4j.appender.A.MaxFileSize = 1MB
log4j.appender.A.MaxBackupIndex = 9
log4j.appender.A.Encoding = UTF-8

运行结果
OSGI详解_第6张图片

你可能感兴趣的:(笔记,eclipse,java,ide)