Java高级编程 魏勇 清华大学出版社
ISBN-9787302450948
仅供参考, 自建索引, 以备后查
一、javadoc、jar、JMX、SVN、Git
/**
* 此类注释出在执行命令后生成文档,而且只在public方法、属性上有效
* @author Colin
* @version 1.3.2 Colin 2020-11-19 <br/>
* 1.3.3 Colin 2021-01-19
*/
# javadoc [options] [packagenames] [sourcefiles] [@files]
[options]
-sourcepath E:\workspace\socket-http test.java0.socket_http
切换到 E:\workspace\socket-http 文件夹开始编译包
-classpath E:\workspace\maven-repository\
引用 E:\workspace\maven-repository\ 目录中的jar引用类
[packagenames] 不支持递归子包,无通配符
[@files] 读取文件内容进行编译
如 package-compile.txt 文件内容如下
com.baidu.advertisement
com.baidu.videoshare
则执行 # javadoc -d apidoc @package-compile.txt 后
会对文件中的包中的java文件进行编译
install4j exe4j TowerJ jaxegen.exe InstallAnywhere JET JOVE JToExe
MANIFESAT.MF 一般放置于 META-INF/ 文件夹
Manifest-Version: 1.0
Main-Class: test.java0.socket_http.SocketHttpFileStarter
Created-By: 1.0.1 Colin
# cd /d E:\workspace\socket-http
# jar cvfm socket-http.jar MANIFEST.MF test.java0.socket_http
JMX (Java Management Extensions) 为应用程序、设备、系统植入管理功能的框架
虽然这么印刷的,还没太深入理解,倒是加入到了项目中,发现也挺有用
javax.management.MBeanServer JMX代理层核心 (个人理解:类似于反射工具类)
javax.management.ObjectName 用于为类指定标识名,用于查找类实例对象
public static void main(String[] args) throws Exception {
String dir = "E:/workspace/filesocket/src/main/webapp";
HttpFileServerSocket hfss = HttpFileServerSocket.getInstance().setWebDir(dir);
new Thread(hfss).start();
ObjectName sn = new ObjectName("HttpFileServerSocket:name=DirSetter");
/// MBeanServer mbs = MBeanServerFactory.createMBeanServer();
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); /// 推荐
/// 为 hfss 实例注册 之后可以进行管理
mbs.registerMBean(hfss, sn);
}
打到 JAVA_HOME\bin 目录下 jconsole
# jconsole 运行
选择对应的进程
对于我的这个程序来说,作用就是动态修改了某个属性
Subversion:SVN服务提供程序
TortoiseSVN:SVN独立客户端
SVNService.exe:把SVN加入到Windows服务
TortoiseGit:Git客户端软件
二、顺序、链式存储、队列、堆栈、树、二叉树
队列 First In First Out Vector rear: 队列尾部 front: 队列头部
堆栈 Last In First Out Stack
如:进制转换可以把 余数 及最后 商 依次入栈,再全部出栈即可
Java中的链式是自身引用实现,C、C++中则使用指针实现
class ListNode { Object data; ListNode next; }
单向链表、单向循环链表、双向链表
interface ListIterator extends Iterator { } Java中的双向遍历接口
先根 中 左 右
中根 左 中 右
后根 左 右 中
T=(D,R) D:所有结点集合 R:节点关系集合
二叉树 Binary Tree 顺序存储(完全二叉树比较合适)或链式存储均可实现
Enumeration 传统数据结构使用此类遍历
public SequenceInputStream(Enumeration<? extends InputStream> e) { }
Properties extends Hashtable<Object, Object> { }
BitSet <========
三、多层网络结构、Socket、WebSocket
OSI 网络七层结构
应用层 表示层 会话层 传输层 网络层 数据链路层 物理层
TCP/IP Transport Control Protocol / Internet Protocol
- 链路层:数据链路层或网络接口层,与操作系统的设备驱动程序和网络接口卡交互
- 网络层:IP地址 InetAddress Inet4Address Inet6Address
- 传输层:端口号,TCP/IP 数据传输控制 Socket ServerSocket DatagramPacket DatagramSocket MulticastSocket
- 应用层:最上层协议 ftp http smtp pop3 telnet
URL Uniform Resource Locator
openStream() getContent() openConnection()
URLConnection
MIME Multipurpose Internet Mail Extensions 多用途Internet邮件扩展
IP地址分类 <========
Socket 实现 Web 服务器
HTTP请求报文 请求行 请求头部 空行 请求数据
Java中 浏览器 组件
JEditorPane Lobo+FramePanel JDIC+JDICPlus SWT+Browser
J2ME cHTML Browser
Dooble
j2wap WAP浏览器
Proteus 基于J2ME的手机浏览器
jCellBrowser 同上
MicroBrowser4ME 同上
MoDaBrowser
WebSocket 可以参考 https://blog.csdn.net/u011225581/article/details/112465492
HTTP请求头中 Upgrade: websocket 表示需要切换到 websocket 进行通信
websocket 聊天程序示例
SMTP Simple Mail Transfer Protocol 简单邮件传输协议
邮件发送示例
四、MINA、状态转换
MINA Multipurpose Infrastructure for Network Applications
ApacheDirectory、AsyncWeb、ApacheQpid、QuickFIX/J、
Openfire、SubEthaSTMP、red5
基于 JAVA NIO 采用非阻塞异步传输,事件驱动、支持TCP/IP等
NioSocketAcceptor NioSocketConnector
IoSession IoHandlerAdapter IoHandler
sessionCreated() sessionOpened() sessionIdle()
exceptionCaught() messageReceived() messageSent()
ApacheMINA网络应用一般架构
IO服务:执行实际IO操作,支持不同协议,如TCP/IP、UDP/IP、串口、管道
IO过滤器:转换字节流到指定对象与数据结构;对输入输出的数据进行处理
IO处理器:具体业务逻辑
MINA实现数学计算示例:Java调用JS引擎
MINA与Spring框架Ioc/DI整合使用
MINA在Android上应用,Service服务中加入连接获取 Session
MINA状态机 - 事件驱动控制流程
传统应用程序:遵循事先程序设定,很少有事件改变执行流程
事件驱动程序:根据接收事件执行相应流程,由用户事件改变控制流程
@Transitions({
@Transition(on="", in="", next=""),
@Transition(on="", in="", next="")
})
@Transition(on="触发事件名称", in="起始状态", next="结束状态", weight=10)
StateContext StateContextLookup StateControl
mina: IoSessionStateContextLookup -> IoSession
IoHandlerTransition IoHandlerTransitions
StateMachineProxyBuilder SingletonStateContextLookup
方法匹配规格,根据 @Transition 及方法的参数数量与类型
状态继承 @State Root @State(Root) Playing
使用 StateContext 而非 IoSession 是因为不需要类型转换
Java5开始支持 静态导入
Windows 安装 Openfire XMPP/Jabber 协议
%Openfire%\resources\database 各类数据库表的脚本
%Openfire%\conf\openfire.xml <database>存储配置的数据库连接信息
Spark 客户端 可与 MSN、ICQ 等 IM 服务器通信 Openfire需要安装 IM Gateway 插件
ClarosChat 基于 Ajax 的Web即时消息客户端
Jeti Java即时消息客户端
JWChat 基于Web的客户端
Ashcast SwingGUI客户端
NFC Chat
JClaim
Eclipse 导入 openfire_src 二次开发
Openfire Socket 网络连接 ConnectionManager+ConnectionManagerImpl
- 服务器与服务器之间的连接 端口 5269
- 外部组件和服务器之间的连接 端口5275
- 多元连接 5269
- 客户端与服务器的连接 5222
- 客户端TLS/SSL与服务器的连接 5223
Protocol Codec Factory 通过 Decoder Encoder 扩展支持各种 Socket 网络协议
自定义需实现 interface ProtocolCodecFactory { } 参考 XMPPCodecFactory { }
Openfire 客户端实现 IoHandler 的类是 ClientConnectionHandler 继承处 ConnectionHandler
五、Java类加载器ClassLoader、加密算法、JAAS、JSSE、JCE、DES、MD5
类加载器:JVM类装载器Bootstrap、自定义装载器(extends ClassLoader)
无用户装载器的情况下,使用 ClassLoader.getSystemClassLoader() 装载用户类
Bootstrap ClassLoader:C++实现,所有类加载器的最终父加载器,装载关键Java类
ExtClassLoader:位于Luncher.java文件,装载Java支行环境扩展包 (jre/lib/ext) 等,
虽然被Bootstrap加载,但父加载器类被设置为null (C++/Java)
AppClassLoader:位于Luncher.java文件,在ExtClassLoader之后同样被Boostrap加载,
但是父类被设置为 ExtClassLoader。 父类和哪个加载器来加载,不一定一一对应
Parent Delegation 模型
类似于树形结构,先由父级去加载,父级会先判断由父级加载,若不成功则自己加载
命名空间
加载的类被放入JVM,相应有一个完全匹配类名对应,而 加载器 作为此名称的一部分,
所以被不同加载器加载的同一个类是两个不同的类
运行时包
和命名空间类似,防止用户自定义类冒充核心类库
如:定义 java.lang.OOObject 被用户自定义类加载器加载,但是由于装载器不同,因此
不能自由访问 java.lang 包中其它的可见成员
扩展ClassLoader方法
extends ClassLoader { @Override public Class findClass(Stirng name) { } }
消息摘要:检验消息完整性, MD4、MD5,SHA-1 java.security.MessageDigest
/// MD5方式生成文件 file 摘要信息
md5 = MessageDigest.getInstance("MD5");
input = new DigestInputStream(
new BufferedInputStream(
new FileInputStream(file)), md5);
int len = 0;
byte[] buf = new byte[2048];
while ((len=input.read(buf))>0) {}
byte[] digest = input.getMessageDigest().digest();
input.close();
javax.xml.bind.DatatypeConverter.printHexBinary(digest);
JCE Java密码术扩展 Java Cryptography Extension
JSSE Java安全套接字扩展 Java Secure Sockets Extension
JAAS Java认证和授权服务 Java Authentication and Authorization Service
JGSS Java能用安全性服务 Java General Security Service
CertPath API Java证书路径API Certication Path API
SSL 安全套接字层 Secure Sockets Layer
TLS 传输层安全性 Transport Layer Security
应该使用 transient 关键字对密码等敏感字段进行标识
Java支持的消息摘要算法
MD2、MD5:128位算法
SHA-1:160位算法
SHA-256、SHA-383、SHA-512
plainText = "要生成摘要的数据";
/// plainText == bytes[] 同样适用
byte[] bytes = new byte[12];
new Random().nextBytes(bytes);
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(bytes); /// 前后加干扰
md.update(plainText.getBytes());
md.update(bytes); /// 前后加干扰
return DatatypeConverter.printHexBinary(md.digest());
消息认证码:密钥和数据一起用来生成消息摘要
Java支持 HMAC/SHA-1 HMAC/MD5
/// 生成密钥
SecretKey md5key = KeyGenerator.getInstance("HmacMD5").generateKey();
/// Mac 类用于生成消息认证码
Mac mac = Mac.getInstance("HmacMD5");
mac.init(md5key);
mac.update(plainText);
/// 消息认证码
byte[] bytes = mac.doFinal();
Base64编码 0-25 'A'-'Z' 26-51 'a'-'z' 52-61 '0'-'9' 62'+' 63'/'
Java 中 byte 用两个字节 0xFF 表示,即最多由 8 位 二进制 组成
Base64最多64个字符,用 6 位 二进制 2^6 足以表示
转换:byte[] 转换二进制位,每 6 位进行分隔,有余数则加入 零值字节(0000 0000)
一字节是8位二进制,补完之后的总字节 % 24 == 0 即可 因为 24%8==24%6
ASCII 中 ’A‘ = 65 = 0100 0001 ===> 则补位变为 0100 0001 0000 0000 0000 0000
切分为两段,每段 6 位二进制,即 010000(16) 010000(16) 000000(=) 000000(=)
即 ASCII 中 'A' 对就的 Base64 编码为 QQ==
私钥密码术 密码块 填充 序列密码 密码方式
/// 生成DES密钥Key
KeyGenerator kg = KeyGenerator.getInstance("DES");
kg.init(56);
Key key = kg.generatorKey();
/// 获取加密对象实例
/// DES加密 ECB方式 PKCS5填充
Cipher cipher = Cipher.getIntance("DES/ECB/PKCS5Padding");
/// 加密明文
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encrypts = cipher.doFinal(plainText); ///
/// 解密
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] normals = cipher.doFinal(encrypts);
私钥可以加密单一位或位块,通常是64位大小,若有余数,则需要进行填充
如填充全0或全1 PKCS5为私钥常用填充方式 PKCS1为公钥常用方式
ECB:电子密码本 Electronic Code Block
CBC:密码块链接 Cipher Block Chaining
CFB:密码反馈方式 Cipher Feedback Mode
OFB:输出反馈方式 Output Feedback Mode
PCBC:填充密码块链接 Propagating Cipher Block Chaining
私钥算法 DES TripleDES AES RC2 RC4 RC5 Blowfish PBE
公钥加密 不对称加密 密钥对
生成密钥对,公钥对外发布,公钥加密则使用私钥解密,私钥加密则使用公钥解密
公钥加密相对比较慢
/// 生成密钥对 含 公钥 私钥
KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
gen.initialize(2048);
KeyPair pair = gen.generateKeyPair();
/// 使用 公钥 或 私钥 加密对象
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pair.getPublic());
///cipher.init(Cipher.ENCRYPT_MODE, pair.getPrivate());
/// 加密明文
byte[] encrypts = cipher.doFinal(plainText);
/// 初始化解密对象
cipher.init(Cipher.DECRYPT_MODE, pair.getPrivate());
///cipher.init(Cipher.DECRYPT_MODE, pair.getPublic());
///
byte[] decrypts = cipher.doFinal(encrypts);
数字签名 确认身份
数字签名不提供消息加密,需要把消息明文和签名发送给对方,所以必须配合加密技术使用
MessageDigest md = ...
byte[] digests = md.digest(); /// 消息摘要
/// 生成密钥对 含 公钥 私钥
KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
gen.initialize(2048);
KeyPair pair = gen.generateKeyPair();
/// 使用 公钥 或 私钥 加密对象
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pair.getPrivate()); /// 此处使用私钥
/// 操作明文叫作加密 操作消息摘要叫数字签名
byte[] encrypts = cipher.doFinal(digests);
可以直接使用 RSA算法用于数字签名和加密,名为 DSA Digital Signature Algorithm
MD2/RSA MD5/RSA SHA1/DSA SHA1/RSA
/// 生成密钥对 含 公钥 私钥
KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
gen.initialize(2048);
KeyPair pair = gen.generateKeyPair();
/// 签名对象
Signature sig = Signature.getInstance("MD5WithRSA");
sig.initSign(key.getPrivate());
sig.update(plainText);
byte[] signatures = sig.sign(); /// 对明文plainText的摘要进行签名
public static void main(String[] args) throws Exception {
String plainText = "Hello,you!I-am-going-to-destroy-you!";
byte[] bytes = plainText.getBytes("utf-8");
/// 密钥对
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(1024);
KeyPair kpr = generator.generateKeyPair();
PrivateKey prk = kpr.getPrivate(); /// 私钥
PublicKey puk = kpr.getPublic(); /// 公钥
byte[] md5_bs = md5(bytes); /// MD5
System.out.println("MD5:" + DatatypeConverter.printHexBinary(md5_bs));
byte[] rsa_bs = rsa(md5_bs, prk);
System.out.println("RSA:" + DatatypeConverter.printHexBinary(rsa_bs));
byte[] de_rsa = de_rsa(rsa_bs, puk);
System.out.println("---:" + DatatypeConverter.printHexBinary(de_rsa));
byte[] sign_bs = sign(bytes, prk);
System.out.println("SIGN:" + DatatypeConverter.printHexBinary(sign_bs));
boolean verified = sign_verify(bytes, puk, sign_bs);
System.out.println("verified:" + verified);
}
static byte[] md5(byte[] bytes) throws Exception {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(bytes);
return md.digest();
}
static byte[] rsa(byte[] bytes, PrivateKey key) throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(bytes);
}
static byte[] de_rsa(byte[] bytes, PublicKey key) throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(bytes);
}
static byte[] sign(byte[] plainText, PrivateKey key) throws Exception {
Signature sig = Signature.getInstance("MD5WithRSA");
sig.initSign(key);
sig.update(plainText);
return sig.sign();
}
static boolean sign_verify(byte[] bytes, PublicKey key, byte[] signs) throws Exception {
Signature sig = Signature.getInstance("MD5WithRSA");
sig.initVerify(key);
sig.update(bytes);
return sig.verify(signs);
}
数字证书 密钥、证书管理工具keytool 密钥库keystore 信任公钥库truststore 认证中心CA
JDK 支持 X.509 数字证书标准
CertPath API 证书路径,操作读取系统密钥等 证书链 CRL证书撤销列表 证书期限
CRL Certificate Revocation Lists
代码签名:对打包的jar文件进行签名
jarsigner 签名工具 提供jar、私钥、证书 生成jar文件的签名版本
# jarsigner HelloWorld.jar KeyAlias /// 用KeyAlias密钥来进行签名
# jarsigner -verify -verbose -certs HelloWorld.jar /// 验证下
SSL/TLS
https连接成功,服务器向客户端发送证书,客户端用本机安装的公共CA证书来校验服务器身份
因为RSA公钥加密时间较长,一般做法是,客户端生成新密钥,如DES、RC4,
然后把此密钥用服务器的公钥加密,并传送给服务器,之后会话就用新密钥来加密解密
/// 证书路径环境变量
System.setProperty("javax.net.ssl.trustStore", KEYSTORE_PATH);
/// 读取证书创建SSLContext上下文
SSLContext context = SSLContext.getInstance("TLS");
KeyStore ks ...
ks.load(..)
KeyManagerFactory kf ...
kf.init(..)
context.init(kf.getKeyManagers(), null, null);
/// 根据证书生成 SSLServerSocket
ServerSocket ss = context.getServerSocketFactory()
.createServerSocket(PORT);
/// 设置信任证书库的位置
System.setProperty("javax.net.ssl.trustStore", KEYSTORE_PATH);
System.setProperty("javax.net.debug", "ssl.handshake"); /// 打印LOG
/// 使用已安装的信任证书库创建 Socket
Socket socket = SSLSocketFactory.getDefault()
.createSocket(REMOTE_IP, REMOTE_PORT);
/// socket instanceof SSLSocket true
生成RSA密钥
# keytool -genkey -alias key-unique-alias -keyalg RSA
导出公钥证书
# keytool -export -alias key-unique-alias -keystore path -file export_file
导入公钥证书
# keytool -import -trustcacerts -alias key-unique-alias -file cer_file -keystore path
SSL协议位于 TCP/IP协议与各种应用层协议之间
SSL记录协议 SSL Record Protocol:数据封装、压缩、加密
SSL握手协议 SSL Handshake Protocol:身份认证、协商加密算法、交换加密密钥
SSL协议针对双方,多方时参考 Visa和MasterCard 制定的 SET 协议
SSL 连接:单次数据传输,与SSL会话相关联
SSL 会话:客户端与服务器的关联,被SSL连接共享,避免频繁创建
六、远程对象 RMI CORBA IDL EJB+JBoss+Tomcat
RMI 远程方法调用 Remote Method Invocation
远程对象interface (调用端、服务端)extends java.rmi.Remote { }
远程对象实现类(服务端)extends UnicastRemoteObject ... throws RemoteException { }
RMI调用类(调用端 Naming.lookup)
RMI发布类(服务端 Naming.bind Name.rebind)
查找某接口实现 RemoteException NotBoundException MalformedURLException
Naming.lookup("rmi://192.168.8.188:1099/location/coordinate/toaddress")
绑定发布某接口实现,可被远程调用
LocationService ls = new LocationServiceImpl();
Naming.rebind("rmi://localhost:1099/location/coordinate/toaddress");
rebind不会抛出AlreadyBoundException
编写远程对象实现类,编译,生成代码存根,开户注册服务,运行服务端、开户调用端
public class LocationServiceImpl extends UnicastRemoteObject implements LocationService { }
# javac LocationServiceImpl.java
# rmic LocationServiceImpl /// 为实现类生成代码存根,会生成两份个class文件
LocationServiceImpl_Stub.class(调用端用) LocationServiceImpl_Skeleton.class
# rmiregistry /// 开启 Naming.bind 的注册服务
RMI系统:框架层、远程引用层、传输层 RMI目前基于TCP实现
CORBA Common Object Request Broker Architecture 公用对象请求代理结构
IDL接口描述语言InterfaceDescriptionLanguage ORB对象请求代理 互操作协议IIOP
对象管理组织 OMG 为解决分布式处理环境 DEC 中硬件和软件系统互联提出的一种解决方案
用于在不同编程语言,不同进程甚至是不同物理机器上的进程之间进行通信
IDL描述对象呈现的接口,编程语言去实现 Ada C C++ Smalltalk Java Python 均支持
如有文件 location.idl 定义了 LocationService 远程调用接口
module Location {
interface LocationService {
string toaddress(in double lan, in double lng);
};
};
# idlj -fall location.idl /// idlj java内置的idl文件编译工具
会生成一个 Location (module的名称) 目录,包含
LocationService.java LocationServiceHelper.java LocationServiceHolder.java
LocationServiceOperations.java _LocationServiceStub.java
# javac *.java /// 对编写与生成的文件进行编译
# tnameserv -ORBInitialPort 900 /// 启动命名服务,相当于启动查找服务
COBRA通过IOR(Interoperable Object Reference 可互操作对象引用)给远程服务定位
IOR:0000db42362.....十六进制数字...983ba34
# java LocationServiceServerStarter -ORBInitialPort 900 /// 启动远程调用服务,指定注册端口
# java LocationClientStarter -ORBInitialHost 192.168.8.188 -ORBInitialPort 900 /// 启动调用端
EJB Enterprise Java Bean
会话Bean(SessionBean 实体Bean(EntityBean 消息驱动Bean(MessageDrivenBean
开发步骤:
开发:一般三个文件 Bean类、Bean本地接口、Bean远程接口
配置:XML文件,声明绑定Bean类,定义环境属性
组装:安装到服务器、与其它Bean组合
ejb-jar.xml: EJB组件发布描述文件,定义EJB组件类型,指定Remote接口,Home接口与Bean类
jboss.xml: EJB组件发布到JBoss服务器的必要描述文件,需指定JNDI名字
----------------- ejb-jar.xml jboss.xml 打包发布组件
jboss-web.xml: WEB应用发布到JBoss-Tomcat时的文件,与web.xml并列
web.xml: WEB应用必要文件,可加入<ejb-ref>指向jboss-web.xml定义的EJB组件
----------------- jboss-web.xml web.xml 发布JBoss中Web应用
EJB基础为 RMI-IIOP(J2EE网络机制) 和 JNDI
Java Remote Method Invocation over the Internet Inter-ORB Protocol
允许编写的分布式对象在内存中跨Java虚拟机及物理设备调用执行
Java Naming and Directory Interface 命名及目录接口
J2EE提供的标准接口,定位用户、机器、网络、对象及服务
七、OSGi Open Service Gateway initiative Java动态模块编程标准
bundle [ˈbʌndl] 大神撰写的 https://blog.csdn.net/acmman/category_6128357.html
个人理解:OSGi开发的项目类似于Tomcat中webapps目录下的Web工程,Tomcat管理界面可以
动态的对这些项目进行管理,卸载,启动,停止,上传新工程(war) 等; 或者像
openfire_src 开发中的独立插件,加入进去即可设置启动、停止、卸载等等; 亦或者
像微服务中的单个工程,可以动态的注册或熔断、停止等,其它工程通过注册中心调用。
public class Activator implements BundleActivator { }
OSGi框架会使用 Class.newInstance() 方法创建 BundleActivator 对象,必须存在无参构造函数(反射)
public void start(BundleContext context) throws Exception { 初始化 }
public void stop(BundleContext context) throws Exception { 销毁注销 }
OSGi可以被当作正常jar来引用,但是当在 MANIFEST.MF 加入一系列属性后,就能被识别为OSGi模块
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: LocationService
Bundle-SymbolicName: LocationService
Bundle-Version: 1.0.0
Bundle-Activator: org.abc.def.LocationActivator
Bundle-ActivationPolicy: lazy
Bundle-RequireExecutionEnvironment: JavaSE-1.8
Import-Package: org.osgi.framework;version="1.3.0"
Bundle组件之间的依赖关系更像是提供者与使用者,而非上下级关系
通过 Import 引用,Export 对外提供
调用其它 Bundle 模块 Export 提供的服务
BundleContext context = ... 模块上下文
ServiceReference ref = context.getServiceReference(""); 获取服务引用
/// ref = context.getServiceReference("", "");
context.getService(ref); 接口实现对象
提供 Bundle 模块的对外可见的服务
BundleContext context = ...
context.registerService("", instance, properties); 注册服务 可被外部调用
context.registerService(String[], instance, properties);
Eclipse 选择 Plug-in Project 项目去创建 OSGi 类型的工程
发布的时候,可以选择 Deployable plug-ins and fragments
若选择导出目录为 D:\workspace\osgi-location ,则会创建 plugins 子文件夹
里面生成 相应的 jar 文件,可以创建 plugins 同级文件夹 configuration ,其内部
新建 config.ini 配置文件,在 eclipse 的 plugins 目录找到 org.eclipse.osgi.***.jar
复制到与 configuration 同级,然后打开终端到当前位置
# java -jar org.eclipse.osgi.***.jar -console
Bundle 模式,通过设置 Export 导出,隐藏了除接口外的实现,调用者只能看到公共接口部分
项目引用 org.osgi.service.event org.eclipse.osgi.services org.eclipse.equinox.event
获取 org.osgi.service.event.EventAdmin 服务 getServiceReference(EventAdmin.class.getName())
可以实现 osgi 框架内事件 发布/订阅
发布事件
EventAdmin ea = ...
ea.sendEvent(event); 同步方式提交事件
ea.postEvent(event); /// event instanceof org.osgi.service.event.Event = true 异步
class MovingEvent extends Event { MovingEvent() { super("EVENT_TOPIC", properties); } }
接收监听事件
class MovingEventHandler implements EventHandler { }
Hashtable<String,String[]> properties = ...
properties.put(EventConstants.EVENT_TOPIC, new String[] { "EVENT_TOPIC" });
bundleContext.registerService("", new MovingEventHandler(), properties); 注册事件监听对象
OSGi + HTTP 服务 org.osgi.service.http javax.servlet javax.servlet.http
HpptService hs = bundleContext.getService(bundleContext.getServiceReference(HttpService.class.getName()));
hs.registerResources("alias", "directory", hs.createDefaultHttpContext()); 类似静态资源映射
hs.registerServlet("alias", servlet, properties, hs.createDefaultHttpContext()); 映射Servlet访问路径
Servlet jsp=new ContextPathServletAdaptor(new JspServlet(context.getBundle(), "/web/"),"/jsp");
hs.registerServlet("/jsp/*.jsp", jsp, properties, hc);
扩展点XML配置 plugin.xml
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
<extension point="org.eclipse.equinox.http.registry.servlets">
<servlet alias="/location_servlet"
class="a.b.c.d.LocationServiceServlet"></servlet>
<servlet alias="/jsp/*.jsp"
class="org.eclipse.equinox.jsp.jasper.registry.JSPFactory:/Webroot/jsp">
</servlet>
</extension>
<extension point="org.eclipse.equinox.http.registry.resources">
<resource alias="/html" base-name="/Webroot/html"/>
</extension>
</plugin>
该方式则还需在 Activator 中显示的调用 registerXXX 来注册 资源 及 Servlet 等
Servlet 嵌入 OSGi 框架
Equinox 官方典型模式,在 web.xml 加入 Servlet
<servlet id="bridge">
<servlet-name>equinoxbridgeservlet</servlet-name>
<display-name>Equinox Bridge Servlet</display-name>
<servlet-class>org.eclipse.equinox.servletbridge.BridgeServlet</servlet-class>
<init-param>
<param-name>commandline</param-name>
<param-value>-console</param-value>
</init-param>
enableFrameworkControls true
extendedFrameworkExports
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>equinoxbridgeservlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>equinoxbridgeservlet</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>