目前我正在尝试更新一个旧项目。 问题是,在我的来源之一(bungeecord)中,他们已将两个文件(请参阅枚举“协议(protocol)”)从公共(public)最终更改为最终修饰符。为了使项目再次运行,我需要访问这两个字段。
因此,我尝试“注入(inject)”该项目。这很好用,所以修改器发生了变化,但我目前无法将其保存到 jar 文件中。但这是必要的。
保存过程非常适合“userconnection”(请参见下面的枚举)。在本例中,我编辑了一个类修饰符。
如果您需要更多信息,请告诉我。
当“注入(inject)”(枚举:协议(protocol))完成并检查这些文件的修饰符类型时,我发现发生了一些变化。 但是当我重新启动系统并在“注入(inject)”之前再次检查提交的修改器时,它们是因为没有任何更改。
public static int inject(InjectionType type) {
try{
System.out.println("Starting injection.");
System.out.println(type.getInfo());
ClassPool cp = ClassPool.getDefault();
CtClass clazz = cp.getCtClass(type.getClazz().getName());
switch (type) {
case USERCONNECTION:
int modifier = UserConnection.class.getModifiers();
if (!Modifier.isFinal(modifier) && Modifier.isPublic(modifier)) {
return -1;
}
clazz.setModifiers(Modifier.PUBLIC);
break;
case PROTOCOL:
CtField field = clazz.getField("TO_CLIENT");
field.setModifiers(Modifier.PUBLIC + Modifier.FINAL);
field = clazz.getField("TO_SERVER");
field.setModifiers(Modifier.PUBLIC + Modifier.FINAL);
break;
default:
return -1; //no data
}
ByteArrayOutputStream bout;
DataOutputStream out = new DataOutputStream(bout = new ByteArrayOutputStream());
clazz.getClassFile().write(out);
InputStream[] streams = { new ByteArrayInputStream(bout.toByteArray()) };
File bungee_file = new File(BungeeCord.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
updateZipFile(bungee_file, type, streams);
return 1;
}catch (Exception e){
e.printStackTrace();
}
return 0;
}
private static void updateZipFile(File zipFile, InjectionType type, InputStream[] ins) throws IOException {
File tempFile = File.createTempFile(zipFile.getName(), null);
if (!tempFile.delete()) {
System.out.println("Warn: Cant delete temp file.");
}
if (tempFile.exists()) {
System.out.println("Warn: Temp target file alredy exist!");
}
if (!zipFile.exists()) {
throw new RuntimeException("Could not rename the file " + zipFile.getAbsolutePath() + " to " + tempFile.getAbsolutePath() + " (Src. not found!)");
}
int renameOk = zipFile.renameTo(tempFile) ? 1 : 0;
if (renameOk == 0) {
tempFile = new File(zipFile.toString() + ".copy");
com.google.common.io.Files.copy(zipFile, tempFile);
renameOk = 2;
if (zipFile.delete()) {
System.out.println("Warn: Src file cant delete.");
renameOk = -1;
}
}
if (renameOk == 0) {
throw new RuntimeException("Could not rename the file " + zipFile.getAbsolutePath() + " to " + tempFile.getAbsolutePath() + " (Directory read only? (Temp:[R:" + (tempFile.canRead() ? 1 : 0) + ";W:" + (tempFile.canWrite() ? 1 : 0) + ",D:" + (tempFile.canExecute() ? 1 : 0) + "],Src:[R:" + (zipFile.canRead() ? 1 : 0) + ";W:" + (zipFile.canWrite() ? 1 : 0) + ",D:" + (zipFile.canExecute() ? 1 : 0) + "]))");
}
if (renameOk != 1) {
System.out.println("Warn: Cant create temp file. Use .copy file");
}
byte[] buf = new byte[Configuration.getLoadingBufferSize()];
System.out.println("Buffer size: " + buf.length);
ZipInputStream zin = new ZipInputStream(new FileInputStream(tempFile));
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFile));
ZipEntry entry = zin.getNextEntry();
while (entry != null) {
String path_name = entry.getName().replaceAll("/", "\\.");
boolean notReplace = true;
for (String f : type.getNames()) {
if (f.equals(path_name)) {
notReplace = false;
break;
}
}
if (notReplace) {
out.putNextEntry(new ZipEntry(entry.getName()));
int len;
while ((len = zin.read(buf)) > 0) {
out.write(buf, 0, len);
}
}
entry = zin.getNextEntry();
}
zin.close();
for (int i = 0; i < type.getNames().length; i++) {
InputStream in = ins[i];
int index = type.getNames()[i].lastIndexOf('.');
out.putNextEntry(new ZipEntry(type.getNames()[i].substring(0, index).replaceAll("\\.", "/") + type.getNames()[i].substring(index)));
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
out.closeEntry();
in.close();
}
out.close();
tempFile.delete();
if (renameOk == -1) {
System.exit(-1);
}
}
}
@Getter
public enum InjectionType {
USERCONNECTION(UserConnection.class, new String[] {"net.md_5.bungee.UserConnection.class"}, "Set modifiers for class UserConnection.class to \"public\""),
PROTOCOL(Protocol.class, new String[] {"net.md_5.bungee.protocol.Protocol"}, "Set modifiers for class Protocol.class to \"public\"");
private Class<?> clazz;
private String[] names;
private String info;
InjectionType (Class<?> clazz, String[] names, String info) {
this.clazz = clazz;
this.names = names;
this.info = info;
}
}
最佳答案
When the "injection" (enum: protocol) is done and I check the modifier type of these fileds I see that there have been some changes. But when I restart the system and check the filed modifiers again before the "injection" they are as there were no changes.
您想要做的是使用 Java 反射永久修改 jar 文件中的字段访问权限。这不起作用,因为反射仅在运行时修改内容:
Reflection is an API which is used to examine or modify the behavior of methods, classes, interfaces at runtime.
摘自this page .
如果您希望更改永久有效,您需要做的是物理编辑 jar 本身。我知道你说你做不到,但据我所知这是唯一可能的方法。如果您希望在应用程序终止后保留更改并在程序启动之前应用更改,则必须对文件本身进行物理更改。
阅读有关Java反射的官方文档here .
但是我不太明白为什么在重新启动系统后更改仍然存在很重要。您需要更改访问权限的原因是您可以在运行时以某种方式访问并可能操作该类。您所做的事情是正确的,反射更重要的方面之一是操作数据而实际上不必修改物理文件本身并最终使用自定义发行版。
编辑:阅读 this问题,评论和接受的答案。他们几乎说的是同样的事情,即您无法编辑 JVM 当前正在使用的 jar 文件,它被锁定在只读状态。
关于java - 运行时编辑并保存其他 jar,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56354944/