Java動態性的兩種常見方式
-反射java
-字節碼操做:所謂字節碼操做就是當xx.class文件被加載到虛擬機後,咱們可使用類庫來操做這些字節碼編程
運行時操做字節碼可讓咱們實現以下功能
-動態生成新的類
-動態改變某個類的結構(添加/刪除/修改新的屬性/方法框架
優點:
-比反射開銷小,性能高。
-JAVAasist性能高於反射,低於ASMmaven
常見的字節碼操做類庫
BCEL性能
ASMthis
CGLIB(Code Generation Library)spa
Javassist代理
Javassist使用
若是須要詳細瞭解:能夠下載框架code
maven依賴jar包對象
<!-- https://mvnrepository.com/artifact/org.javassist/javassist --> <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.25.0-GA</version> </dependency>
簡單使用
字節碼不能操做對象,若是須要操做對象還須要藉助反射
建立新類
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ScriptException, CannotCompileException, NotFoundException { //得到類池 ClassPool aDefault = ClassPool.getDefault(); //建立類的名字 能夠指定包名 例如:com.zy.Dog CtClass ctClass = aDefault.makeClass("Dog"); //添加屬性(能夠new CtField,和添加方法相似(在test01中說起)) CtField public_int_age = CtField.make("public int age;", ctClass); CtField public_string_like= CtField.make("public String like;", ctClass); ctClass.addField(public_int_age); ctClass.addField(public_string_like); //添加方法 CtMethod test = CtMethod.make("public void test(){System.out.println(\"添加成功\");}", ctClass); ctClass.addMethod(test); //添加構造器 CtConstructor ctConstructor = new CtConstructor(new CtClass[]{CtClass.intType,aDefault.get("java.lang.String")}, ctClass); ctConstructor.setBody("{this.age=$1;this.like=$2;}"); ctClass.addConstructor(ctConstructor); //開始建立一個Dog.class文件,能夠指定Dog.class文件路徑 ctClass.writeFile(); }
操做已經存在的類
一、給存在的.class文件追加方法.讓對象去執行這個方法
public static void test01(){ try { ClassPool aDefault = ClassPool.getDefault(); CtClass ctClass = aDefault.get("com.zy.Dog"); //byte[] bytes = ctClass.toBytecode(); //獲取類名 System.out.println(ctClass.getName()); //獲取簡單類名 System.out.println(ctClass.getSimpleName()); //添加方法 //CtMethod test1 = CtMethod.make("public void test(int a,int b){System.out.println(\"添加成功\");}", ctClass); //另外一種添加方法的方式 CtMethod ctMethod = new CtMethod(CtClass.intType, "t", new CtClass[]{CtClass.intType,CtClass.intType}, ctClass); ctMethod.setModifiers(Modifier.PUBLIC); ctMethod.setBody("return $1+$2;"); ctClass.addMethod(ctMethod); //經過反射調用方法 Class<?> aClass = ctClass.toClass(); Method test = aClass.getDeclaredMethod("t",int.class,int.class); //對象0能夠是外部傳遞進來,此時test方法就是一個代理方法 Object o = aClass.getConstructor().newInstance(); Object invoke = test.invoke(o, 1,1); System.out.println(invoke); } catch (NotFoundException e) { e.printStackTrace(); } catch (CannotCompileException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } }
二、經過字節碼實現AOP編程
此時test02必須是一個代理類的方法。咱們將本身的對象經過test02傳遞進去
public static void test02(){ try { ClassPool aDefault = ClassPool.getDefault(); CtClass ctClass = aDefault.get("com.zy.Dog"); CtMethod play = ctClass.getDeclaredMethod("play"); //能夠對已經存在的方法進行修改 //play.setBody("System.out.println(\"Dog沒有play\");"); //執行方法以前能夠執行其餘的方法; play.insertBefore("System.out.println(\"Dog在吃東西\");"); //反射執行該方法 Class<?> aClass = ctClass.toClass(); Method test = aClass.getDeclaredMethod("play"); Object o = aClass.getConstructor().newInstance(); Object invoke = test.invoke(o); } catch (NotFoundException e) { e.printStackTrace(); } catch (CannotCompileException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } }