问题描述
我有以下课程:
package some.clazz.client;
import some.clazz.SomeClass;
public class SomeClassClient {
...
public SomeClass getProc();
...
}
我已经从 SomeClassClient
类字节码中删除/缩小/删除了此 getProc()
Java方法通过使用 new MemberRemoval().stripMethods(ElementMatcher);
ByteBuddy转换在 net.bytebuddy:byte-buddy-maven-plugin
Maven插件中.但是 import some.clazz.SomeClass;
语句仍然存在,并由 CFR Java Decompiler
显示!
I've removed/shrunk/deleted this getProc()
Java method from SomeClassClient
class bytecodeby using new MemberRemoval().stripMethods(ElementMatcher);
ByteBuddy transformationin net.bytebuddy:byte-buddy-maven-plugin
Maven Plugin.But import some.clazz.SomeClass;
statement is still present and shown by CFR Java Decompiler
!
在 SomeClassClient
类中没有对 SomeClass
类的其他引用.
There are no any another reference to SomeClass
class in SomeClassClient
class.
如何从字节码中删除此导入语句(真的,我假设它位于常量池中)?因为尝试使用"SomeClassClient"类时仍会收到ClassNotFoundException.
How can I remove this import statement from bytecode (really I'm assuming it's located in constant pool)?Because I'm still getting ClassNotFoundException when trying to use 'SomeClassClient' class.
我的课
public class MethodsRemover implements net.bytebuddy.build.Plugin {
...
@Override
public DynamicType.Builder<?> apply(DynamicType.Builder<?> builder,
TypeDescription typeDescription,
ClassFileLocator classFileLocator) {
try{
return builder.visit(new MemberRemoval().stripMethods(
ElementMatchers.any().and(
isAnnotatedWith(Transient.class)
.and(
t -> {
log.info(
"ByteBuddy transforming class: {}, strip method: {}",
typeDescription.getName(),
t
);
return true;
}
)
).or(
target -> Arrays.stream(STRIP_METHODS).anyMatch(
m -> {
Class<?> methodReturnType = getMethodReturnType(m);
String methodName = getMethodName(m);
Class<?>[] methodParameters = getMethodParameters(m);
return
isPublic()
.and(returns(
isVoid(methodReturnType)
? is(TypeDescription.VOID)
: isSubTypeOf(methodReturnType)
))
.and(named(methodName))
.and(isNoParams(m)
? takesNoArguments()
: takesArguments(methodParameters)
)
.and(t -> {
log.info(
"ByteBuddy transforming class: {}, strip method: {}",
typeDescription.getName(),
t
);
return true;
}).matches(target)
;
}
)
)
));
...
}
我添加了以下EntryPoint并在bytebuddy插件中对其进行了配置以供使用:
I've added the following EntryPoint and configured it in bytebuddy plugin to use:
public static class EntryPoint implements net.bytebuddy.build.EntryPoint {
private net.bytebuddy.build.EntryPoint typeStrategyEntryPoint = Default.REDEFINE;
public EntryPoint() {
}
public EntryPoint(net.bytebuddy.build.EntryPoint typeStrategyEntryPoint) {
this.typeStrategyEntryPoint = typeStrategyEntryPoint;
}
@Override
public ByteBuddy byteBuddy(ClassFileVersion classFileVersion) {
return typeStrategyEntryPoint
.byteBuddy(classFileVersion)
.with(ClassWriterStrategy.Default.CONSTANT_POOL_DISCARDING)
.ignore(none()); // Traverse through all (include synthetic) methods of type
}
@Override
public DynamicType.Builder<?> transform(TypeDescription typeDescription,
ByteBuddy byteBuddy,
ClassFileLocator classFileLocator,
MethodNameTransformer methodNameTransformer) {
return typeStrategyEntryPoint
.transform(typeDescription, byteBuddy, classFileLocator, methodNameTransformer);
}
}
推荐答案
最终,我发明了一种变通方法,该方法可处理合成桥方法,同时仍使用ElementMatcher-s选择要删除的方法..如上面@Rafael Winterhalter(作者)在其评论中所述:当前(当前为v1.10.22)版本的Byte-Buddy lib无法使用其现有的MemberRemoval类处理桥接方法.因此,只需按以下方式将其扩展为删除/剥离方法即可:
Eventually I've invented a workaround that allows to handle the synthetic bridge methods and at the same time still to use ElementMatcher-s to select methods to remove...As mentioned @Rafael Winterhalter (author) above in its comment: Byte-Buddy lib at its current (v1.10.22 at the moment) version does not handle bridge methods by using its existing MemberRemoval class. So just extend it to remove/strip methods in the following manner:
package com.pany.of.yours.byte.buddy;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.ClassFileVersion;
import net.bytebuddy.asm.MemberRemoval;
import net.bytebuddy.build.Plugin;
import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.field.FieldList;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.method.MethodList;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.scaffold.ClassWriterStrategy;
import net.bytebuddy.dynamic.scaffold.MethodGraph;
import net.bytebuddy.dynamic.scaffold.inline.MethodNameTransformer;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.jar.asm.ClassVisitor;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.pool.TypePool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static net.bytebuddy.matcher.ElementMatchers.is;
import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
import static net.bytebuddy.matcher.ElementMatchers.isBridge;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.isSubTypeOf;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.none;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments;
...
public class MethodsRemover implements Plugin {
private static final Logger log = LoggerFactory.getLogger(MethodsRemover.class);
private static final Object[][] STRIP_METHODS = {
{SomeClass.class, "getProc", void.class} //,
// other methods here
};
public MethodsRemover() {
}
@Override
public boolean matches(TypeDescription typeDefinitions) {
// return typeDefinitions.getName().equals("pkg.SomeClass");
return typeDefinitions.isAssignableTo(SomeClassSuper.class) }
@Override
public DynamicType.Builder<?> apply(DynamicType.Builder<?> builder,
TypeDescription typeDescription,
ClassFileLocator classFileLocator) {
try{
log.info(" ByteBuddy processing type =========> {}", typeDescription);
return builder.visit(new MemberRemovalEx().stripMethods(
ElementMatchers.none()// <= or you can use ElementMatchers.any();
.or(t -> { // <= + .and(..) - as a start point instead.
log.debug("ByteBuddy processing method --> {}", t);
return false;
})
.or(
isAnnotatedWith(Transient.class)
.and(t -> {
log.info(
" ByteBuddy strip transient method ++> {}",
t
);
return true;
})
)
.or(
target -> Arrays.stream(STRIP_METHODS).anyMatch(
m -> {
Class<?> methodReturnType = getMethodReturnType(m);
String methodName = getMethodName(m);
Class<?>[] methodParameters = getMethodParameters(m);
return
isPublic()
.and(returns(
isVoid(methodReturnType)
? is(TypeDescription.VOID)
: isSubTypeOf(methodReturnType)
))
.and(named(methodName))
.and(isNoParams(m)
? takesNoArguments()
: takesArguments(methodParameters)
)
.and(t -> {
log.info(
" ByteBuddy strip signature method ++> {}",
t
);
return true;
}).matches(target)
;
}
)
)
));
} catch (Exception e) {
log.error("ByteBuddy error: ", e);
throw e;
}
}
...
public static class EntryPoint implements net.bytebuddy.build.EntryPoint {
private net.bytebuddy.build.EntryPoint typeStrategyEntryPoint = Default.REDEFINE;
public EntryPoint() {
}
public EntryPoint(net.bytebuddy.build.EntryPoint typeStrategyEntryPoint) {
this.typeStrategyEntryPoint = typeStrategyEntryPoint;
}
@Override
public ByteBuddy byteBuddy(ClassFileVersion classFileVersion) {
return typeStrategyEntryPoint
.byteBuddy(classFileVersion)
.with(MethodGraph.Compiler.Default.forJVMHierarchy()) // Change hashCode/equals by including a return type
.with(ClassWriterStrategy.Default.CONSTANT_POOL_DISCARDING) // Recreate constants pool
.ignore(none()); // Traverse through all (include synthetic) methods of type
}
@Override
public DynamicType.Builder<?> transform(TypeDescription typeDescription,
ByteBuddy byteBuddy,
ClassFileLocator classFileLocator,
MethodNameTransformer methodNameTransformer) {
return typeStrategyEntryPoint
.transform(typeDescription, byteBuddy, classFileLocator, methodNameTransformer);
}
}
private class MemberRemovalEx extends MemberRemoval {
private final Junction<FieldDescription.InDefinedShape> fieldMatcher;
private final Junction<MethodDescription> methodMatcher;
public MemberRemovalEx() {
this(ElementMatchers.none(), ElementMatchers.none());
}
public MemberRemovalEx(Junction<FieldDescription.InDefinedShape> fieldMatcher,
Junction<MethodDescription> methodMatcher) {
super(fieldMatcher, methodMatcher);
this.fieldMatcher = fieldMatcher;
this.methodMatcher = methodMatcher;
}
@Override
public MemberRemoval stripInvokables(ElementMatcher<? super MethodDescription> matcher) {
return new MemberRemovalEx(this.fieldMatcher, this.methodMatcher.or(matcher));
}
@Override
public ClassVisitor wrap(TypeDescription instrumentedType,
ClassVisitor classVisitor,
Implementation.Context implementationContext,
TypePool typePool,
FieldList<FieldDescription.InDefinedShape> fields,
MethodList<?> methods,
int writerFlags,
int readerFlags) {
MethodList<MethodDescription.InDefinedShape> typeBridgeMethods =
instrumentedType.getDeclaredMethods().filter(isBridge());
int bridgeMethodCount = typeBridgeMethods.size();
if (bridgeMethodCount > 0) {
List<MethodDescription> methodsPlusBridges = new ArrayList<>(
methods.size() + bridgeMethodCount
);
methodsPlusBridges.addAll(typeBridgeMethods);
methodsPlusBridges.addAll(methods);
methods = new MethodList.Explicit<>(methodsPlusBridges);
}
return super.wrap(
instrumentedType,
classVisitor,
implementationContext,
typePool,
fields,
methods,
writerFlags,
readerFlags
);
}
}
}
这也是使用的字节码Maven插件配置:
And also here is the used byte-buddy Maven plugin configuration:
<build>
<plugins>
<plugin>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-maven-plugin</artifactId>
<version>${byte-buddy-maven-plugin.version}</version>
<executions>
<execution>
<id>byte.buddy.strip.methods</id>
<phase>process-classes</phase>
<goals>
<goal>transform</goal>
</goals>
<configuration>
<transformations>
<transformation>
<!-- Next plugin transformer removes @Transient annotated and some predefined methods from entities -->
<plugin>com.pany.of.yours.byte.buddy.MethodsRemover</plugin>
<!-- Optionally, specify groupId, artifactId, version of the class -->
</transformation>
</transformations>
<!-- Optionally, add 'initialization' block with EntryPoint class -->
<initialization>
<entryPoint>
com.pany.of.yours.byte.buddy.MethodsRemover$EntryPoint
</entryPoint>
</initialization>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>some.your.aux.dependency.group</groupId>
<artifactId>dependency-artifact</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
这篇关于如何删除/缩小“导入some.clazz.SomeClass;"Java中通过字节码操作库/框架进行声明?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!