skywalking
产品)。我总结的学习步骤如下
字节码的知识—>ASM的知识—>JVM一些知识(类的加载,运行,卸载,类型)—> JNI(调用使用第三方代码) —> jdk attach的原理 —> jdk.instrument 知识 —> bytebuddy的知识。
这是我个人整理的一些资料
《深入理解字节码》
水平暂时不够,不写了。但是看代码的顺序是对的。
鬼写的bytebuddy。
整体的讲解是自下而上,一层一层递进着讲的。后面的内容的理解依赖于前面的内容。
这里重点讲bytebuddy如何封装类型的,这个是其他模块的基石
。
最好具备
字节码
的知识,以及Class类详解的知识- 从bytebuddy开发指南了解到源码的组织结构
代码位置
在模块byte-buddy-dev/description包是有关类型的描述
Class类详解中有更详细的介绍
名称 | 描述 | 备注 |
---|---|---|
annotation | 注解 | |
enumeration | 枚举 | |
field | filed成员 | |
method | 方法成员 | |
modifier | 修饰符,本质是一个2byte的int,代表一个方法的的描述。比如 public 对应的是 0X0001 | |
modifier | 修饰符,本质是一个2byte的int,代表一个方法的的描述。比如 public 对应的是 0X0001 | |
type | 类型,类型的类型。对Class进行分类 |
bytebuddy的代码比较整齐,所有的类基本都是如下的模式。
package bytecode.annotation;
public interface ByteBuddyTemplate {
// 接口层
public void method();
// 抽象层
public abstract class AbstractBase implements ByteBuddyTemplate {
public void method() {
System.out.println("base");
}
}
// 实现层
public static class sub01 extends AbstractBase{
@Override
public void method() {
super.method();
System.out.println("sub01");
}
}
public static class sub02 extends AbstractBase {
@Override
public void method() {
super.method();
System.out.println("sub02");
}
}
// 实现层- 常用枚举模式
enum sub03 implements ByteBuddyTemplate{
INSTANCE;
@Override
public void method() {
}
}
}
好处是有个清晰的结构,从接口的定义出发,再细致到具体的实现,都在一个类中实现。缺点是类很臃肿,一个类基本都是1000+或者10000+行。
class 以及class成员的字节码都有u2 access_flags
字段,modifier就是代表着access_flags
。
修饰符的核心类,内包含几个实现,复合1.1的设计模式
会讲明白,bytebuddy是如何定义修饰符的,耐心看。
核心方法
public interface ModifierContributor {
/**
* 空的mask
*/
int EMPTY_MASK = 0;
/**
* 获取mask,mask是有效位数
*/
int getMask();
/**
* 获取屏蔽位
*/
int getRange();
/**
* 这个是否是最初的 modifier
*/
boolean isDefault();
}
示意图
标准的access_flags只有16bit,但是asm提供了超过16位的标识,这个只对asm有用,asm 写入字节码时,会讲多余的忽虑掉。
比如field字段有意义的标识符只有9位(asm中是10位)。使用mask
可以添加没有的标志符
这个是field的mask
// Opcodes.ACC_PUBLIC 就是public 0x0001
int MASK = Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE
| Opcodes.ACC_DEPRECATED | Opcodes.ACC_ENUM | Opcodes.ACC_FINAL | Opcodes.ACC_STATIC
| Opcodes.ACC_SYNTHETIC | Opcodes.ACC_TRANSIENT | Opcodes.ACC_VOLATILE;
mask
是规范中有效的位数,range
是额外的屏蔽位。
public int resolve(int modifiers) {
for (T modifierContributor : modifierContributors) {
modifiers = (modifiers & ~modifierContributor.getRange()) | modifierContributor.getMask();
}
return modifiers;
}
modifier
~modifierContributor.getRange()
运算。加入你要屏蔽掉ACC_PUBLIC 0X0001
。那么取反码,第一位就变成了0,其余为1,结果为二进制11111111111111111111111111111110
(modifiers & ~modifierContributor.getRange())
,public的位置就变成了0。|mask
。注意这里是或,也就是说有一位为1,就是1。所以range
的作用是屏蔽位
示例
下面是自定义的一个ModifierContributor
实现。DemoModifierContributor.Resolver. resolve(int modifier))
是根据ModifierContributor
对传入的modifier做修改。
public static
代表着 1001
range
处理(1001 & ~0001) ,结果是 1000
1010
所以rang
代表屏蔽,mask
代表额外的添加
public class DemoModifierContributor implements ModifierContributor {
public static String demo = "hello;";
@Override
public int getMask() {
// 仅有前四位有效的mask 即 0000000000001010
int MASK = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC;
return MASK;
}
@Override
public int getRange() {
// 屏蔽调 public
return Opcodes.ACC_PUBLIC;
}
@Override
public boolean isDefault() {
return false;
}
public static void main(String[] args) throws Exception{
// 打印demo 的modifier
int modifier = DemoModifierContributor.class.getDeclaredField("demo").getModifiers();
System.out.println("origin modifier of demo : "+ Integer.toBinaryString(modifier));
// mask
DemoModifierContributor demo = new DemoModifierContributor();
System.out.println("mask : "+ Integer.toBinaryString(demo.getMask()));
// range
System.out.println("range : "+ Integer.toBinaryString(demo.getRange()));
// resolver 用来获取 有效的 modifier
List<ModifierContributor> list = Collections.singletonList(new DemoModifierContributor());
DemoModifierContributor.Resolver resolver = DemoModifierContributor.Resolver.of(list);
// 获取 (modifiers & ~modifierContributor.getRange()) | modifierContributor.getMask();
// (1001 & ~0001)|1100 --> (1001 & 1110)|1010 --> 1000|1010 --> 1010
System.out.println( Integer.toBinaryString(resolver.resolve(modifier)));
}
}
//打印
origin modifier of demo : 1001
mask : 1010
range : 1
1010
内置了四个子接口ForField
,ForMethod
,ForType
,ForParameter
。
通过了解自定义ModifierContributor的子类。就很好理解四个实现,很类似。比如
仅仅是定义了10位mask
interface ForField extends ModifierContributor {
/**
* A mask for all legal modifiers of a Java field.
*/
int MASK = Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE
| Opcodes.ACC_DEPRECATED | Opcodes.ACC_ENUM | Opcodes.ACC_FINAL | Opcodes.ACC_STATIC
| Opcodes.ACC_SYNTHETIC | Opcodes.ACC_TRANSIENT | Opcodes.ACC_VOLATILE;
}
用来处理int modifiers
。
创建时接受多个ModifierContributor
public static <S extends ModifierContributor> Resolver<S> of(Collection<? extends S> modifierContributors) {
return new Resolver<S>(modifierContributors);
}
使用时,调用resolve获取处理过的modifiers。
public int resolve(int modifiers) {
for (T modifierContributor : modifierContributors) {
modifiers = (modifiers & ~modifierContributor.getRange()) | modifierContributor.getMask();
}
return modifiers;
}
这个包里面接口无一例外都是实现ModifierContributor
的类
挑一个说明FieldManifestation
就是来判断一个Field
是否被final,volatile
或者修饰,
public enum FieldManifestation implements ModifierContributor.ForField {
/**
* Modifier for a non-final, non-volatile field. (This is the default modifier.)
*/
PLAIN(EMPTY_MASK),
/**
* Modifier for a final field.
*/
FINAL(Opcodes.ACC_FINAL),
/**
* Modifier for a volatile field.
*/
VOLATILE(Opcodes.ACC_VOLATILE);
/**
* The mask the modifier contributor.
*/
private final int mask;
/**
* Creates a new field manifestation.
*
* @param mask The modifier mask of this instance.
*/
FieldManifestation(int mask) {
this.mask = mask;
}
/**
* {@inheritDoc}
*/
public int getMask() {
return mask;
}
/**
* {@inheritDoc}
*/
public int getRange() {
return Opcodes.ACC_FINAL | Opcodes.ACC_VOLATILE;
}
/**
* {@inheritDoc}
*/
public boolean isDefault() {
return this == PLAIN;
}
/**
* Returns {@code true} if this manifestation represents a {@code final} type.
*
* @return {@code true} if this manifestation represents a {@code final} type.
*/
public boolean isFinal() {
return (mask & Opcodes.ACC_FINAL) != 0;
}
/**
* Returns {@code true} if this manifestation represents a {@code volatile} type.
*
* @return {@code true} if this manifestation represents a {@code volatile} type.
*/
public boolean isVolatile() {
return (mask & Opcodes.ACC_VOLATILE) != 0;
}
/**
* Returns {@code true} if this manifestation represents a field that is neither {@code final} or {@code volatile}.
*
* @return {@code true} if this manifestation represents a field that is neither {@code final} or {@code volatile}.
*/
public boolean isPlain() {
return !isFinal() && !isVolatile();
}
上面讲了修饰符的各种实现类,就是一个汇总类,负责统筹上面的修饰符。也是开发中真正被使用的
,一般不会直接使用1.2中的类。
这个类理解起来不复杂,比如 ForFieldDescription
内置了一堆方法来判断针对field的修饰符。
interface ForFieldDescription extends OfEnumeration {
/**
* Specifies if the modifier described by this object
*/
boolean isVolatile();
/**
*/
boolean isTransient();
/**
*/
FieldManifestation getFieldManifestation();
/**
*/
FieldPersistence getFieldPersistence();
}
实现了ForFieldDescription
的AbstractBase
,看看如何实现isVolatile()
。
可以看出就是 和Opcodes.ACC_VOLATILE 0x0040;
去 &
,看看相应位是否设为1
abstract class AbstractBase implements ForTypeDefinition, ForFieldDescription, ForMethodDescription, ForParameterDescription {
public boolean isVolatile() {
return matchesMask(Opcodes.ACC_VOLATILE);
}
private boolean matchesMask(int mask) {
return (getModifiers() & mask) == mask;
}
description 中其他package(除了modifier),都实现了这几个接口。先讲modifier是因为modifier被这几个接口使用了。
字节码的二进制中每个块都有对应的名称。NamedElement
就代表了这个结构,方便的获取块的名称。以下是介绍各个函数的作用,以及给了一个实现类 ,查看调用的效果。
public interface NamedElement {
/**
* element 没有名称
*/
String NO_NAME = null;
/**
* 源码中没有名称
*/
String EMPTY_NAME = "";
/**
* 源码中的真实名称,如果没有的话,就会返回EMPTY_NAME 。比如`pubclic
* String demo`,demo就是真实名称
*/
String getActualName();
在NamedElement中实现NamedElement 的内置接口
名称 | 接口 | |
---|---|---|
WithRuntimeName | String getName() | 返回对于running Java application可见的字节码中的名称 |
java runtime期间,被命名单元的名称 | getInternalName() | 返回Java class file format中可见的字节码名称 |
WithOptionalName | ||
标志字节码元素是否被明确命名 | ||
WithGenericName | toGenericString() | 字节码元素的泛型名称 |
泛型风格的名称 | ||
WithDescriptor | getDescriptor() | 返回字节码元素的描述符 |
返回文件描述符和泛型签名 | getGenericSignature() | 如果是泛型就返回泛型名称 |
从一个实现看上面的作用
ForLoadedField
是实现了NamedElement的三个接口
public class NamedElementTest {
public String demo = "one";
public void print() throws Exception {
Field demoField = NamedElementTest.class.getDeclaredField("demo");
FieldDescription.ForLoadedField loadedField = new FieldDescription.ForLoadedField(demoField);
System.out.println("getActualName() : " + loadedField.getActualName());
// 实现了NamedElement中的三个内置实现
System.out.println("impl form WithRuntimeName : " + (loadedField instanceof NamedElement.WithRuntimeName));
System.out.println("getName() : " + loadedField.getName());
System.out.println("getInternalName() : " + loadedField.getInternalName());
System.out.println("impl form WithDescriptor : " + (loadedField instanceof NamedElement.WithDescriptor));
System.out.println("getName() : " + loadedField.getDescriptor());
System.out.println("getGenericSignature() : " + loadedField.getGenericSignature());
System.out.println("impl form WithGenericName : " + (loadedField instanceof NamedElement.WithGenericName));
System.out.println("toGenericString() : " + loadedField.toGenericString());
System.out.println("impl form WithOptionalName : " + (loadedField instanceof NamedElement.WithOptionalName));
}
public static void main(String[] args) throws Exception {
new NamedElementTest().print();
}
}
打印
getActualName() : demo
impl form WithRuntimeName : true
getName() : demo
getInternalName() : demo
impl form WithDescriptor : true
getName() : Ljava/lang/String;
getGenericSignature() : null
impl form WithGenericName : true
toGenericString() : public java.lang.String bytebuddys.NamedElementTest.demo
impl form WithOptionalName : false
返回类型的定义,TypeDefinition
后面会讲到,这个接口也是最上层的接口之一。
TypeDefinition
是bytebuddy对java 原生Type
的封装,也是代表了类的类型。
public interface DeclaredByType {
TypeDefinition getDeclaringType();
}
顺理成章讲到这个类,这个类继承了上面讲到的所有类,除了AnnotationSource
暂时理解为封装注解的类,用来处理注解。后面会在介绍注解时会详细介绍。
ByteCodeElement
从单词的字面就可以理解,这就代表这一个 字节吗的一个元素,实现可以是field
,或者method
,等。
核心的方法
名称 | 接口 | |
---|---|---|
boolean isVisibleTo(TypeDescription typeDescription) | 对于传入的类型是否可见,比如同级的classLoader加载类,就互相看不见 | |
boolean isAccessibleTo(TypeDescription typeDescription) | 对于传入的类型是否权限访问,比如其他类看不见privite |
举例说明
DemoModifierContributor.class和ByteCodeElementTest.class在同一个包。
ByteCodeElementTest有两个方法public
和privite
。DemoModifierContributor 可以看见和访问public
, 但是看不见privite
。
ForLoadedMethod
是ByteCodeElement
的一个实现。下来就来展示看一下。
package bytebuddys;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import java.lang.reflect.Method;
public class ByteCodeElementTest {
private void notSee() {
}
public void canSee() {
}
public void test() throws Exception {
Method notsee = ByteCodeElementTest.class.getDeclaredMethod("notSee");
Method cansee = ByteCodeElementTest.class.getDeclaredMethod("canSee");
MethodDescription.ForLoadedMethod notSeeBD = new MethodDescription.ForLoadedMethod(notsee);
MethodDescription.ForLoadedMethod canSeeBD = new MethodDescription.ForLoadedMethod(cansee);
// 同包下的另外一个类
TypeDescription samePkgAnotherClass = TypeDescription.ForLoadedType.of(DemoModifierContributor.class);
System.out.println("samePkgAnotherClass cant see privite : " + notSeeBD.isVisibleTo(samePkgAnotherClass));
System.out.println("samePkgAnotherClass cant use privite : " + notSeeBD.isAccessibleTo(samePkgAnotherClass));
System.out.println("samePkgAnotherClass can see public : " + canSeeBD.isVisibleTo(samePkgAnotherClass));
System.out.println("samePkgAnotherClass can use public : " + canSeeBD.isAccessibleTo(samePkgAnotherClass));
}
public static void main(String[] args) throws Exception{
new ByteCodeElementTest().test();
}
}
打印
samePkgAnotherClass cant see privite : false
samePkgAnotherClass cant use privite : false
samePkgAnotherClass can see public : true
samePkgAnotherClass can use public : true
这两个类位于ByteCodeElement,但是和ByteCodeElement没有继承或者实现关系。
Token,其实就是代表真实的字节码。熟悉ASM的不会陌生。ASM的CoreAPI接受ClassVister便利class字节码。Token也是类似的,被用来便利字节码。
一个Token就是一个字节码元素。
/**
* A token representing a byte code element.
*
* @param The type of the implementation.
*/
interface Token<T extends Token<T>> {
/**
* Transforms the types represented by this token by applying the given visitor to them.
*
* @param visitor The visitor to transform all types that are represented by this token.
* @return This token with all of its represented types transformed by the supplied visitor.
*/
T accept(TypeDescription.Generic.Visitor<? extends TypeDescription.Generic> visitor);
/**
* A list of tokens.
*
* @param The actual token type.
*/
class TokenList<S extends Token<S>> extends FilterableList.AbstractBase<S, TokenList<S>> {
/**
* The tokens that this list represents.
*/
private final List<? extends S> tokens;
/**
* Creates a list of tokens.
*
* @param token The tokens that this list represents.
*/
@SuppressWarnings("unchecked")
public TokenList(S... token) {
this(Arrays.asList(token));
}
/**
* Creates a list of tokens.
*
* @param tokens The tokens that this list represents.
*/
public TokenList(List<? extends S> tokens) {
this.tokens = tokens;
}
/**
* Transforms all tokens that are represented by this list.
*
* @param visitor The visitor to apply to all tokens.
* @return A list containing the transformed tokens.
*/
public TokenList<S> accept(TypeDescription.Generic.Visitor<? extends TypeDescription.Generic> visitor) {
List<S> tokens = new ArrayList<S>(this.tokens.size());
for (S token : this.tokens) {
tokens.add(token.accept(visitor));
}
return new TokenList<S>(tokens);
}
@Override
protected TokenList<S> wrap(List<S> values) {
return new TokenList<S>(values);
}
/**
* {@inheritDoc}
*/
public S get(int index) {
return tokens.get(index);
}
/**
* {@inheritDoc}
*/
public int size() {
return tokens.size();
}
}
}
interface Token>
意味着这个是链式的,一个token继承着上一个token。意味着一个Token,前面有很多限定条件,他必须是谁的子类,这样你能精确识别Token。
访问者模式
Vistitor
我要去拜访朋友。
我有两个爱好,喝酒和抽烟。
小李是我的同学,他爱喝酒。当他问我爱什么,我知道要说喝酒,才能对主人礼貌。
小王是我的同事,他爱抽烟。当他问我爱什么,我知道要说抽烟,才能对主人礼貌
Host
本地有个待客的习惯,当有客人来时要问,你喜欢什么。
小李和小王都懂这个礼貌。
除此之外
小李,在询问前,要先拥抱一下
小王,在询问前,要握手
…
进一步分析,访作为访问者的我,定义了到访不同客人家要执行的行为。
作为主人的小王和小李,仅仅需要接待我,进行礼貌的询问。
package bytebuddys;
public class VisitorMode {
// 拜访者
public interface Visitor {
public void visit(WangHost wangHost);
public void visit(LiHost liHost);
}
public static class MeVisitor implements Visitor {
@Override
public void visit(WangHost wangHost) {
System.out.println(" 抽根烟");
}
@Override
public void visit(LiHost liHost) {
System.out.println(" 来点酒");
}
}
// 主人
public interface Host {
// 接待
public void accept(Visitor v);
// 询问
default public void ask() {
System.out.println(" 欢迎,你想要点什么?");
}
}
public static class WangHost implements Host {
public void accept(Visitor v) {
System.out.println("握手");
ask();
v.visit(this);
}
}
public static class LiHost implements Host {
public void accept(Visitor v) {
System.out.println("拥抱");
ask();
v.visit(this);
}
}
public static void main(String[] args) {
System.out.println("风和日丽的一天 我作为客人");
Visitor me = new MeVisitor();
System.out.println("上午去了小王家,得到了热情的 款待");
Host wang = new WangHost();
wang.accept(me);
System.out.println("下午去了小李家,得到了热情的 款待");
Host li = new LiHost();
li.accept(me);
}
}
打印
风和日丽的一天 我作为客人
上午去了小王家,得到了热情的 款待
握手
欢迎,你想要点什么?
抽根烟
下午去了小李家,得到了热情的 款待
拥抱
欢迎,你想要点什么?
来点酒
当然Token的更复杂,这个设计可以让token像金字塔,一层套一层。接受一个类型作为参数 ,使用accept
,返回下一个token
=当前token
+传入类型
。
Dependant —> 受扶养者
一个复合类。充当ElementMatcher
和Token
的桥梁
interface TypeDependant<T extends TypeDependant<?, S>, S extends ByteCodeElement.Token<S>> {
/**
* Returns this type dependant in its defined shape, i.e. the form it is declared in and without its type variable's resolved.
*
* @return This type dependant in its defined shape.
*/
T asDefined();
/**
* Returns a token representative of this type dependant. All types that are matched by the supplied matcher are replaced by
* {@link net.bytebuddy.dynamic.TargetType} descriptions.
*
* @param matcher A matcher to identify types to be replaced by {@link net.bytebuddy.dynamic.TargetType} descriptions.
* @return A token representative of this type dependant.
*/
S asToken(ElementMatcher<? super TypeDescription> matcher);
}
TypeDependantImpl implments TypeDependant
。TypeDependantImpl
的作用,就是返回定义的类ElementMatcher
变成Token, ElementMatcher后面会说首先java中有一个TypeVariable
,这个是泛型中的变量,比如ListTypeVariable
。
Byte Buddy 中的TypeVariableSource
和TypeVariable
并不对应。这里含义更广:代表了 code element 的类型。
实现了Modifier的接口,具备了判断access_flags
的能力
继承下来判断修饰符的方法
比如
名称 | 描述 |
---|---|
isPublic() | |
isAbstract() | |
isStatic() |
新添加的方法
isInferrable
名称 | 描述 |
---|---|
isInferrable() | 类型是否可以被动态的推断。就是类型可不可义在运行过程中生成,或者更改 |
isGenerified() | 是否是泛型。这个类本身被声明为泛型格式class List ,或者内部的类是泛型,那么都会为真 |
TypeList.Generic getTypeVariables() | 返回内部的泛型 |
TypeVariableSource getEnclosingSource() | 获取外部的包裹,Enclosing,Enclosing指的就是类型位于的位置。Enclosed就是内部方法和类型 |
TypeDescription.Generic findVariable(String symbol) | 根据符号,在TypeVariableSource中查找匹配的类型 |
accept(Visitor |
访问者模式,visitor中封装着处理TypeVariableSource 的逻辑。 |
TypeVariableSource UNDEFINED = null; |
不是方法,UNDEFINED 就代表着空的TypeVariableSource |
示例
先重点说一下getEnclosingSource()
,就是获取外层的包裹类或者方法。
java 自身的getEnclosingMethod()和getEnclosingClass()是一样的效果。
getEnclosingSource()
相当于两者之和,这个是在匿名类时特别有用。
public class TypeVariableSourceTest {
public <T extends Food> T makeFood(T food) {
return food;
}
public Object makeSauce() {
class Sauce {
public void print() {
}
}
return new Sauce();
}
// 食物
public static class Food {
}
public static void main(String[] args) throws Exception {
Method makeFood = TypeVariableSourceTest.class.getDeclaredMethod("makeFood", Food.class);
MethodDescription.ForLoadedMethod makeFoodBD = new MethodDescription.ForLoadedMethod(makeFood);
System.out.println("Enclosing 外围的包裹类" + makeFoodBD.getEnclosingSource().toString());
TypeDescription.ForLoadedType sauceBD = new TypeDescription.ForLoadedType(
new TypeVariableSourceTest().makeSauce().getClass());
System.out.println("Enclosing 外围的包裹方法" + sauceBD.getEnclosingSource());
}
}
makeFood
方法定义在类内,他的包裹类就是TypeVariableSourceTest
makeSauce
方法产生了一个匿名类,对这个匿名类而言,他的包裹方法就是makeSauce
方法
打印
Enclosing 外围的包裹类或者方法class bytebuddys.TypeVariableSourceTest
Enclosing 外围的包裹类public java.lang.Object bytebuddys.TypeVariableSourceTest.makeSauce()
其他方法参照着下面的使用理解
ForLoadedMethod
是一个实现类之一
System.out.println("Inferrce 可否动态修改: " + makeFoodBD.isInferrable());
System.out.println("isGenerified() 是否或者包含泛型: " + makeFoodBD.isGenerified());
System.out.println("Enclosing 外围的包裹类或者方法: " + makeFoodBD.getEnclosingSource().toString());
System.out.println("getTypeVariables() 获取泛型: " + makeFoodBD.getTypeVariables().toString());
System.out.println("Enclosing 外围的包裹类或者方法: " + makeFoodBD.findVariable("T").toString());
打印
Inferrce 可否动态修改: true
isGenerified() 是否或者包含泛型: true
Enclosing 外围的包裹类或者方法: class bytebuddys.TypeVariableSourceTest
getTypeVariables() 获取泛型: [T]
Enclosing 外围的包裹类或者方法: T
interface Visitor<T> {
/**
* Vistor 针对 typeDescription类型动作
*/
T onType(TypeDescription typeDescription);
/** Vistor 针对 methodDescription类型动作
*/
T onMethod(MethodDescription.InDefinedShape methodDescription);
/** NoOp ,op就是操数,NoOp就是什么也不干原样返回
*/
enum NoOp implements Visitor<TypeVariableSource> {
/**
* The singleton instance.
*/
INSTANCE;
/**
* {@inheritDoc}
*/
public TypeVariableSource onType(TypeDescription typeDescription) {
return typeDescription;
}
/**
* {@inheritDoc}
*/
public TypeVariableSource onMethod(MethodDescription.InDefinedShape methodDescription) {
return methodDescription;
}
}
}
方法 | 描述 | |
---|---|---|
TypeDescription.Generic asGenericType() | 转化为泛型 | |
TypeDescription asErasure() | 擦出类型 | |
TypeDescription.Generic getSuperClass() | 返回父类 | |
TypeList.Generic getInterfaces() | 返回实现的接口类型 | |
FieldList> getDeclaredFields() | 返回Fileld类型 | |
MethodList> getDeclaredMethods() | 返回method类型 | |
TypeDefinition getComponentType() | 返回数组的类型,比如String[],就返回代表String的类型 | |
RecordComponentList> getRecordComponents(); | record是jdk 14新特性,类似C的Struct | |
String getTypeName() | record是jdk 14新特性,类似C的Struct | |
StackSize getStackSize() | 栈帧大小 | |
boolean represents(Type type); | 传入对象是否相等 | |
Sort getSort() | 返回对象代表的一堆类型,Sort是个集合 |
对象类型的常量的集合
名称 | 描述 |
---|---|
NON_GENERIC | 非泛型 |
GENERIC_ARRAY | 泛型数组 |
PARAMETERIZED | 参数 |
WILDCARD | WildcardType是Type的子接口,用于描述形如“? extends classA” 或 “?super classB”的“泛型参数表达式”。List extends String>这种类型就叫WildcardType |
VARIABLE | 代表了被绑定到net.bytebuddy.description.TypeVariableSource 上的类型 |
VARIABLE_SYMBOLIC | 代表了一个类型,但是仅仅是一个符号,并不会被绑定到net.bytebuddy.description.TypeVariableSource |
public static TypeDescription.Generic describe(Type type)
protected static TypeDescription.Generic describe(Type type, TypeDescription.Generic.AnnotationReader annotationReader) {
if (type instanceof Class<?>) {
return new TypeDescription.Generic.OfNonGenericType.ForLoadedType((Class<?>) type, annotationReader);
} else if (type instanceof GenericArrayType) {
return new TypeDescription.Generic.OfGenericArray.ForLoadedType((GenericArrayType) type, annotationReader);
} else if (type instanceof ParameterizedType) {
return new TypeDescription.Generic.OfParameterizedType.ForLoadedType((ParameterizedType) type, annotationReader);
} else if (type instanceof TypeVariable) {
return new TypeDescription.Generic.OfTypeVariable.ForLoadedType((TypeVariable<?>) type, annotationReader);
} else if (type instanceof WildcardType) {
return new TypeDescription.Generic.OfWildcardType.ForLoadedType((WildcardType) type, annotationReader);
} else {
throw new IllegalArgumentException("Unknown type: " + type);
}
}
类型描述类,也是承载具体类型的定义
Field
预定义的几种类型。
名称 | 描述 |
---|---|
OBJECT | ForLoadedType(Object.class) |
STRING | ForLoadedType(String.class) |
PARAMETERIZED | 参数 |
CLASS | ForLoadedType(Class.class) |
THROWABLE | ForLoadedType(Throwable.class) |
VOID | ForLoadedType(void.class) |
ARRAY_INTERFACES | TypeList.Generic.ForLoadedTypes(Cloneable.class, Serializable.class) |
UNDEFINED | TypeDescription UNDEFINED = null |
方法
这里调从字面意思不容易看出来的
名称 | 描述 |
---|---|
boolean isInstance(Object value) | 是否是给定对象的实例 |
boolean isAssignableFrom(Class> type) | 是否是从当前对象继承而来,包括当前。Foo 和class Bar extends Foo ,Foo.class.isAssignableFrom(Bar.class) 返回true |
boolean isAssignableTo(Class> type) | 刚好相反 |
boolean isInHierarchyWith(Class> type) | 一个类是否在另一个类的继承体系中 |
MethodDescription.InDefinedShape getEnclosingMethod() | 外层的方法 |
getActualModifiers(boolean superFlag) | 返回class file中的修饰符 |
boolean isAnonymousType() | 是否是匿名类 |
boolean isMemberType() | 是否是成员类型,Field、method、constructors |
PackageDescription getPackage() | 获取包的描述符 |
AnnotationList getInheritedAnnotations() | 获取继承的注解 |
int getInnerClassCount() | 获取内部类的数量 |
TypeDescription asBoxed() | 装箱类,Intger |
TypeDescription asUnboxed() | 拆箱类,int |
这个是泛型和非泛型的共同父类,不是字面意义上的泛型。
可以看到这个泛型非常之复杂。但是确实很有必要去将每一个部分。
前面一层一层不厌其烦的描述一些类的方法和作用。就是为了理解现在这个类,这个类又对理解后面的类很重要。
但是重点是理解
,并不是全部罗列。
之前解释Visitor,就是让大家明白。visitor是真正定义动作的地方。
NoOp
就是统一对类型不加工,传入什么样返回什么样。TypeErasing
类型擦除,对传入的类型进行类型参数,遇到wildcard type
抛出异常AnnotationStripper
类型注解去除,去除一个类型所有注解类型Assigner
这里可以翻译为,归属于。前面也有isAssignableFrom
,就是用来判断两个类型之间的关系。怎么判断呢,他用了一个模式分发
,Dispatcher
,逐层判断分发。Validator
校验,用来在class file 中校验格式是否正确Reifying
纠正,如果一个类代码字节中的原本的类型(raw types),就是来纠正ForSignatureVisitor
追加类型签名,将新的类型签名,添加到classfile文件中Substitutor
代替,用来代替原始的类型内部实现
declaration context
中取消某个变量的锚定,这个是通过ElementMatcher
提供的targetType
来去匹配的。Token
代替所有的targetType
位置。ForRawType
代表原始类型,类似于擦除。比如List
就被会被转化为原始的类型List
Reducing
接受参数,擦除与参数匹配的类型反射详解中提到了,除了Type类型之外,java还提供了一种注解形式的类型,运行是标注在类型。
AnnotationType
。传统的JVM依旧靠着解析Type类型
来处理class的类型。但是新的JVM依靠注解来驱动类型的解析。
名词解释
之前了解了这么多例子,一般说说一个泛型? extend T
,或者一个类型class extends A implments
。 所以函数中出现 LowerBound就是获取下边界的类型,upperBoundType就是获取上边界。
resolve
是找到的意思
ofXXX
就是创建一个类动作
ForXX
是某个接口的子类之一,针对谁的实现
Dispatcher
ForLegcayVm implments Dispatcher
就是传统解析类的,比如识别一个接口类型
public AnnotationReader resolveInterfaceType(Class<?> type, int index) {
return NoOp.INSTANCE;
}
ForJava8CapableVm implments Dispatcher
CreateAction
就是一个穿件ForJava8CapableVm
的工厂,调用run
,就产生一个ForJava8CapableVm
Delegator
委托模式,是个抽象类,实现了AnnotationReader
,对每个方法委托调用
比如
public AnnotationReader ofWildcardUpperBoundType(int index) {
return new ForWildcardUpperBoundType(this, index);
}
protected ForWildcardUpperBoundType(AnnotationReader annotationReader, int index) {
// 委托
super(annotationReader);
this.index = index;
}
TypeDescription的内置实现类之一。为了描述类型。为了加速,预制了这么多基本类型
对于数组,比如String[]
类型的映射。代表了一个数组类型。注意数组不是集合。
一个潜在的类型描述。对于一个没有任何方法和任何field的类。可以想像得到创建一个类的时候,首先会创建一个空的对象。这个对象就是Latent.
代表了一个包的描述
代表了这样的类型:总是企图去加载委托类的父类。
名字也可以看出来,就是代表加载super类型的工具类的类型。
内置一个List包含对类型的集合。字节码中,很多区域是变长的。比如method区域,他的本地变量大小,和栈深(stacksize)是编译时计算出来的。
这就导致一个问题,我要插入一个方法,我必须要计算前面方法的本地变量表
和栈深
,进行累加,然后确认新method插入的位置。
Byte buddy提供了一个结构TypeList
,可以将多个类型组合,划分为一组。可以得到总的StakSize
。这样我仅需和一个TypeList
比较,而不是挨个计算。
举例
计算一组变量的stackSize
public int getStackSize() {
return StackSize.of(this);
}
// 进行累加
public static int of(Collection<? extends TypeDefinition> typeDefinitions) {
int size = 0;
for (TypeDefinition typeDefinition : typeDefinitions) {
size += typeDefinition.getStackSize().getSize();
}
return size;
描述包的结构的类,典型的会在每个包底下创建package_info.java
来存放包的信息。
核心的方法
boolean contains
(TypeDescription typeDescription);常用的实现类是
ForLoadedPackage
,以下是一个示例。判断类的包含关系。public class ForLoadPkg {
public static void main(String[] args) {
PackageDescription.ForLoadedPackage pkg = new PackageDescription.ForLoadedPackage(ForLoadPkg.class.getPackage());
TypeDescription.ForLoadedType thisclass = new TypeDescription.ForLoadedType(ForLoadPkg.class);
TypeDescription.ForLoadedType anotherclass = new TypeDescription.ForLoadedType(Enums.Demo.class);
System.out.println("package name" + pkg.getName());
System.out.println(pkg.getName() + " contains : " + thisclass.getName() + " : " + pkg.contains(thisclass));
System.out.println(pkg.getName() + " contains : " + anotherclass.getName() + " : " + pkg.contains(anotherclass));
}
}
打印
package namebytebuddys
bytebuddys contains : bytebuddys.ForLoadPkg : true
bytebuddys contains : Enums.Demo : false
继承自 ByteCodeElement.Token
就是Token,真正的字节码
注意 getSymbol()是集成自Generic,是针对泛型的,非泛型使用会报错
System.out.println(
TypeDescription.Generic.OfNonGenericType.ForLoadedType.of(TypeVariableSourceTest.class).getSymbol()
);
打印
Exception in thread "main" java.lang.IllegalStateException: A non-generic type does not imply a symbol: class bytebuddys.TypeVariableSourceTest
at net.bytebuddy.description.type.TypeDescription$Generic$OfNonGenericType.getSymbol(TypeDescription.java:3804)
at bytebuddys.TypeVariableSourceTest.main(TypeVariableSourceTest.java:45)
通过对Type的描述,基本上会清晰的看出bytebuddy是如何对Type进行描述的。
这个类位于description.annotation
是对原先的javaannotation
进行了封装。
Annotation详解有关于注解的定义和使用。
对于一个注解而言,注解
,和注解的赋值
Class.getgetDeclaredAnnotations()
。但是AnnotationList
是一个集合,是Bytebuddy定义的,方便对一组注解管理,和之前的TypeList
类似。Explicit
,因为模糊就意味着无法准确的生成字节码。注解自身有一堆属性值,这个类就是
代表了一个未加载(unloaded)的注解值。这个值的类型可以是:
这个被代表的值不一定是要被找到的(有效的)。 比如可以包含一个不可获取的类型,未知的枚举类,或者不正确的注解。
注意接口定义public interface AnnotationValue
,
是一个泛型,T
代表未加载(unloaded)的类型,S
代表加载过(loaded)的类型。
枚举类,代表了值的类型
UNDEFINED | 未被定义,代表着注解的属性没有找到,并且抛出java.lang.annotation.IncompleteAnnotationException |
UNRESOLVED | 代表着注解的属性者,不合法,但是处于一个异常状态 |
RESOLVED | 被解决,并以找到的真实的value代表 |
AnnotationValue的一个变形。代表一个需要被classLodaer
去加载,但是还没有加载的一个类型。会抛出
java.lang.annotation.IncompleteAnnotationExceptio
:注解的的变量没有赋值,也没有默认值,比如@Student{id=0}
但是也要求{name="XXX"}
时,没有赋值name的异常java.lang.EnumConstantNotPresentException
: 不知道的枚举常量类型,比如用了定义之外的常量java.lang.annotation.AnnotationTypeMismatchException
:annotation property
类型不符合这个接口的实现必须要实现,Object.hashCode()
,Object.toString()
,因为这些都会被
java.lang.annotation.Annotation
的实现用到。也必须要重新实现hashCode()
,equals()
和toString()
确保两个相同的代表同样annotation value
值的实例相等。
interface Loaded<U> {
// 获取状态
State getState();
// 找到真实的注解value,找不到时抛出异常
U resolve();
// 判断两个实例是否是同一个类型
boolean represents(Object value);
}
注意这里的U 往往就是传入一个AnnotationValue
类型
AnnotationValue
的状态filter
(MethodDescription.InDefinedShape property, TypeDefinition typeDefinition);AnnotationValue
不匹配。那么返回一个代表错误类型AnnotationValue
// 举例 一个子类的实现:先判断,不是的话就返回`ForMismatchedType`
public AnnotationValue<U, U> filter(MethodDescription.InDefinedShape property, TypeDefinition typeDefinition) {
return typeDefinition.asErasure().asBoxed().represents(value.getClass()) ? this : new ForMismatchedType<U, U>(property, value.getClass().isArray()
? "Array with component tag: " + RenderingDispatcher.CURRENT.toComponentTag(TypeDescription.ForLoadedType.of(value.getClass().getComponentType()))
: value.getClass().toString() + '[' + value + ']');
}
public interface AnnotationValue
中的T 代表未被加载(unloaded)的类型。reslove就是去调用classLoader去找到这个类型。S
是加载过的类,返回一个加载过的注解值。一个Annotation可以作为另外一个Annotation的属性值。就是代表了成员注解的类型
ForEnumerationDescription :枚举类型
ForTypeDescription: Type类型
ForDescriptionArray: annotation的数组
ForMissingType: 无法加载的类型
ForMismatchedType: 类型不匹配
ForMissingValue: 没有值的注解属性,也没有定义default
ForIncompatibleType: 代表了一个拥有异常的类型。
代表注解的类型。
SOURCE
,CLASS
,RUNTIME
TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE,ANNOTATION_TYPE,PACKAGE,TYPE_PARAMETER,TYPE_USE
java反射中的InvocationHandler。这个是封装注解的InvocationHandler
代表一个早已经加载完成的注解
代表一个定义明确,但是还没有load的注解
分为Method和Parameter 方法和参数类型。
Parameter是 java方法或者构造器的参数。
对于TypeDescription
的缓存池。加载后的各种类型,可以放到Type pool
中。这个类可以存放不实用classLoader加载的类。也就是说可以绕过jvm的记载机制直接从文件加载。
仅有两个方法
TypeDescription
是否可以从文件中找到,Byte Buddy的示例,从ClassPath生成一个Type.
class MyApplication {
public static void main(String[] args) {
TypePool typePool = TypePool.Default.ofClassPath();
new ByteBuddy()
.redefine(typePool.describe("foo.Bar").resolve(), // do not use 'Bar.class'
ClassFileLocator.ForClassLoader.ofClassPath())
.defineField("qux", String.class) // we learn more about defining fields later
.make()
.load(ClassLoader.getSystemClassLoader());
assertThat(Bar.class.getDeclaredField("qux"), notNullValue());
}
}
最新的API已经没有ofClassPath()
了
ClassFileLocator 代表着类的位置
dynamicType是Byte Buddy对生成类的抽象。也是被用来操作和
用来查找类文件。
用来需找类,并返回类定义byte[]
。LocationStrategy
核心就是如何使用ClassFileLocator
这个类。
内部11个实现类
order | return type | method | 描述 |
---|---|---|---|
0 | Resolution | locate(String name) | 根据类的名称,返回一个Resolution 。Resolutiong包含类的信息,比如isResolved 代表是否找到,byte[] resolve() 返回类的字节码定义 |
ClassFileLocator的内部接口Resolution代表类的二进制信息
order | return type | method | 描述 |
---|---|---|---|
0 | boolean | isResolved() | 检查是否存在这个类 |
1 | byte[] | resolve() | 找到类的定义,返回一个byte[] |
找不到时
Resolution的实现类
class Illegal implements
Resolution。可以看到,获取类的二进制信息时直接抛出异常
/**
* {@inheritDoc}
*/
public boolean isResolved() {
return false;
}
/**
* {@inheritDoc}
*/
public byte[] resolve() {
throw new IllegalStateException("Could not locate class file for " + typeName);
}
找到时
Resolution的实现类
class Explicit implements
Resolution。可以看到,获取类的二进制信息
/**
class Explicit implements Resolution {
/**
* The represented data.
*/
private final byte[] binaryRepresentation;
/**
* Creates a new explicit resolution of a given array of binary data.
*
* @param binaryRepresentation The binary data to represent. The array must not be modified.
*/
@SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "The array is not to be modified by contract")
public Explicit(byte[] binaryRepresentation) {
this.binaryRepresentation = binaryRepresentation;
}
/**
* {@inheritDoc}
*/
public boolean isResolved() {
return true;
}
/**
* {@inheritDoc}
*/
@SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "The array is not to be modified by contract")
public byte[] resolve() {
return binaryRepresentation;
}
}
enum NoOp implements ClassFileLocator
找不到对象,返回Illegal
Resolution
class Simple implements ClassFileLocator
,查找类定的简单实现
Filed
private final Map
核心的Map ,一个由类名和 类定义,组成kv的ClassFileLocator
。
method
只写几个,Simple体现在,函数都是接受类定义byte[]
,放入ClassFileLocator
中
比如of
方法
public static ClassFileLocator of(Map<TypeDescription, byte[]> binaryRepresentations) {
Map<String, byte[]> classFiles = new HashMap<String, byte[]>();
for (Map.Entry<TypeDescription, byte[]> entry : binaryRepresentations.entrySet()) {
classFiles.put(entry.getKey().getName(), entry.getValue());
}
return new Simple(classFiles);
}
class ForClassLoader implements ClassFileLocator
需要给定classloader
,在classloader
中找类
Filed
核心就是这个
private final ClassLoader classLoader;
method
核心方法locate
,使用classloader
寻找
protected static Resolution locate(ClassLoader classLoader, String name) throws IOException {
InputStream inputStream = classLoader.getResourceAsStream(name.replace('.', '/') + CLASS_FILE_EXTENSION);
if (inputStream != null) {
try {
return new Resolution.Explicit(StreamDrainer.DEFAULT.drain(inputStream));
} finally {
inputStream.close();
}
} else {
return new Resolution.Illegal(name);
}
}
Module jDK9的特性,比classLoader更高一级
class ForModule implements ClassFileLocator
class ForJarFile implements ClassFileLocator
在jar
包里面寻找class文件。
可以看到locate
方法在jar里需找类的方法
public Resolution locate(String name) throws IOException {
ZipEntry zipEntry = jarFile.getEntry(name.replace('.', '/') + CLASS_FILE_EXTENSION);
if (zipEntry == null) {
return new Resolution.Illegal(name);
} else {
InputStream inputStream = jarFile.getInputStream(zipEntry);
try {
return new Resolution.Explicit(StreamDrainer.DEFAULT.drain(inputStream));
} finally {
inputStream.close();
}
}
}
从 module,Folder,ForURL中寻找class文件
PackageDiscriminating 是包
支持多个classFileLocator
。 locate
方法查找时,也是从classFileLocator
中遍历查找优先,返回第一个。
/**
* The {@link ClassFileLocator}s which are represented by this compound
* class file locator in the order of their application.
*/
private final List<ClassFileLocator> classFileLocators;
/**
* Creates a new compound class file locator.
*
* @param classFileLocator The {@link ClassFileLocator}s to be
* represented by this compound class file locator in the order of their application.
*/
public Compound(ClassFileLocator... classFileLocator) {
this(Arrays.asList(classFileLocator));
}
/**
* Creates a new compound class file locator.
*
* @param classFileLocators The {@link ClassFileLocator}s to be represented by this compound class file locator in
* the order of their application.
*/
public Compound(List<? extends ClassFileLocator> classFileLocators) {
this.classFileLocators = new ArrayList<ClassFileLocator>();
for (ClassFileLocator classFileLocator : classFileLocators) {
if (classFileLocator instanceof Compound) {
this.classFileLocators.addAll(((Compound) classFileLocator).classFileLocators);
} else if (!(classFileLocator instanceof NoOp)) {
this.classFileLocators.add(classFileLocator);
}
}
}
/**
* {@inheritDoc}
*/
public Resolution locate(String name) throws IOException {
for (ClassFileLocator classFileLocator : classFileLocators) {
Resolution resolution = classFileLocator.locate(name);
if (resolution.isResolved()) {
return resolution;
}
}
return new Resolution.Illegal(name);
}
负责加载类的包
负责定义包的属性,当一个包中的类被刚加载时。也可以选择在定义一个包时,不做什么。
包的定义
默认的实现
仅仅返回Define的两个实现
enum NoOp implements PackageDefinitionStrategy {
/**
* The singleton instance.
*/
INSTANCE;
/**
* {@inheritDoc}
*/
public Definition define(ClassLoader classLoader, String packageName, String typeName) {
return Definition.Undefined.INSTANCE;
}
}
enum Trivial implements PackageDefinitionStrategy {
/**
* The singleton instance.
*/
INSTANCE;
/**
* {@inheritDoc}
*/
public Definition define(ClassLoader classLoader, String packageName, String typeName) {
return Definition.Trivial.INSTANCE;
}
}
读取jar包的META-INF/MANIFEST.MF
教程。就是新定义的类如何被动态的加载
重要点是理解
默认会选择
)net.bytebuddy.dynamic.loading.ByteArrayClassLoader
去加载。net.bytebuddy.dynamic.loading.ByteArrayClassLoader
加载
集成classLoder 负责加载类型
classLoader 负责
ClassInjector
将一个类,注入到java.lang.ClassLoader
。
AccessControlContext
执行的。脚手架,这个是使用asm 的API,来生成字节码的。
bytebuddy中,redfine和rebase。还有subclass。
DynamicType
这里只介绍功能,不介绍细节。太复杂了。
java.lang.ClassLoader
加载的类型。Nexus是用于使用LoadedTypeInitializers 初始化类的全局调度程序 。为此,系统类加载器应以显式方式加载此类。然后,任何检测到的类都将一个代码块注入其静态类型初始化器中,该代码块调用此非常相似的联系,该Nexus已预先注册了已加载的类型初始化器。
注意:Nexus
通过将PROPERTYsystem
属性设置为,可以完全禁用类的可用性及其向系统类加载器的注入false。
重要提示:绝对不能直接访问nexus,而只能通过NexusAccessor 来确保系统类加载器已加载nexus。否则,如果某个类由另一个在其层次结构中没有系统类加载器的类加载器加载,则该类可能无法初始化自身。
负责加载类并初始化它LoadedTypeInitializer的。
实施用于确定是否应生成可见性桥的策略。往往调用时,会产生类不可见的问题。
一个brige就是用来充当这个作用的。
不完全是java bridge method,这个能帮助理解桥的作用
transform
(TypeDescription instrumentedType, T target);核心的方法
只有一个方法 matches 被用来和目标类进行匹配
入门介绍
AgentBuilder
暂时水平不够。这玩意到底写了多少啊。