AOP之Javassist应用于自动实现EventBus解读

Posted 小钟视野

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AOP之Javassist应用于自动实现EventBus解读相关的知识,希望对你有一定的参考价值。

解读demo:https://github.com/north2016/T-MVP

一.前言

javassist是一个操作class文件即class字节码的动态类库;在打包过程中,用来检查、”动态”修改以及创建 Java类。其功能与jdk自带的反射功能类似,但比反射功能更强大。

为了方便看,我将javassist的demo剥离出来

二.原理

gradle从1.5开始,gradle插件包含了一个叫Transform的API,这个API允许第三方插件在class文件转为为dex文件前操作编译好的class文件,这个API的目标是简化自定义类操作,而不必处理Task,并且在操作上提供更大的灵活性。并且可以更加灵活地进行操作。 
官方文档:http://google.github.io/android-gradle-dsl/javadoc/ 

demo的原理也是基于此来完成的。即java代码编译后生成class文件,当要打包.class-->.dex时,Javassit库操作.class文件,读取所有的文件的注解,根据注解将EventBus的注解与反注解以及触发事件的方法生成到当前的.class文件中。

三.核心类

ClassPool:javassist的类池,使用ClassPool 类可以跟踪和控制所操作的类,它的工作方式与 JVM 类装载器非常相似, 
CtClass: 提供了检查类数据(如字段和方法)以及在类中添加新字段、方法和构造函数、以及改变类、父类和接口的方法。
CtField:用来访问域 (属性)
CtMethod :用来访问方法 

CtConstructor:用来访问构造器

不过,Javassist 并未提供删除类中字段、方法或者构造函数的任何方法。 

注意限制与局限性:当调用ctClass.toClass()时,修改后的类将被当前的ClassLoader加载并实例化。 在调用ctClass.toClass()时,会加载此类,如果此类在之前已经被加载过,则会报一个duplicate load的错误,表示不能重复加载一个类。所以,修改方法的实现必须在修改的类加载之前进行。不能访问块之外的局部变量。如果在一个方法的开始和结尾都增加了代码段,那么在方法的结尾块中无法访问方法开始中的代码段中的变量(不太完美的解决方法是将原方法改名,然后再增加与原方法同名的方法)。

CtClass.getDeclaredMethods()获取自己申明的方法,CtClass.getMethods()会把所有父类的方法都加上

四.代码详解

注意:修改插件中的代码一定要重新发布,发布之前可先将之前的插件删除,将build删除,重新运行./gradlew -p plugin clean build uploadArchives --info

1.transform插件

1.1在插件module的build.gradle中加入javassist库,并配置发布的插件

apply plugin: 'groovy'
apply plugin: 'maven'

dependencies 
    compile gradleApi()//gradle sdk
    compile localGroovy()//groovy sdk
    compile 'com.android.tools.build:gradle:2.3.1'
//    compile 'com.android.tools.build:transform-api:1.5.0'
    compile 'org.javassist:javassist:3.20.0-GA'//javassist库

uploadArchives 
    repositories.mavenDeployer //重新发布之后,会在本地的repo路径生成仓库(也可发布到远程)可参照http://geek.csdn.net/news/detail/53459
        repository(url: uri('../repo'))
        pom.groupId = 'com.app.plugin'
        pom.artifactId = 'gradleplugin'
        pom.version = '1.0.0'
    


repositories 
    jcenter()


//注意⚠️:   插件修改后运行前需要重新发布: ./gradlew -p plugin clean build uploadArchives --info

1.2继承插件Plugin并将自定义的Transform注册到android中的插件中

public class JavassistPlugin implements Plugin<Project> 

    void apply(Project project) 
        def log = project.logger
        log.error "========================";
        log.error "Javassist开始修改Class!";
        log.error "========================";
        //版本不一样获取的AppExtension方式不一样,这里一定要注意
        //project.android.registerTransform(new JavassistTransform(project)) 这里是无效的
        def android = project.extensions.getByType(AppExtension);
        android.registerTransform(new JavassistTransform(project));
        project.task('transformPath') 
            doLast 
                System.out.println('+++++++++++++++++++++transformPath task')
            
        
    
1.3自定义Transform
public class JavassistTransform extends Transform 
    Project project

    public JavassistTransform(Project project)     // 构造函数,我们将Project保存下来备用
        this.project = project
        project.logger.error("====start===JavassistTransform");
    

    //transformClassesWithMyClassTransformForDebug 运行时的名字
    //transformClassesWith + getName() + For + Debug或Release
    @Override
    String getName() // 设置我们自定义的Transform对应的Task名称
        return com.app.plugin.JavassistTransform.simpleName;
    


    @Override
    // 指定输入的类型,通过这里的设定,可以指定我们要处理的文件类型这样确保其他类型的文件不会传入
    //需要处理的数据类型,有两种枚举类型
    //CLASSES和RESOURCES,CLASSES代表处理的java的class文件,RESOURCES代表要处理java的资源
    Set<QualifiedContent.ContentType> getInputTypes() 
        return Sets.immutableEnumSet(QualifiedContent.DefaultContentType.CLASSES)
    


    @Override
    /****指定Transform的作用范围
     *
     *  指Transform要操作内容的范围,官方文档Scope有7种类型:
     *  EXTERNAL_LIBRARIES            只有外部库
     *  PROJECT                       只有项目内容
     *  PROJECT_LOCAL_DEPS            只有项目的本地依赖(本地jar)
     *  PROVIDED_ONLY                 只提供本地或远程依赖项
     *  SUB_PROJECTS                  只有子项目。
     *  SUB_PROJECTS_LOCAL_DEPS       只有子项目的本地依赖项(本地jar)。
     *  TESTED_CODE                   由当前变量(包括依赖项)测试的代码
     */
    Set<QualifiedContent.Scope> getScopes() 
        return Sets.immutableEnumSet(QualifiedContent.Scope.PROJECT, QualifiedContent.Scope.PROJECT_LOCAL_DEPS,
                QualifiedContent.Scope.SUB_PROJECTS, QualifiedContent.Scope.SUB_PROJECTS_LOCAL_DEPS,
                QualifiedContent.Scope.EXTERNAL_LIBRARIES)
    

    /***当前Transform是否支持增量编译*/
    @Override
    boolean isIncremental() 
        return false
    

    /***
     * Transform中的核心方法,
     * @param context
     * @param inputs 中是传过来的输入流,其中有两种格式,一种是jar包格式一种是目录格式。
     * @param referencedInputs
     * @param outputProvider outputProvider 获取到输出目录,最后将修改的文件复制到输出目录,这一步必须做不然编译会报错
     * @param isIncremental
     * @throws IOException
     * @throws TransformException
     * @throws InterruptedException
     */
    @Override
    void transform(Context context, Collection<TransformInput> inputs,
                   Collection<TransformInput> referencedInputs,
                   TransformOutputProvider outputProvider, boolean isIncremental)
            throws IOException, TransformException, InterruptedException //只有打包apk时才会执行,build不会执行
        def startTime = System.currentTimeMillis();
        project.logger.error("===exe=====transform" + inputs.size());
        // Transform的inputs有两种类型,一种是目录,一种是jar包,要分开遍历
        inputs.each  TransformInput input ->
            try 
                input.jarInputs.each 
                    project.logger.error("===exe=====jarInputs" );
                    MyInject.injectDir(it.file.getAbsolutePath(), BusHelper.PKG_NAME, project)
                    String outputFileName = it.name.replace(".jar", "") + '-' + it.file.path.hashCode()
                    def output = outputProvider.getContentLocation(outputFileName, it.contentTypes, it.scopes, Format.JAR)
                    FileUtils.copyFile(it.file, output)
                
             catch (Exception e) 
                project.logger.error e.getMessage();
            
            //对类型为“文件夹”的input进行遍历
            input.directoryInputs.each  DirectoryInput directoryInput ->
                project.logger.error("===exe=====directoryInputs" );
                //文件夹里面包含的是我们手写的类以及R.class、BuildConfig.class以及R$XXX.class等
                MyInject.injectDir(directoryInput.file.absolutePath, BusHelper.PKG_NAME, project)
                // 获取output目录
                def dest = outputProvider.getContentLocation(directoryInput.name,
                        directoryInput.contentTypes, directoryInput.scopes,
                        Format.DIRECTORY)

                // 将input的目录复制到output指定目录
                FileUtils.copyDirectory(directoryInput.file, dest)
            
        
        ClassPool.getDefault().clearImportedPackages();
        project.logger.error("JavassistTransform cast :" + (System.currentTimeMillis() - startTime) / 1000 + " secs");
    

主要看transform(xxx)方法。当编译打包时,会执行t该方法,通过看代码,主要是遍历两个jar和文件夹列表。随后主要是以下这行代码。

 MyInject.injectDir(it.file.getAbsolutePath(), BusHelper.PKG_NAME, project)
将当前文件夹路径以及当前项目的包名作为参数传进去。注意包名:要有统一的包名的开头,用于自动匹配app\\build\\intermediates\\classes\\debug\\统一包名\\....的包名路径

如这里:BusHelper.PKG_NAME=“me\\”;那么自动匹配app\\build\\intermediates\\classes\\debug\\me\\...

MyInject.injectDir:

/****
     *
     * @param path jar包或者文件夹路径
     * @param packageName 自己的包名,一定要有一个统一的包名
     * @param project
     */
    public static void injectDir(String path, String packageName, Project project) 
        project.logger.error( "p == "+path+" pkg "+packageName);
        pool.appendClassPath(path)
        //project.android.bootClasspath 加入android.jar,否则找不到android相关的所有类
        pool.appendClassPath(project.android.bootClasspath[0].toString());
        Utils.importBaseClass(pool);
        File dir = new File(path)
        if (dir.isDirectory()) //遍历文件夹
            dir.eachFileRecurse  File file ->
                String filePath = file.absolutePath//确保当前文件是class文件,并且不是系统自动生成的class文件
                if (filePath.endsWith(".class") && !filePath.contains('R$') && !filePath.contains('$')//代理类
                        && !filePath.contains('R.class') && !filePath.contains("BuildConfig.class")) 
                    // 判断当前目录是否是在我们的应用包里面
                    int index = filePath.indexOf(packageName);
                    project.logger.error( index+" filePath == "+filePath);//C:\\Users\\Administrator\\Desktop\\AOP\\EventBus-master-javassit\\ViewFinder-master\\sample\\build\\intermediates\\classes\\debug\\me\\brucezz\\viewfinder\\sample\\SecondActivity.class
                    project.logger.error( index+" packageName == "+packageName);
                    boolean isMyPackage = index != -1;
                    project.logger.error( index+"isMyPackage == "+isMyPackage);
                    if (isMyPackage) 
                        //将路径转成包名+类名
                        String className = Utils.getClassName(index, filePath);
                        project.logger.error( "pool : "+pool.toString());
                        project.logger.error( "className : "+className);
                        CtClass c = pool.getCtClass(className)
                        if (c.isFrozen()) c.defrost()
                        BusInfo mBusInfo = new BusInfo()
                        mBusInfo.setProject(project)
                        mBusInfo.setClazz(c)
                        if (c.getName().endsWith("Activity") || c.getSuperclass().getName().endsWith("Activity")) mBusInfo.setIsActivity(true)
                        boolean isAnnotationByBus = false;
                        //getDeclaredMethods获取自己申明的方法,c.getMethods()会把所有父类的方法都加上
                        for (CtMethod ctmethod : c.getDeclaredMethods()) 
                            String methodName = Utils.getSimpleName(ctmethod);
                            if (BusHelper.ON_CREATE.contains(methodName)) mBusInfo.setOnCreateMethod(ctmethod)
                            if (BusHelper.ON_DESTROY.contains(methodName)) mBusInfo.setOnDestroyMethod(ctmethod)
                            for (Annotation mAnnotation : ctmethod.getAnnotations()) 
                                if (mAnnotation.annotationType().canonicalName.equals(BusHelper.OkBusRegisterAnnotation))
                                    mBusInfo.setBusRegisterMethod(ctmethod)
                                if (mAnnotation.annotationType().canonicalName.equals(BusHelper.OkBusUnRegisterAnnotation))
                                    mBusInfo.setBusUnRegisterMethod(ctmethod)
                                if (mAnnotation.annotationType().canonicalName.equals(BusHelper.OkBusAnnotation)) 
                                    project.logger.info " method:" + c.getName() + " -" + ctmethod.getName()
                                    mBusInfo.methods.add(ctmethod)
                                    mBusInfo.annotations.add(mAnnotation)
                                    if (!isAnnotationByBus) isAnnotationByBus = true
                                
                            
                        
                        if (((mBusInfo.BusRegisterMethod != null && mBusInfo.BusUnRegisterMethod == null
                                || mBusInfo.BusRegisterMethod == null && mBusInfo.BusUnRegisterMethod != null)))
                            assert false: Utils.getBusErr()
                        if (mBusInfo != null && isAnnotationByBus) 
                            try 
                                project.logger.error( "intBus path == "+path);
                                BusHelper.intBus(mBusInfo, path)
                             catch (DuplicateMemberException e) 
                            
                        
                        c.detach()//用完一定记得要卸载,否则pool里的永远是旧的代码
                    
                
            
        
    

一行行代码看:

1:

private final static ClassPool pool = ClassPool.getDefault();Utils.importBaseClass(pool); 

 /**
     * 事先载入相关类
     * @param pool
     */
    static void importBaseClass(ClassPool pool) 
        pool.importPackage(LogTimeHelper.LogTimeAnnotation);
        pool.importPackage(BusHelper.OkBusAnnotation);
        pool.importPackage(BusHelper.OkBusRegisterAnnotation);
        pool.importPackage(BusHelper.OkBusUnRegisterAnnotation);
        pool.importPackage("android.os.Bundle");
        pool.importPackage("me.brucezz.viewfinder.sample.event.OkBus")
        pool.importPackage("me.brucezz.viewfinder.sample.event.Event")
        pool.importPackage("android.os.Message")
    

将需要的包名都导入池子中。主要是新增的代码所需要的导入的包。

2:递归遍历文件夹中所有class文件,并根据文件路径和包名开头获取class的包名+类名,从而从javassist库的池子中获取CtClass类操作当前.class

                       
                       
CtClass c = pool.getCtClass(className)
if (c.isFrozen())
    c.defrost()

BusInfo mBusInfo = new BusInfo()
mBusInfo.setProject(project)
mBusInfo.setClazz(c)
if (c.getName().endsWith("Activity") || c.getSuperclass().getName().endsWith("Activity"))
    mBusInfo.setIsActivity(true)

获取当前class实例,并将其解冻使其可用,随后生成BusInfo实例,用以记录增加EventBus注册和反注册以及触发事件方法的注解。再者当前Activity命名必须是以activity结尾或父类的命名是以Activity结尾,这个写一个BaseActivity就可以了。

BusInfo

public class BusInfo 

    Project project//保留当前工程的引用
    CtClass clazz//当前处理的class
    List<CtMethod> methods = new ArrayList<>()//带有Bus注解的方法列表
    List<Annotation> annotations = new ArrayList<>()//带有Bus注解的注解列表
    List<Integer> eventIds = new ArrayList<>()//带有Bus注解的注解id列表
    boolean isActivity = false;//是否是在Activity
    CtMethod OnCreateMethod//Activity或Fragment的初始化方法
    CtMethod OnDestroyMethod//Activity或Fragment的销毁方法
    CtMethod BusRegisterMethod//被Register注解标注的初始化方法
    CtMethod BusUnRegisterMethod//被UnRegister注解标注的销毁方法

接着,遍历当前类的方法是否有使用注解以及是否重写onCreate()和onDestory(),如果有,则将注解信息解析存储到busInfo实例中。

//getDeclaredMethods获取自己申明的方法,c.getMethods()会把所有父类的方法都加上
for (CtMethod ctmethod : c.getDeclaredMethods()) 
    String methodName = Utils.getSimpleName(ctmethod);
    if (BusHelper.ON_CREATE.contains(methodName)) mBusInfo.setOnCreateMethod(ctmethod)
    if (BusHelper.ON_DESTROY.contains(methodName)) mBusInfo.setOnDestroyMethod(ctmethod)
    for (Annotation mAnnotation : ctmethod.getAnnotations()) 
        if (mAnnotation.annotationType().canonicalName.equals(BusHelper.OkBusRegisterAnnotation))
            mBusInfo.setBusRegisterMethod(ctmethod)
        if (mAnnotation.annotationType().canonicalName.equals(BusHelper.OkBusUnRegisterAnnotation))
            mBusInfo.setBusUnRegisterMethod(ctmethod)
        if (mAnnotation.annotationType().canonicalName.equals(BusHelper.OkBusAnnotation)) 
            project.logger.info " method:" + c.getName() + " -" + ctmethod.getName()
            mBusInfo.methods.add(ctmethod)
            mBusInfo.annotations.add(mAnnotation)
            if (!isAnnotationByBus) isAnnotationByBus = true
        
    

最后:解析完信息后,在onCreate()、onDestory()或带有注解的方法实现注册与反注册的EventBus,以及在当前类增加触发事件的方法。

调用BusHelper.intBus(mBusInfo, path):

static void intBus(BusInfo mBusInfo, String path) 
        if (mBusInfo.clazz.isFrozen()) mBusInfo.clazz.defrost()//解冻
        if (mBusInfo.BusRegisterMethod != null) //有被BusRegister注解
            mBusInfo.project.logger.error "BusRegisterMethod not null" +
            mBusInfo.BusRegisterMethod.insertAfter(getRegisterEventMethodStr(mBusInfo));
         else if (mBusInfo.getOnCreateMethod() == null) //没有OnCreateMethod,创建并加上新代码
            mBusInfo.project.logger.quiet "getOnCreateMethod  null "  + mBusInfo.isActivity
            String pre_create_str = mBusInfo.isActivity ? Activity_OnCreate : Fragment_OnCreate;
            String m = pre_create_str + getRegisterEventMethodStr(mBusInfo) + ""
            mBusInfo.project.logger.quiet m
            mBusInfo.project.logger.error m
            CtMethod mInitEventMethod = CtNewMethod.make(m, mBusInfo.clazz);
            mBusInfo.clazz.addMethod(mInitEventMethod)
         else //有OnCreateMethod,直接插入新代码
            mBusInfo.project.logger.error "OnCreateMethod not null"
            mBusInfo.project.logger.quiet "OnCreateMethod not null"
            mBusInfo.OnCreateMethod.insertAfter(getRegisterEventMethodStr(mBusInfo));
        
        if (mBusInfo.BusUnRegisterMethod != null) //有被BusUnRegister注解的方法
            mBusInfo.project.logger.quiet "BusUnRegisterMethod not null"
            mBusInfo.project.logger.error "BusUnRegisterMethod not null"
            mBusInfo.BusUnRegisterMethod.insertAfter(getUnRegisterEventMethodStr(mBusInfo));
         else if (mBusInfo.OnDestroyMethod == null) 
            mBusInfo.project.logger.quiet "OnDestroyMethod null"
            mBusInfo.project.logger.error "OnDestroyMethod null"
            String m = Pre_OnDestroy + getUnRegisterEventMethodStr(mBusInfo) + "";
            mBusInfo.project.logger.quiet m
            CtMethod destroyMethod = CtNewMethod.make(m, mBusInfo.clazz)
            mBusInfo.clazz.addMethod(destroyMethod)
         else 
            mBusInfo.project.logger.quiet "OnDestroyMethod not null"
            mBusInfo.project.logger.error "OnDestroyMethod not null"
            mBusInfo.OnDestroyMethod.insertAfter(getUnRegisterEventMethodStr(mBusInfo));
        

        mBusInfo.clazz.writeFile(path)
    

主要是查看是否有使用注解或是否重写oncreate()和onDestory(),随后在带有注解的方法或onCreate()和onDestory()方法的最后通过ctMethod.insertAfter()插入EventBus的注册与反注册java语句或者class.addMethod()添加java方法,随后通过javassit库中的ctclass.writeFile()将新增的内容写回文件中。

反注册:

/**
     * 生成取消事件注册的代码
     * @param mBusInfo
     */
    static String getUnRegisterEventMethodStr(mBusInfo) 
        String dis_Str = "";
        mBusInfo.eventIds.each  id -> dis_Str += "OkBus.getInstance().unRegister(" + id + ");\\n" 
        return dis_Str;
    
注册:
/**
     * 获取初始化OkBus方法的代码
     * @param mBusInfo 事件信息
     * @return
     */
    static String getRegisterEventMethodStr(BusInfo mBusInfo) 
        String CreateStr = "";
        //为当前的类添加事件处理的接口:当前类要实现事件Event接口
        mBusInfo.clazz.addInterface(mBusInfo.clazz.classPool.get("me.brucezz.viewfinder.sample.event.Event"));
        for (int i = 0; i < mBusInfo.getMethods().size(); i++) 
            MethodInfo methodInfo = mBusInfo.getMethods().get(i).getMethodInfo();
            Annotation mAnnotation = mBusInfo.getAnnotations().get(i)
            AnnotationsAttribute attribute = methodInfo.getAttribute(AnnotationsAttribute.visibleTag);
            //获取注解属性
            javassist.bytecode.annotation.Annotation annotation = attribute.getAnnotation(mAnnotation.annotationType().canonicalName);
            //获取注解
            int id = ((IntegerMemberValue) annotation.getMemberValue("value")).getValue();//获取注解的值
            int thread = -1;
            if (annotation.getMemberValue("thread") != null)
                thread = ((IntegerMemberValue) annotation.getMemberValue("thread")).getValue();
            mBusInfo.eventIds.add(id)
            CreateStr += "OkBus.getInstance().register(" + id + ",(Event)this," + thread + ");\\n"
        
        initEventDispatch(mBusInfo)
        return CreateStr;
    

当前类是实现统一事件的接口Event:注册完后,生成事件分发的逻辑代码方法如下:

/**
     * 生成event事件分发的逻辑代码
     * @param mBusInfo
     * @return
     */
    static initEventDispatch(BusInfo mBusInfo) 
        String SwitchStr = Pre_Switch_Str;//接口实现方法
        for (int i = 0; i < mBusInfo.eventIds.size(); i++) 
            CtMethod method = mBusInfo.getMethods().get(i)
            CtClass[] mParameterTypes = method.getParameterTypes();
            assert mParameterTypes.length <= 1
            boolean one = mParameterTypes.length == 1
            boolean isBaseType = false;
            String packageName = "";
            if (one) 
                String mParameterType = mParameterTypes[0].name;
                switch (mParameterType) 
                //Primitive Types(原始型)	Reference Types(Wrapper Class)(引用型,(包装类))
                    case "boolean": mParameterType = "Boolean"; isBaseType = true; break;
                    case "byte": mParameterType = "Byte"; isBaseType = true; break;
                    case "char": mParameterType = "Character"; isBaseType = true; break;
                    case "float": mParameterType = "Float"; isBaseType = true; break;
                    case "int": mParameterType = "Integer"; isBaseType = true; break;
                    case "long": mParameterType = "Long"; isBaseType = true; break;
                    case "short": mParameterType = "Short"; isBaseType = true; break;
                    case "double": mParameterType = "Double"; isBaseType = true; break;
                
                mBusInfo.project.logger.quiet "name:" + mParameterType;
                packageName = isBaseType ? "java.lang." + mParameterType : mParameterType;
                mBusInfo.clazz.classPool.importPackage(packageName)
            //如果是基本数据类型,需要手动拆箱,否则会报错

            String ParamStr = isBaseType ? ("((" + packageName + ")msg.obj)." +
                    mParameterTypes[0].name + "Value()") : ("(" + packageName + ")msg.obj");
            SwitchStr += "case " + mBusInfo.eventIds.get(i) + ":" + method.getName() +
                    "(" + (one ? ParamStr : "") + ");\\n break;\\n"
            mBusInfo.project.logger.error "pgkname:" + packageName;
        
        String m = SwitchStr + "\\n"
        mBusInfo.project.logger.quiet m
        CtMethod mDispatchEventMethod = CtMethod.make(m, mBusInfo.clazz);
        mBusInfo.clazz.addMethod(mDispatchEventMethod)
    



MainActivity源代码:

public class MainActivity extends AppCompatActivity implements View.OnClickListener 

    private static final String TAG = "MainActivity";
    TextView mTextView;
    Button mButton;
    EditText mEditText;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = (TextView) findViewById(R.id.tv);
        mButton = (Button) findViewById(R.id.btn);
        mEditText = (EditText) findViewById(R.id.et);
        mTextView.setOnClickListener(this);
        mButton.setOnClickListener(this);
        mEditText.setOnClickListener(this);
    

    @Override
    public void onClick(View v) 
        if (v == mTextView) 
            Toast.makeText(this, "1 onTextClick", Toast.LENGTH_SHORT).show();
         else if (v == mButton) 
            Toast.makeText(this, "1 onButtonClick", Toast.LENGTH_SHORT).show();
            OkBus.getInstance().onEvent(EventTags.FLASH_INIT_UI);
         else if (v == mEditText) 

        
    
    @Bus(EventTags.FLASH_INIT_UI)
    public void initUI() 
        AlphaAnimation anim = new AlphaAnimation(0.8f, 0.1f);
        anim.setDuration(500);
        anim.setAnimationListener(new Animation.AnimationListener() 
            @Override
            public void onAnimationStart(Animation animation) 

            

            @Override
            public void onAnimationEnd(Animation animation) 
                Log.d("jumpToMainPage","====onAnimationEnd");
                OkBus.getInstance().onEvent(EventTags.JUMP_TO_MAIN);
            

            @Override
            public void onAnimationRepeat(Animation animation) 

            
        );
        mEditText.startAnimation(anim);
    

    @Bus(EventTags.JUMP_TO_MAIN)
    public void jumpToMainPage() 
//        TRouter.go(C.HOME);
        SecondActivity.startInstance(this);
        Log.d("jumpToMainPage","====jumpToMainPage");
        finish();
    

经过编译运行之后,class的代码就会被修改为:只列出生成的代码及方法,其它不变的删除了。如下

查看class路径:sample\\build\\intermediates\\classes\\debug

    protected void onCreate(Bundle savedInstanceState) 
        OkBus.getInstance().register(1, (Event)this, -1);
        OkBus.getInstance().register(2, (Event)this, -1);
    
	
    public void call(Message var1) 
        switch(var1.what) 
        case 1:
            this.initUI();
            break;
        case 2:
            this.jumpToMainPage();
        

    

    protected void onDestroy() 
        super.onDestroy();
        OkBus.getInstance().unRegister(1);
        OkBus.getInstance().unRegister(2);
    

可以看见自动生成了call(msg)方法和注册与发注册的语句。这样就完成了频繁书写注册与反注册麻烦事件。

javassit的用法就是这么简单,其它的可以查看javassit手册或者https://blog.csdn.net/u011425751/article/details/51917895

demo:https://download.csdn.net/download/zhongwn/10450171

以上是关于AOP之Javassist应用于自动实现EventBus解读的主要内容,如果未能解决你的问题,请参考以下文章

使用Javassist对字节码操作为JBoss实现动态"AOP"框架

springboot 整合javassist详解

九 AOP的概述

Android AOP编程——Javassist基础

Android AOP编程——Javassist基础

Android AOP编程——Javassist基础