FatJar技术
概念
将一个jar及其依赖的三方jar全部打到一个包中,这个包即为FatJar。
作用
作用: Jar包隔离,避免Jar冲突。
打包方式
- maven-shade-plugin插件;
- spring-boot-maven-plugin插件(Spring Boot打包插件);
嵌套Jar资源加载方案
思路:扩展Jar URL协议+定制ClassLoader;
扩展Jar URL协议
问题: JDK内置的Jar URL协议只支持一个’!/’,需要扩展此协议使其支持多个’!/’,以便能够加载jar in jar的资源,如下所示:
jar:file:/data/spring-boot-theory/BOOT-INF/lib/spring-aop-5.0.4.RELEASE.jar!/org/springframework/aop/SpringProxy.class
jar:file:/data/spring-boot-theory.jar!/BOOT-INF/lib/spring-aop-5.0.4.RELEASE.jar!/org/springframework/aop/SpringProxy.class
解决方案:定制协议处理器Handler,定制规则查看另一篇,SpringBoot实现如下图所示:
定制ClassLoader
加载class:重载loadclass,添加对应的包路径;
加载其它资源:使用URLClassLoader原有逻辑即可;
SpringBoot提供了LaunchedURLClassLoader ,实现如下所示:
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.loader;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.util.Enumeration;
import java.util.jar.JarFile;
import org.springframework.boot.loader.jar.Handler;
/**
* {@link ClassLoader} used by the {@link Launcher}.
*
* @author Phillip Webb
* @author Dave Syer
* @author Andy Wilkinson
*/
public class LaunchedURLClassLoader extends URLClassLoader {
static {
ClassLoader.registerAsParallelCapable();
}
/**
* Create a new {@link LaunchedURLClassLoader} instance.
* @param urls the URLs from which to load classes and resources
* @param parent the parent class loader for delegation
*/
public LaunchedURLClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
@Override
public URL findResource(String name) {
Handler.setUseFastConnectionExceptions(true);
try {
return super.findResource(name);
}
finally {
Handler.setUseFastConnectionExceptions(false);
}
}
@Override
public Enumeration<URL> findResources(String name) throws IOException {
Handler.setUseFastConnectionExceptions(true);
try {
return new UseFastConnectionExceptionsEnumeration(super.findResources(name));
}
finally {
Handler.setUseFastConnectionExceptions(false);
}
}
@Override
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
Handler.setUseFastConnectionExceptions(true);
try {
try {
definePackageIfNecessary(name);
}
catch (IllegalArgumentException ex) {
// Tolerate race condition due to being parallel capable
if (getPackage(name) == null) {
// This should never happen as the IllegalArgumentException indicates
// that the package has already been defined and, therefore,
// getPackage(name) should not return null.
throw new AssertionError("Package " + name + " has already been "
+ "defined but it could not be found");
}
}
return super.loadClass(name, resolve);
}
finally {
Handler.setUseFastConnectionExceptions(false);
}
}
/**
* Define a package before a {@code findClass} call is made. This is necessary to
* ensure that the appropriate manifest for nested JARs is associated with the
* package.
* @param className the class name being found
*/
private void definePackageIfNecessary(String className) {
int lastDot = className.lastIndexOf('.');
if (lastDot >= 0) {
String packageName = className.substring(0, lastDot);
if (getPackage(packageName) == null) {
try {
definePackage(className, packageName);
}
catch (IllegalArgumentException ex) {
// Tolerate race condition due to being parallel capable
if (getPackage(packageName) == null) {
// This should never happen as the IllegalArgumentException
// indicates that the package has already been defined and,
// therefore, getPackage(name) should not have returned null.
throw new AssertionError(
"Package " + packageName + " has already been defined "
+ "but it could not be found");
}
}
}
}
}
private void definePackage(String className, String packageName) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
String packageEntryName = packageName.replace('.', '/') + "/";
String classEntryName = className.replace('.', '/') + ".class";
for (URL url : getURLs()) {
try {
URLConnection connection = url.openConnection();
if (connection instanceof JarURLConnection) {
JarFile jarFile = ((JarURLConnection) connection)
.getJarFile();
if (jarFile.getEntry(classEntryName) != null
&& jarFile.getEntry(packageEntryName) != null
&& jarFile.getManifest() != null) {
definePackage(packageName, jarFile.getManifest(), url);
return null;
}
}
}
catch (IOException ex) {
// Ignore
}
}
return null;
}, AccessController.getContext());
}
catch (java.security.PrivilegedActionException ex) {
// Ignore
}
}
/**
* Clear URL caches.
*/
public void clearCache() {
for (URL url : getURLs()) {
try {
URLConnection connection = url.openConnection();
if (connection instanceof JarURLConnection) {
clearCache(connection);
}
}
catch (IOException ex) {
// Ignore
}
}
}
private void clearCache(URLConnection connection) throws IOException {
Object jarFile = ((JarURLConnection) connection).getJarFile();
if (jarFile instanceof org.springframework.boot.loader.jar.JarFile) {
((org.springframework.boot.loader.jar.JarFile) jarFile).clearCache();
}
}
private static class UseFastConnectionExceptionsEnumeration
implements Enumeration<URL> {
private final Enumeration<URL> delegate;
UseFastConnectionExceptionsEnumeration(Enumeration<URL> delegate) {
this.delegate = delegate;
}
@Override
public boolean hasMoreElements() {
Handler.setUseFastConnectionExceptions(true);
try {
return this.delegate.hasMoreElements();
}
finally {
Handler.setUseFastConnectionExceptions(false);
}
}
@Override
public URL nextElement() {
Handler.setUseFastConnectionExceptions(true);
try {
return this.delegate.nextElement();
}
finally {
Handler.setUseFastConnectionExceptions(false);
}
}
}
}
加载步骤
- 注册定制Handler;
- 获取当前Jar包及其嵌套Jar包URL;
- 创建ClassLoader,进行资源加载;
使用示例代码,如下所示:
import java.net.URL;
import java.util.List;
import com.cainiao.iots.client.utils.loader.ExecutableArchiveLauncher;
import com.cainiao.iots.client.utils.loader.archive.Archive;
import com.cainiao.iots.client.utils.loader.jar.JarFile;
public class IotClientLauncher extends ExecutableArchiveLauncher {
static final String BOOT_INF_LIB = "sar/jars/";
private ClassLoader classLoader;
@Override
protected boolean isNestedArchive(Archive.Entry entry) {
return entry.getName().startsWith(BOOT_INF_LIB);
}
@Override
protected void launch(String[] args) throws Exception {
//step1:注册handler
JarFile.registerUrlProtocolHandler();
//step2:获取当前Jar包及其嵌套Jar包URL
List<Archive> archives = getClassPathArchives();
for(int i = 0; i < archives.size(); i++){
System.out.println("Archive url: " + archives.get(i).getUrl());
}
//step3:创建ClassLoader
this.classLoader = createClassLoader(archives);
}
public ClassLoader getClassLoader() {
return classLoader;
}
public static void main(String[] args) throws Exception {
//1. 创建ClassLoader
IotClientLauncher launcher = new IotClientLauncher();
launcher.launch(args);
ClassLoader loader = launcher.getClassLoader();
//2. 加载jar in jar的资源
URL url = loader.getResource("1.jpg");
Class<?> clazz = loader.loadClass("*.*.*");
}
}
参考:
原文地址:https://blog.csdn.net/yangguosb/article/details/80764971
FatJar技术的更多相关文章
- 阿里Java架构师打包 FatJar 方法小结
在函数计算(Aliyun FC)中发布一个 Java 函数,往往需要将函数打包成一个 all-in-one 的 zip 包或者 jar 包.Java 中这种打包 all-in-one 的技术常称之为 ...
- JAVA核心知识点--打包 FatJar 方法小结
目录 什么是 FatJar 三种打包方法 1. 非遮蔽方法(Unshaded) 2. 遮蔽方法(Shaded) 3. 嵌套方法(Jar of Jars) 小结 参考阅读 原文地址:https://yq ...
- SpringBoot究竟是如何跑起来的?
摘要: 神奇的SpringBoot. 原文:SpringBoot 究竟是如何跑起来的? 作者:老钱 Fundebug经授权转载,版权归原作者所有. 不得不说 SpringBoot 太复杂了,我本来只想 ...
- 关于解决python线上问题的几种有效技术
工作后好久没上博客园了,虽然不是很忙,但也没学生时代闲了.今天上博客园,发现好多的文章都是年终总结,想想是不是自己也应该总结下,不过现在还没想好,等想好了再写吧.今天写写自己在工作后用到的技术干货,争 ...
- SQL Server技术内幕笔记合集
SQL Server技术内幕笔记合集 发这一篇文章主要是方便大家找到我的笔记入口,方便大家o(∩_∩)o Microsoft SQL Server 6.5 技术内幕 笔记http://www.cnbl ...
- 本人提供微软系.NET技术顾问服务,欢迎企业咨询!
背景: 1:目前微软系.NET技术高端人才缺少. 2:企业很难直接招到高端技术人才. 3:本人提供.NET技术顾问,保障你的产品或项目在正确的技术方向. 技术顾问服务 硬服务项: 1:提供技术.决策. ...
- 分布式锁1 Java常用技术方案
前言: 由于在平时的工作中,线上服务器是分布式多台部署的,经常会面临解决分布式场景下数据一致性的问题,那么就要利用分布式锁来解决这些问题.所以自己结合实际工作中的一些经验和网上看到的一些资 ...
- 【大型网站技术实践】初级篇:借助LVS+Keepalived实现负载均衡
一.负载均衡:必不可少的基础手段 1.1 找更多的牛来拉车吧 当前大多数的互联网系统都使用了服务器集群技术,集群即将相同服务部署在多台服务器上构成一个集群整体对外提供服务,这些集群可以是Web应用服务 ...
- 探真无阻塞加载javascript脚本技术,我们会发现很多意想不到的秘密
下面的图片是我使用firefox和chrome浏览百度首页时候记录的http请求 下面是firefox: 下面是chrome: 在浏览百度首页前我都将浏览器的缓存全部清理掉,让这个场景最接近第一次访问 ...
随机推荐
- Codeforces 414A
题目链接 首先考虑无解的情况: n / 2 > k 或者 n==1 且 k != 0 (因为两个数的最大公约数最小为1) 然后因为有 n / 2 组(把 a[i] 和 a[i+1] 看成一组), ...
- PLUTO平台是由美林数据技术股份有限公司下属西安交大美林数据挖掘研究中心自主研发的一款基于云计算技术架构的数据挖掘产品,产品设计严格遵循国际数据挖掘标准CRISP-DM(跨行业数据挖掘过程标准),具备完备的数据准备、模型构建、模型评估、模型管理、海量数据处理和高纬数据可视化分析能力。
http://www.meritdata.com.cn/article/90 PLUTO平台是由美林数据技术股份有限公司下属西安交大美林数据挖掘研究中心自主研发的一款基于云计算技术架构的数据挖掘产品, ...
- golang数据类型三
- 【JZOJ4921】【NOIP2017提高组模拟12.10】幻魔皇
题目描述 幻魔皇拉比艾尔很喜欢斐波那契树,他想找到神奇的节点对. 所谓斐波那契树,根是一个白色节点,每个白色节点都有一个黑色节点儿子,而每个黑色节点则有一个白色和一个黑色节点儿子.神奇的节点对则是指白 ...
- 遗传算法MATLAB实现(2):一元函数优化举例
遗传算法提供了一种求解非线性.多模型.多目标等复杂系统优化问题的通用框架. 先从例子开始,慢慢再总结理论... [例]利用遗传算法计算函数f(x)=x*cos(5*pi*x)+3.5在区间[-1,2. ...
- Directx11教程(33) 纹理映射(3)
原文:Directx11教程(33) 纹理映射(3) 现在我们在myTutorialD3D11_5的基础上,来逐步编码实现纹理映射,之所以在myTutorialD3D11_5基础上改写,是 ...
- docker-ce 安装和卸载
一.按照官网给的安装方法进行Ubuntu16.04 docker-ce 的安装,步骤如下: 1.由于apt官方库里的docker版本可能比较旧,所以先卸载可能存在的旧版本: sudo apt-get ...
- mysql锁机制之表锁(三)
顾名思义,表锁就是一锁锁一整张表,在表被锁定期间,其他事务不能对该表进行操作,必须等当前表的锁被释放后才能进行操作.表锁响应的是非索引字段,即全表扫描,全表扫描时锁定整张表,sql语句可以通过执行计划 ...
- SWF在线绘本批量制作高质量PDF的新方法(重点在批量制作)
SWF在线绘本批量制作高质量PDF的新方法(重点在批量制作) 2012-12-21 未来决定... http://www.ebama.net/thread-107643-1-1.html ...
- Java中try catch finally语句中含return语句的执行情况总结-编程陷阱
前言:有java编程基础的人对java的异常处理机制都会有一定了解,而且可能感觉使用起来也比较简单,但如果在try catch finally语句块中遇到return语句,开发者可能就会遇到一些逻辑问 ...