问题来历

今天研讨了下如何将gradle的aop插件打包上传到Nexus私服,原本也想运用第三方的maven仓库,但是发现网上的几乎都关服了,那么没办法,只能自己建立一下Nexus私服了。

建立Nexus环境

我运用的是CentOS 7.6(64位版别),至少需求4G内存的服务器。那么咱们现在开始上干货了。

安装java的环境

yum install java

Nexus下载

wget https://download.sonatype.com/nexus/3/nexus-3.37.1-01-unix.tar.gz

AOP Gradle插件的仓库地址不稳定,干脆自己来搭建Nexus私服
解压缩

tar -zxvf nexus-3.37.1-01-unix.tar.gz

运转Nexus

进入bin目录

cd nexus-3.37.1-01/bin

运转nexus

./nexus start // 后台运转
./nexus run // 前台运转,退出指令行中止

这儿你能够先拿run的指令测验一下,有问题能够随时Ctrl+C关掉。

敞开服务器端口,装备安全组规矩

AOP Gradle插件的仓库地址不稳定,干脆自己来搭建Nexus私服
这儿留意选出方向,入方向是指该服务器访问其他服务器敞开的端口。在浏览器输入你的服务器的公网IP地址加上8081端口,8081是Nexus的默许端口,假如你不装备,那就是它。

AOP Gradle插件的仓库地址不稳定,干脆自己来搭建Nexus私服

然后复制maven releases地址。

AOP Gradle插件的仓库地址不稳定,干脆自己来搭建Nexus私服

这个地址在Gradle插件项目中会用到。

apply plugin: 'groovy'
apply plugin: 'maven-publish'
repositories {
    mavenCentral()
}
dependencies {
    implementation gradleApi()//gradle sdk
    implementation localGroovy()//groovy sdk
    implementation fileTree("libs/aspectjtools-1.9.19.jar")
}
jar {
    from {
        zipTree(new File(project.projectDir.absolutePath + "/libs/aspectjtools-1.9.19.jar"))
    }
}
java {
    withJavadocJar()
    withSourcesJar()
}
sourceSets {
    main {
        java {
            srcDirs = ["src"]
        }
    }
}
publishing {
    // Components are the standard way of defining a publication.
    // They are provided by plugins, usually of the language or platform variety.
    // For example, the Java Plugin defines the components.java SoftwareComponent,
    // while the War Plugin defines components.web.
    publications {
        myLibrary(MavenPublication) {
            from components.java
            groupId "com.dorachat"
            version "1.0"
        }
    }
    repositories {
        maven {
            name = "beta"
            // 不运用https
            allowInsecureProtocol true
            url = uri("http://47.236.19.46:8081/repository/maven-releases/")
            credentials {
                username = "admin"
                password = "填入你的Nexus暗码"
            }
        }
    }
}
// 留意⚠️:   插件修改后需求从头发布: ./gradlew clean build publish --info
// 留意jdk的字节码版别和gradle的对应关系

我这儿就为了图便利,就不加ssl证书了,这样的话要设置allowInsecureProtocol证书为true,你也能够添加SSL证书,这样就能够以https的方式访问了。然后履行

./gradlew clean build publish –info

这行指令就发布成功了。

AOP Gradle插件的仓库地址不稳定,干脆自己来搭建Nexus私服

Java字节码版别对照

假如你报这个错: General error during conversion: Unsupported class file major version 63,那就是gradle和jdk版别不匹配导致的。

49 = Java 5

50 = Java 6

51 = Java 7

52 = Java 8

53 = Java 9

54 = Java 10

55 = Java 11

56 = Java 12

57 = Java 13

58 = Java 14

59 = Java 15

60 = Java 16

61 = Java 17

62 = Java 18

63 = Java 19

64 = Java 20

Java version First Gradle version to support it
8 2.0
9 4.3
10 4.7
11 5.0
12 5.4
13 6.0
14 6.3
15 6.7
16 7.0
17 7.3
18 7.5
19 7.6
20 8.1⚠

⚠: Indicates that the Java version can be used for compilation and tests, but not yet running Gradle itself.

Kotlin

Gradle is tested with Kotlin 1.6.10 through 1.8.10. Beta and RC versions may or may not work.

Gradle version Embedded Kotlin version Kotlin Language version
5.0 1.3.10 1.3
5.1 1.3.11 1.3
5.2 1.3.20 1.3
5.3 1.3.21 1.3
5.5 1.3.31 1.3
5.6 1.3.41 1.3
6.0 1.3.50 1.3
6.1 1.3.61 1.3
6.3 1.3.70 1.3
6.4 1.3.71 1.3
6.5 1.3.72 1.3
6.8 1.4.20 1.3
7.0 1.4.31 1.4
7.2 1.5.21 1.4
7.3 1.5.31 1.4
7.5 1.6.21 1.4
7.6 1.7.10 1.4
8.0 1.8.10 1.8

Android项目中运用aop插件

在settings.xml中添加以下代码,告诉编译器在哪里找插件。

pluginManagement {
    repositories {
        maven {
            setUrl("http://47.236.19.46:8081/repository/maven-releases/")
            isAllowInsecureProtocol = true
            credentials {
                username = "admin"
                password = "填入你的Nexus暗码"
            }
        }
    }
    resolutionStrategy {
        eachPlugin {
            if (requested.id.namespace == "com.dorachat") {
                useModule("com.dorachat:dora-aop-plugin:1.0")
            }
        }
    }
}

然后在app模块的build.gradle运用插件。

id("com.dorachat.aop")

那么要点来了,这个id是哪个id呢?

AOP Gradle插件的仓库地址不稳定,干脆自己来搭建Nexus私服

这个id就是咱们META-INF/gradle-plugins/那个properties文件的名称。这个属性文件直接指向了咱们插件的入口代码。

package com.dorachat.plugin
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
import org.gradle.api.Plugin
import org.gradle.api.Project
/**
 * @运用ajc编译java代码 , 同 时 织 入 切 片 代 码
 * 运用 AspectJ 的编译器(ajc,一个java编译器的扩展)
 * 对一切受 aspect 影响的类进行织入。
 * 在 gradle 的编译 task 中增加额外装备,使之能正确编译运转。
 */
 class AspectjPlugin implements Plugin<Project> {
    void apply(Project project) {
        project.dependencies {
            implementation 'org.aspectj:aspectjrt:1.9.19'
        }
        final def log = project.logger
        log.info "============DoraChat AOP START============"
        def hasApp = project.plugins.hasPlugin("com.android.application")
        final def variants
        if (hasApp) {
            variants = project.android.applicationVariants
        } else {
            variants = project.android.libraryVariants
        }
        variants.all { variant ->
            def javaCompile = variant.javaCompile
            javaCompile.doLast {
                String[] args = [
                        "-showWeaveInfo",
                        "-1.8",
                        "-inpath", javaCompile.destinationDir.toString(),
                        "-aspectpath", javaCompile.classpath.asPath,
                        "-d", javaCompile.destinationDir.toString(),
                        "-classpath", javaCompile.classpath.asPath,
                        "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)
                ]
                log.debug "ajc args: " + Arrays.toString(args)
                MessageHandler handler = new MessageHandler(true)
                new Main().run(args, handler)
                for (IMessage message : handler.getMessages(null, true)) {
                    switch (message.getKind()) {
                        case IMessage.ABORT:
                        case IMessage.ERROR:
                        case IMessage.FAIL:
                            log.error message.message, message.thrown
                            break
                        case IMessage.WARNING:
                            log.warn message.message, message.thrown
                            break
                        case IMessage.INFO:
                            log.info message.message, message.thrown
                            break
                        case IMessage.DEBUG:
                            log.debug message.message, message.thrown
                            break
                    }
                }
            }
        }
        log.info "============DoraChat AOP STOP============"
    }
}

运用到AOP的比方

避免快速点击

package site.doramusic.app.aop
import dora.util.LogUtils
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Pointcut
/**
 * 能过注解@SingleClick aop切片的方式在编译期间织入源代码中,避免二次点击。
 */
@Aspect
class SingleClickAspect {
    private var lastClickTime: Long = 0
    @Pointcut("execution(@site.doramusic.app.annotation.SingleClick * *(..))")
    fun singleClick() {
    }
    @Around("singleClick()")
    @Throws(Throwable::class)
    fun aroundPointMethod(joinPoint: ProceedingJoinPoint) {
        if (isFastClick) {
            LogUtils.e("您的手速太赞了")
            return
        }
        joinPoint.proceed()
    }
    private val isFastClick: Boolean
        get() {
            val currentClickTime = System.currentTimeMillis()
            val flag = currentClickTime - lastClickTime < MIN_DELAY_TIME
            lastClickTime = currentClickTime
            return flag
        }
    companion object {
        private const val MIN_DELAY_TIME = 500
    }
}
package site.doramusic.app.annotation
/**
 * 避免短时间内多次触发点击事件。
 */
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
annotation class SingleClick

埋点统计办法履行时间

package site.doramusic.app.aop
import android.util.Log
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.reflect.MethodSignature
import org.aspectj.lang.reflect.SourceLocation
import site.doramusic.app.BuildConfig
import site.doramusic.app.annotation.TimeTrace
/**
 * 打印办法履行时间。
 */
@Aspect
internal class TimeTraceAspect {
    companion object {
        const val TAG = "TimeTrace"
    }
    @Around("execution(@site.doramusic.app.annotation.TimeTrace * *(..))")
    @Throws(Throwable::class)
    fun methodTimeTrace(joinPoint: ProceedingJoinPoint) {
        if (BuildConfig.DEBUG) {
            val methodSignature: MethodSignature = joinPoint.signature as MethodSignature
            val methodLocation: SourceLocation = joinPoint.sourceLocation
            val methodLine = methodLocation.line
            val className = methodSignature.declaringType.simpleName
            val methodName = methodSignature.name
            val timeTrace: TimeTrace = methodSignature.method.getAnnotation(TimeTrace::class.java) as TimeTrace
            val startTime = System.currentTimeMillis()
            joinPoint.proceed()
            val duration: Long = System.currentTimeMillis() - startTime
            Log.d(TAG, String.format("ClassName:【%s】,Line:【%s】,Method:【%s】,【%s】耗时:【%dms】", className, methodLine, methodName, duration))
        } else {
            joinPoint.proceed()
        }
    }
}
package site.doramusic.app.annotation
/**
 * 打印办法履行时间。
 */
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class TimeTrace

总结

运用到AOP面向切面编程的比方还有很多,比方点击按钮发送网络恳求前先检测下网络,假如没有网,就拦截掉,不发恳求,然后显示一个没有网络的对话框。再比方Android6.0运转时权限的动态请求,在需求请求权限的办法前装备一个权限注解,就能够先保证权限被授予再调用该办法。再比方,一切没有登录情况下的统一处理等。假如你擅长Gradle插件开发,你也能够建立一个自己的插件仓库,以后要运用某个插件,直接apply就能够了。