java从jar包中读取资源⽂件
楔⼦
⼀个JAVA项⽬需要在应⽤启动时读取⼀个⽂件,在这遇到了⼀个坑,就是在Idea 中,应⽤启动时可以正常读取这个⽂件,但应⽤打成jar 包后直接运⾏就读取不到。
要读取的⽂件位于/src/main/resources⽬录下,其相对路径为/src/main/resources/HotleAllCity.json如下图所⽰:
IDE中读取
IDE中的读取⽅式是,先获取⽂件的路径,然后读取⽂件
// path = Resource(fileName).getPath();
try
{
// classpath:fileName
path = File(ResourceUtils.CLASSPATH_URL_PREFIX + fileName)
.getAbsoluteFile().getPath();
}
catch(FileNotFoundException e)
{
throw new ServiceException(PublicErrorCodeEnum.ERR_Code(), fileName +"⽂件不存在");
}
IDE中编译后的⽬录为:
从上图我们可以看到,开发时期的项⽬⾥,src/main/下⾯的java和resources⽂件夹都被(编译)打包到了⽣产包的classes/⽬录下.
这个时候,我们可以看到classes这个⽂件夹,它就是我们要的classpath
举例:
l使⽤classpath:这种前缀,就只能代表⼀个⽂件,表⽰只会到你的class路径中查第⼀个匹配的⽂件。
java学习资源classpath*:**/mapper/mapping/*l,使⽤classpath*:这种前缀,则可以代表多个匹配的⽂件,不仅包含class路径,还包括jar⽂件中(class路径)进⾏查。**/mapper/mapping/*l,双星号**表⽰在任意⽬录下,也就是说在classes/下任意层的⽬录,只要符合后⾯的⽂件路径,都会被作为资源⽂件到。
classpath的讲解详解下⼩结。
以上代码在IDE中能够完美的运⾏。但是在jar包中就会如下的错误:
错误的意思是在⽂件系统中不到json⽂件,但是⽂件的路径是对的,该⽂件在lib中的jar包中,jar包中的⽂件如下所⽰:
代码之所以不能读取Jar包中的⽂件,是因为整个jar包时⼀个⽂件,所以⾥边的东西你是⽆法再⽂件系统中访问的。
什么是classpath
在java项⽬中,你⼀定碰到过classpath,通常情况下,我们是⽤它来指定配置/资源⽂件的路径。
顾名思义,classpath就是class的path,也就是类⽂件(*.class的路径),CLASSPATH环境变量的作⽤是⽤来指定Java程序搜索类的路径的。
安装完JDK后我们通常需要配置环境变量CLASSPATH,通常配置为.;%JAVA_HOME%\lib\tools.jar;%JAVA_HOME%\lib\dt.jar
,它包含了当前⽬录.。
dt.jar:运⾏环境类库,主要是Swing包,这⼀点通过⽤压缩软件打开dt.jar也可以看到。如果在开发时候没有⽤到Swing包,那么可以不⽤将dt.jar添加到CLASSPATH变量中。
tools.jar:⼯具类库,它跟我们程序中⽤到的基础类库没有关系。我们注意到在Path中变量值bin⽬录下的各个exe⼯具的⼤⼩都很⼩,⼀般都在27KB左右,这是因为它们实际上仅仅相当于是⼀层代码的包装,这些⼯具的实现所要⽤到的类库都在tools.jar中,⽤压缩软件打开tools.jar,你会发现有很多⽂件是和bin⽬录下的exe⼯具相对性的,如下图:
读取jar包中的⽂件
之所以不能读取Jar包中的⽂件,这主要是因为jar包是⼀个单独的⽂件⽽⾮⽂件夹,绝对不可能通过file:/e:/.../ResourceJar.jar/resource
/这种形式的⽂件URL来定位。所以即使是相对路径,也⽆法定位到jar⽂件内的txt⽂件。
我们可以⽤类装载器(ClassLoader)来读取jar包中的⽂件。
我们需要利⽤Class().getResourceAsStream⽅法,以流的形式拿到Jar包中的⽂件,正确写法如下所⽰:
BufferedReader in =new BufferedReader(new Class().getClassLoader().getResourceAsStream(path)));
StringBuffer buffer =new StringBuffer();
String line ="";
while((line = in.readLine())!= null){
buffer.append(line);
}
String input = String();
ClassLoader简介
ClassLoader是⼀个抽象类,我们⽤它的实例对象来装载类 ,它负责将 Java 字节码装载到 JVM 中 , 并使其成为 JVM ⼀部分。 JVM 的类动态装载技术能够在运⾏时刻动态地加载或者替换系统的某些功能模块,⽽不影响系统其他功能模块的正常运⾏。
类装载就是寻⼀个类或是⼀个接⼝的字节码⽂件并通过解析该字节码来构造代表这个类或是这个接⼝的 class 对象的过程 。
同时⽀持IDE与JAR包读取
在IEA中⽀持读取资源⽂件能够⽅便我们调试,在jar包中读取资源⽂件能够⽅便我们部署,如何两者同时⽀持?
⼀般我们在Windows下调试,在Linux下部署,所以我们可以通过判断操作系统类型来同时⽀持。
例如:
public static List<City>getCriteriaHotCity()
{
if(CollectionUtils.isEmpty(criteriaHotCity))
{
// 通过不同的OS来进⾏不同⽅式的读取
Property("os.name").toLowerCase().startsWith("win"))
{
String filePath =getCriteriaFilePath(FileName.CRITERIA_HOT_CITY_JSON);
criteriaHotCity = adFileToList(filePath, City.class);
}
else
{
String content =readFile(FileName.CRITERIA_HOT_CITY_JSON);
criteriaHotCity = JSONObject.parseArray(content, City.class);
}
}
return criteriaHotCity;
}
private static String getCriteriaFilePath(String fileName)
{
String path;
// path = Resource(fileName).getPath();
try
{
path = File(ResourceUtils.CLASSPATH_URL_PREFIX + fileName)
.
getAbsoluteFile().getPath();
}
catch(FileNotFoundException e)
{
throw new ServiceException(PublicErrorCodeEnum.ERR_Code(), fileName +"⽂件不存在");
}
return path;
}
private static String readFile(String fileName)
{
String ret;
try
{
ret =readJarFile(fileName);
}
}
catch(IOException e)
{
throw new ServiceException(PublicErrorCodeEnum.ERR_Code(), e.getMessage());
}
return ret;
}
/**
*  读取jar包中的资源⽂件
* @param fileName ⽂件名
* @return ⽂件内容
* @throws IOException 读取错误
*/
private static String readJarFile(String fileName)throws IOException
{
BufferedReader in =new BufferedReader(
new InputStreamReader(ClassLoader().getResourceAsStream(fileName)));
StringBuilder buffer =new StringBuilder();
String line;
while((line = in.readLine())!= null)
{
buffer.append(line);
}
String();
}
参考⽂献

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。