It is very convenient to use the ASM package to modify the class file, but unfortunately, ASM does not provide ready-made tools, so we can use the powerful bytecode operation capability provided by it to do it ourselves:
The basic idea is as follows: assume that the operation class is a, and assume that the attribute to be added is Pa. For convenience of operation, construct Class B and add the PA attribute of Class B to Class, with this idea, you can quickly implement the following code:
Bytes ---------------------------------------------------------------------------------------------------------------
First, construct visitor to add the member variable PA:
Public class bytecodeclassfieldadder extends classadapter {
Private final list <fieldnode> fieldnodestoappend;
/**
* Construct for current class
*
* @ Param CV
* @ Param fieldnode
*/
Public bytecodeclassfieldadder (classvisitor CV, list <fieldnode> fieldnodes ){
Super (CV );
This. fieldnodestoappend = fieldnodes;
}
/**
* Visit to the end for current class, append to the vistor class
*
*/
Public void visitend (){
For (fieldnode FN: This. fieldnodestoappend ){
FN. Accept (CV );
}
Super. visitend ();
}
}
Bytes ---------------------------------------------------------------------------------------------------------------
Then construct the operation class for extracting members from another class B:
Public class bytecodeclassfilterutil implements ibytecodecontainer {
Private classnode = NULL;
/**
* Bytecode class filter utility construction
*
* @ Param classfile
* @ Param op
* @ Throws ioexception
*/
Public bytecodeclassfilterutil (final string classfile) throws ioexception {
Fileinputstream FCM = new fileinputstream (classfile );
Classreader Cr = new classreader (FCM );
Bytecodeclassfilter CA = new bytecodeclassfilter (null );
Cr. Accept (Ca, classreader. expand_frames );
If (FS! = NULL ){
FCM. Close ();
}
}
/**
* Bytecode class filter utility construction
*
* @ Param classfile
* @ Param op
* @ Throws ioexception
*/
Public bytecodeclassfilterutil (File classfile) throws ioexception {
Fileinputstream FCM = new fileinputstream (classfile );
Classreader Cr = new classreader (FCM );
Bytecodeclassfilter CA = new bytecodeclassfilter (null );
Cr. Accept (Ca, classreader. expand_frames );
If (FS! = NULL ){
FCM. Close ();
}
}
/**
* Get a specified class node instance for current bytecode class filter Utility
*
* @ Return
*/
Public classnode getclassnode (){
Return this. classnode;
}
/**
* Get a specified field node by a specified name pattern and description Pattern
*
* @ Param name
* @ Return
*/
@ Suppresswarnings ("unchecked ")
Public list <fieldnode> getfieldnode (string namepattern, string descpattern ){
List <fieldnode> returnnodes = new arraylist <fieldnode> ();
List fields = This. classnode. fields;
If (Fields! = NULL ){
For (Object ofield: fields ){
Fieldnode field = (fieldnode) ofield;
Boolean blnnamematch = true;
Boolean blndescmatch = true;
If (namepattern! = NULL ){
Blnnamematch = pattern. Matches (namepattern, field. Name );
}
If (descpattern! = NULL ){
Blndescmatch = pattern. Matches (descpattern, field. DESC );
}
If (blnnamematch & blndescmatch ){
Returnnodes. Add (field );
}
}
}
Return returnnodes;
}
/**
* Get a specified method name or a list of them.
*
* @ Param name
* @ Param description
* @ Return
*/
@ Suppresswarnings ("unchecked ")
Public list <methodnode> getmethodnode (string namepattern, string descpattern ){
List <methodnode> returnnodes = new arraylist <methodnode> ();
List methods = This. classnode. methods;
If (methods! = NULL ){
For (Object omethod: Methods ){
Methodnode method = (methodnode) omethod;
Boolean blnnamematch = true;
Boolean blndescmatch = true;
If (namepattern! = NULL ){
Blnnamematch = pattern. Matches (namepattern, method. Name );
}
If (descpattern! = NULL ){
Blndescmatch = pattern. Matches (descpattern, method. DESC );
}
If (blnnamematch & blndescmatch ){
Returnnodes. Add (method );
}
}
}
Return returnnodes;
}
/**
* Get all of the field descriptions for a specified class
*
* @ Return
*/
@ Suppresswarnings ("unchecked ")
Public list <string> getfielddescription (){
List <string> desclist = new arraylist <string> ();
List fields = This. classnode. fields;
If (Fields! = NULL ){
For (Object ofield: fields ){
Fieldnode field = (fieldnode) ofield;
Stringbuilder sb = new stringbuilder ();
SB. append (field. Name). append (":"). append (field. DESC );
Desclist. Add (sb. tostring ());
}
}
Return desclist;
}
/**
* Get all of the method list for a specified class
*
* @ Return
*/
@ Suppresswarnings ("unchecked ")
Public list <string> getmethoddescription (){
List <string> desclist = new arraylist <string> ();
List methods = This. classnode. methods;
If (methods! = NULL ){
For (Object omethod: Methods ){
Methodnode method = (methodnode) omethod;
Stringbuilder sb = new stringbuilder ();
SB. append (method. Name). append (":"). append (method. DESC );
Desclist. Add (sb. tostring ());
}
}
Return desclist;
}
/**
* Bytecode class filter extend from class adpater class.
*
*/
Class bytecodeclassfilter extends classadapter {
// Construction call for current class
Public bytecodeclassfilter (final classvisitor cv ){
Super (New classnode (){
Public void visitend (){
If (CV! = NULL ){
Accept (CV );
}
}
});
}
// Execute the next operation after this visit ending
Public void visitend (){
Classnode = (classnode) CV;
}
}
}
Bytes ------------------------------------------------------------------------------------------------
Construct the call function to implement the property "transfer" function:
Public void addfieldtoclass (string SRC, string des, string combine, string namefilter, string descfilter)
Throws ioexception {
Bytecodeclassfilterutil util = new bytecodeclassfilterutil (SRC );
List <fieldnode> fields = util. getfieldnode (namefilter, descfilter );
// Visitor current class
If (fields. Size () = 0 ){
System. Out. println ("error: no field is chosen out by the filter .");
} Else {
Classwriter CW = new classwriter (0 );
Bytecodeclassfieldadder adder = new bytecodeclassfieldadder (CW, fields );
Fileinputstream FCM = new fileinputstream (DES );
Classreader Cr = new classreader (FCM );
Cr. Accept (adder, classreader. expand_frames); // need to expand frames for current end user
If (FS! = NULL ){
FCM. Close ();
}
// Convert the specified method into current class
Byte [] bytearray = CW. tobytearray ();
Fileoutputstream Fos = new fileoutputstream (combine );
FOS. Write (bytearray );
FOS. Flush ();
FOS. Close ();
}
}
-------------------------------------------------------------------
Last Mount interface:
Addfieldtoclass (sourcefile, targetfile, destfile, namefilter, descfilter );
Note: sourcefile = B. Class;
Targetfile = A. Class;
Destfile = merged a. Class
Namefilter and descfilter support regular expressions and can be null. If they are null, all fields are added;
The following is a small question:
If it is a static variable that only copies the declared part and does not copy the assigned part, what is the reason?
It turns out that I forgot to copy the cinit () function, but this is part of the next part.